# AVSLib :: 'string' package :: 'core' module
# Copyright (c) 2005 George Zarkadas (gzarkadas@users.sourceforge.net)

# This program is free software; you can redistribute it and/or modify it under the terms of 
# the GNU General Public License as published by the Free Software Foundation; either 
# version 2 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
# PURPOSE.  See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with this program; 
# if not, write to the "Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
# MA  02111-1307  USA"

DefineModule("avslib", "string", "core")

global __load__ = Eval("""
	LoadModule("avslib", "base", "core") # needed by str_funcs.avsi
	""")

# Temporary LeftStr, RightStr, MidStr replacements (up to Avisynth 2.55)

global __load__ = VersionNumber() > 2.55 \
	? Import("1-1-0(beta)\avslib\string\ver_current\str_funcs.avsi") \
	: Import("ver_2-5-5\str_funcs.avsi")

# CHAR_SET constant
# Use Chr to allow for trasparent use in locales different from US-English
# Note that since Avisynth strings are 1-based, the position of char x in the 
# CharacterSet string is x+1
global CHAR_SET = \
	Chr(  0) + Chr(  1) + Chr(  2) + Chr(  3) + Chr(  4) + Chr(  5) + \
	Chr(  6) + Chr(  7) + Chr(  8) + Chr(  9) + Chr( 10) + Chr( 11) + \
	Chr( 12) + Chr( 13) + Chr( 14) + Chr( 15) + Chr( 16) + Chr( 17) + \
	Chr( 18) + Chr( 19) + Chr( 20) + Chr( 21) + Chr( 22) + Chr( 23) + \
	Chr( 24) + Chr( 25) + Chr( 26) + Chr( 27) + Chr( 28) + Chr( 29) + \
	Chr( 30) + Chr( 31) + Chr( 32) + Chr( 33) + Chr( 34) + Chr( 35) + \
	Chr( 36) + Chr( 37) + Chr( 38) + Chr( 39) + Chr( 40) + Chr( 41) + \
	Chr( 42) + Chr( 43) + Chr( 44) + Chr( 45) + Chr( 46) + Chr( 47) + \
	Chr( 48) + Chr( 49) + Chr( 50) + Chr( 51) + Chr( 52) + Chr( 53) + \
	Chr( 54) + Chr( 55) + Chr( 56) + Chr( 57) + Chr( 58) + Chr( 59) + \
	Chr( 60) + Chr( 61) + Chr( 62) + Chr( 63) + Chr( 64) + Chr( 65) + \
	Chr( 66) + Chr( 67) + Chr( 68) + Chr( 69) + Chr( 70) + Chr( 71) + \
	Chr( 72) + Chr( 73) + Chr( 74) + Chr( 75) + Chr( 76) + Chr( 77) + \
	Chr( 78) + Chr( 79) + Chr( 80) + Chr( 81) + Chr( 82) + Chr( 83) + \
	Chr( 84) + Chr( 85) + Chr( 86) + Chr( 87) + Chr( 88) + Chr( 89) + \
	Chr( 90) + Chr( 91) + Chr( 92) + Chr( 93) + Chr( 94) + Chr( 95) + \
	Chr( 96) + Chr( 97) + Chr( 98) + Chr( 99) + Chr(100) + Chr(101) + \
	Chr(102) + Chr(103) + Chr(104) + Chr(105) + Chr(106) + Chr(107) + \
	Chr(108) + Chr(109) + Chr(110) + Chr(111) + Chr(112) + Chr(113) + \
	Chr(114) + Chr(115) + Chr(116) + Chr(117) + Chr(118) + Chr(119) + \
	Chr(120) + Chr(121) + Chr(122) + Chr(123) + Chr(124) + Chr(125) + \
	Chr(126) + Chr(127) + Chr(128) + Chr(129) + Chr(130) + Chr(131) + \
	Chr(132) + Chr(133) + Chr(134) + Chr(135) + Chr(136) + Chr(137) + \
	Chr(138) + Chr(139) + Chr(140) + Chr(141) + Chr(142) + Chr(143) + \
	Chr(144) + Chr(145) + Chr(146) + Chr(147) + Chr(148) + Chr(149) + \
	Chr(150) + Chr(151) + Chr(152) + Chr(153) + Chr(154) + Chr(155) + \
	Chr(156) + Chr(157) + Chr(158) + Chr(159) + Chr(160) + Chr(161) + \
	Chr(162) + Chr(163) + Chr(164) + Chr(165) + Chr(166) + Chr(167) + \
	Chr(168) + Chr(169) + Chr(170) + Chr(171) + Chr(172) + Chr(173) + \
	Chr(174) + Chr(175) + Chr(176) + Chr(177) + Chr(178) + Chr(179) + \
	Chr(180) + Chr(181) + Chr(182) + Chr(183) + Chr(184) + Chr(185) + \
	Chr(186) + Chr(187) + Chr(188) + Chr(189) + Chr(190) + Chr(191) + \
	Chr(192) + Chr(193) + Chr(194) + Chr(195) + Chr(196) + Chr(197) + \
	Chr(198) + Chr(199) + Chr(200) + Chr(201) + Chr(202) + Chr(203) + \
	Chr(204) + Chr(205) + Chr(206) + Chr(207) + Chr(208) + Chr(209) + \
	Chr(210) + Chr(211) + Chr(212) + Chr(213) + Chr(214) + Chr(215) + \
	Chr(216) + Chr(217) + Chr(218) + Chr(219) + Chr(220) + Chr(221) + \
	Chr(222) + Chr(223) + Chr(224) + Chr(225) + Chr(226) + Chr(227) + \
	Chr(228) + Chr(229) + Chr(230) + Chr(231) + Chr(232) + Chr(233) + \
	Chr(234) + Chr(235) + Chr(236) + Chr(237) + Chr(238) + Chr(239) + \
	Chr(240) + Chr(241) + Chr(242) + Chr(243) + Chr(244) + Chr(245) + \
	Chr(246) + Chr(247) + Chr(248) + Chr(249) + Chr(250) + Chr(251) + \
	Chr(252) + Chr(253) + Chr(254) + Chr(255) 

