# AVSLib :: 'debug' 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", "debug", "core")

global __load__ = Eval("""
	LoadModule("avslib", "base", "core")
	""")
global __load__ = IsPackageLoaded("avslib", "array") ? NOP : Eval("""
	LoadModule("avslib", "array", "core")
	LoadModule("avslib", "array", "operators")
	LoadModule("avslib", "array", "slices")
	""")

global PRN_WSIZE = 720
global PRN_HSIZE = 576
global PRN_LSIZE = 16
global PRN_LINES = 36	# PRN_LSIZE * PRN_LINES == PRN_HSIZE for these values

# each page lasts 1 sec; ok since it will be inspected statically 
# and we do not want pages to last long.
#
global PrintBase = BlankClip(width=PRN_WSIZE, height=PRN_HSIZE, length=24, fps=24)

Function __print_item(val item, clip "base")
{
	base = Default(base, PrintBase)
	tx = VarType(item) == 1 \
		? "clip w:" + String(item.Width) + \
		  " h:" + String(item.Height) + \
		  " fr:" + String(item.Framecount) + \
		  " fps:" + String(item.Framerate) \
		: Apply(ArraySetValueHandler, item)
	return base.Crop(0, 0, base.Width, 16).SubTitle(tx, x=2, y=2, \
		font="Courier", size=12, text_color=$ffffff, align=7)
}

Function ArrayPrint(string array)
{
	# by invoking __print_item from inside ArraySum we avoid 
	# the creation of intermediate auto-clip globals
	return array.ArraySum(elm_func="__print_item", sum_func="StackVertical")
}

# Limit arguments to 36 (value of PRN_LINES)
#
Function Print(val x01, val "x02", val "x03", val "x04", val "x05", val "x06", \
	val "x07", val "x08", val "x09", val "x10", val "x11", val "x12", val "x13", \
	val "x14", val "x15", val "x16", val "x17", val "x18", val "x19", val "x20", \
	val "x21", val "x22", val "x23", val "x24", val "x25", val "x26", val "x27", \
	val "x28", val "x29", val "x30", val "x31", val "x32", val "x33", val "x34", \
	val "x35", val "x36") 
{
	# change delimiter to allow printing of arrays as a single item; 
	# use the same value as Break, BreakIf
	old = ArrayDelimiterSet(" | ")
	apr = ArrayCreate( x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, \
		x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, \
		x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35, x36)
	ret = ArrayPrint(apr)
	dummy = ArrayDelimiterSet(old)
	return ret
}

# Stops execution, showing a string with the values of the variables passed 
# as arguments in the standard Avisynth message box.
# Useful for quickly inspecting values inside user functions
#
Function Break(val "x01", val "x02", val "x03", val "x04", val "x05", val "x06", \
	val "x07", val "x08", val "x09", val "x10", val "x11", val "x12", val "x13", \
	val "x14", val "x15", val "x16", val "x17", val "x18", val "x19", val "x20")
{
	old = ArrayDelimiterSet(" | ")
	msg = ArrayCreate("User break: " + Chr(10), x01, x02, x03, x04, x05, x06, \
		x07, x08, x09, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)
	dummy = ArrayDelimiterSet(old)
	return Throw(msg)
}

# Stops execution if cond is met, showing a string with the values of the 
# variables passed as arguments in the standard Avisynth message box
# Useful for quickly inspecting values inside user functions and catching errors.
#
Function BreakIf(bool condition, val "x01", val "x02", val "x03", val "x04", \
	val "x05", val "x06", val "x07", val "x08", val "x09", val "x10", val "x11", \
	val "x12", val "x13", val "x14", val "x15", val "x16", val "x17", val "x18", \
	val "x19", val "x20")
{
	old = ArrayDelimiterSet(" | ")
	# create the message array only if condition is met
	msg = condition \
		? ArrayCreate("Conditional break:" + Chr(10), x01, x02, x03, x04, x05, \
		x06, x07, x08, x09, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20) \
		: ""
	dummy = ArrayDelimiterSet(old)
	return Assert(!condition, msg)
}

# ArrayPrintCP block

	# returns an array with count empty strings
	#
	Function __print_pad(int count)
	{
		return count > 1 \
			? Chr(34) + Chr(34) + __ARRAY_DLM + __print_pad(count - 1) \
			: Chr(34) + Chr(34)
	}
	
	# assumes array is properly padded so that last column has 
	# the same height as all the others
	#
	Function __print_column_split(string array, int co_items, string "__ret")
	{
		__ret = Default(__ret, "")
		col = ArraySplit(array, 0, chunksize=co_items)
		c = col.ArrayPrint()
		__ret = __ret.ArrayInsert(c)
		new_arr = array.ArrayDelRange(0, co_items)
		# legal and faster than checking ArrayLen under the assumption made
		return new_arr != "" \
			? __print_column_split(new_arr, co_items, __ret) \
			: __ret
	}
	
	# assumes array is properly padded so that last page 
	# has the same columns as all the others
	#
	Function __print_merge_columns(string array, int columns, string "__ret")
	{
		__ret = Default(__ret, "")
		cols = ArraySplit(array, 0, chunksize=columns)
		page = ArraySum(cols, sum_func="StackHorizontal")
		__ret = __ret.ArrayInsert(page)
		new_arr = array.ArrayDelRange(0, columns)
		# legal and faster than checking ArrayLen under the assumption made
		return new_arr != "" \
			? __print_merge_columns(new_arr, columns, __ret) \
			: __ret
	}

Function ArrayPrintCP(string array, int "columns")
{
	columns = Default(columns, 1)
	Assert(columns > 0, \
		"ArrayPrintCP: Print columns must be at least one.")
	prw = PrintBase.Width
	prh = PrintBase.Height
	ar_items = array.ArrayLen()
	# use line size for calculations since PrintBase 
	# may be set to a different clip by the user
	co_items = Int(prh / PRN_LSIZE)
	pg_items = co_items * columns
	pages = Int(ar_items / pg_items) + (ar_items % pg_items > 0 ? 1 : 0)
	# pad array with empty strings to allow 
	# proper stacking of subtitles
	pad_items = ar_items % co_items == 0 \
		? 0 \
		: co_items - ar_items % co_items
	tot_columns = Int(ar_items / co_items) + (ar_items % co_items > 0 ? 1 : 0)
	pad_columns = pages * columns - tot_columns
	# add co_items empty strings for each pad_column 
	# so that pages have all the same width
	pad_items = pad_items + pad_columns * co_items
	array = pad_items > 0 \
		? ArrayJoin(array, __print_pad(pad_items)) \
		: array
	colw = Int(prw / columns)
	# calculate remainder to add later with stackhorizontal 
	# so that final clip has the same width as PrintBase
	colr = prw - columns * colw
	global __print_HRem = colr > 0 \
		? PrintBase.Crop(0, 0, prh, colr) \
		: Null(PrintBase)
	# save previous PrintBase
	oldpbase = PrintBase
	global PrintBase = PrintBase.Crop(0, 0, colw, prh)
	cols = __print_column_split(array, co_items)
	pgs = __print_merge_columns(cols, columns)
	pgs = colr > 0 \
		? pgs.ArrayOpFunc("StackHorizontal", "__print_HRem") \
		: pgs
	# restore previous PrintBase before returning
	global PrintBase = oldpbase
	return pgs.ArraySum() # simply add pgs items with + 
}