# Returns true if x starts and ends with " characters (note that 
# for zero-length and one-length strings it returns false)
#
Function IsQuoted(string x)
{ 
	return (StrLen(x) > 1) \
		? (__left_str(x, 1) == Chr(34) && __right_str(x, 1) == Chr(34)) \
		: false
}

# Returns x surrounded by double quotes.
#
Function StrQuote(string x)
{
	return Chr(34) + x + Chr(34)
}

# Removes quotes surrounding x if both present. Does not remove quotes inside x.
#
Function StrUnquote(string x)
{ 
	slen = StrLen(x)
	return (slen >= 2) \
		? (__left_str(x, 1) == Chr(34) && __right_str(x, 1) == Chr(34) \
			? __mid_str(x, 2, slen - 2) \
			: x) \
		: x
}

# Strips a string representation of a number from leading and trailing zeros
# Note for Avisynth 2.5.5 and before: Use standard LeftStr, etc. for speed since 
# it is unlikely that a string representation of a float will be > 512 bytes

	# assume fl is the part of number before the period
	
	Function __zstrip_left(string fl)
	{
		slen = StrLen(fl)
		head = (slen > 1) ? LeftStr(fl, 1) : ""
		fl = (head == "0") ? MidStr(fl, 2) : fl
		return (head == "0" && slen - 1 > 1) ? __zstrip_left(fl) : fl
	}
	
	# assume that 'fr' is the part of number after the period 
	# and if called, then initial number string does have a period
	 
	Function __zstrip_right(string fr)
	{
		slen = StrLen(fr)
		tail = slen > 1 ? RightStr(fr, 1) : ""
		fr = (tail == "0") ? LeftStr(fr, slen - 1) : fr
		return (tail == "0" && slen - 1 > 1) ? __zstrip_right(fr) : fr
	}

Function ZStrip(string fpnum)
{
	bisnum = IsFloat(Value(fpnum))
	dotp = FindStr(fpnum, ".")
	return bisnum \
		? (dotp > 0 \
			? __zstrip_left(LeftStr(fpnum, dotp-1)) + "." \
				+ __zstrip_right(MidStr(fpnum, dotp + 1)) \
			: __zstrip_left(fpnum) ) \
		: fpnum
}

# Returns 'expr' quoted if it fails to evaluate (and thus it is NOT
# a variable name or a valid expression).
# Use it for constructing proper strings to pass to Eval but be aware 
# of possible side effects if 'expr' contains an asignment expression 
# or a function that assigns to globals and not just a variable name.
# 'expr' evaluates only if not quoted since a string surrounded with "" 
# behaves as a string with no quotes when passed to Eval.

	Function __quote_no_expr(string expr)
	{
		global __try_qnexp_temp = expr
		try { 
			dummy = Eval(expr)
			# if expr is a comment or an empty string, dummy 
			# will be undefined; in that case quote expr
			global __try_qnexp_temp = Defined(dummy) \
				? __try_qnexp_temp \
				: StrQuote(__try_qnexp_temp)
			# this is also true if expression assigns to a variable (caveat emptor)
		}
		catch(err_msg) { 
			global __try_qnexp_temp = StrQuote(__try_qnexp_temp)
		}
		return __try_qnexp_temp
	}

Function QuoteNoexpr(string expr)
{
	return IsQuoted(expr) \
		? expr \
		: (expr != "" ? __quote_no_expr(expr) : StrQuote(expr))
}

# Returns a string containing 'count' copies of 's'.
# If count < 0 the function defaults to throwing an error; 
# set 'strict' to false to return an empty string instead.
#
Function StrFill(string s, int count, bool "strict") {
	strict = Default(strict, true)
	Assert((strict ? count >= 0 : true), "StrFill: 'count' cannot be negative")
	return count > 0 ? s + StrFill(s, count - 1) : ""
}

# NOTE: string comparisons are case insensitive 
# (at least for US-English) while searching with FindStr is not.

# StrCompare
# Compares strings (case in/sensitive); returns -1, 0, 1 if s1<s2, s1==s2, s1>s2
# A custom comparison can be made if an appropriate 'comparefunc' function is 
# provided; it should return -1, 0, 1 as well.

	# helper functions 
	# (declared public to aid custom compare functions implementation)

	# Performs a standard (char by char) case-sensitive comparison
	# using the CHAR_SET constant
	
	Function strcmp_CaseSensitive(string s1, string s2)
	{
		# we must explicitly check for s1 or s2 being empty because FindStr with 
		# an empty search string returns one instead of zero
		slen1 = StrLen(s1)
		slen2 = StrLen(s2)
		ch1 = FindStr(CHAR_SET, StrLeft(s1, 1))
		ch2 = FindStr(CHAR_SET, StrLeft(s2, 1))
		# at the tests below: whichever string finishes first 
		# is the smallest (because up to this char they have compared equal)
		return (slen1 > 0 && slen2 > 0) ? ( \
			ch1 < ch2 ? \
				-1 : ( \
			ch1 > ch2 ? \
				1 \
			: \
				strcmp_CaseSensitive(StrMid(s1, 2), strMid(s2, 2)) )) \
		: (slen1 > 0 ? \
			1 \
		: (slen2 > 0 ? \
			-1 \
		: \
			0 ))
	}
	
	# Performs a standard (char by char) case-insensitive comparison
	Function strcmp_CaseInsensitive(string s1, string s2)
	{
		return s1 < s2 ? -1 : ( s1 > s2 ? 1 : 0)
	}

Function StrCompare(string s1, string s2, bool "ignorecase", string "comparefunc")
{
	# ignorecase defaults to true because this is the Avisynth language's 
	# default: to perform case insensitive comparisons
	ignorecase = Default(ignorecase, true)
	return Defined(comparefunc) ? \
		Apply(comparefunc, s1, s2, ignorecase) : ( \
	ignorecase == false ? \
		strcmp_CaseSensitive(s1, s2) \
	: \
		strcmp_CaseInsensitive(s1, s2))
}
	

