[variables] (v0.21) 	Corrected TVariable.RawToVariable parsing for eval variables; added allowevalvars option

Rem
Copyright (c) 2010 Tim Howard

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
End Rem

SuperStrict

Rem
bbdoc: Variables module
End Rem
Module duct.variables

ModuleInfo "Version: 0.21"
ModuleInfo "Copyright: Tim Howard"
ModuleInfo "License: MIT"

ModuleInfo "History: Version 0.21"
ModuleInfo "History: Corrected TVariable.RawToVariable parsing for eval variables; added allowevalvars option"
ModuleInfo "History: Version 0.20"
ModuleInfo "History: Added TBoolVariable support in TVariable.DeserializeUniversal"
ModuleInfo "History: Added TBoolVariable support in TVariable.RawToVariable"
ModuleInfo "History: Fixed an issue with eval variables in TVariable.RawToVariable"
ModuleInfo "History: Corrected some method names and some documentation."
ModuleInfo "History: Version 0.19"
ModuleInfo "History: Added TBoolVariable; documentation correction"
ModuleInfo "History: Version 0.18"
ModuleInfo "History: TIdentifier.AddValue now sets the variable's parent to itself"
ModuleInfo "History: Added SetParent and GetParent to TVaraible"
ModuleInfo "History: Version 0.17"
ModuleInfo "History: General cleanup"
ModuleInfo "History: Version 0.16"
ModuleInfo "History: Added SetAmbiguous to all TVariable types"
ModuleInfo "History: Added option to always use quoting with TStringVariables (on by default)"
ModuleInfo "History: Fixed TStringVariable.ConvToString returning of Null strings"
ModuleInfo "History: Version 0.15"
ModuleInfo "History: Change the Create method definition for easier use"
ModuleInfo "History: Changed the '/eval::' recognizer to '/e:' (as per SNode format change)"
ModuleInfo "History: Added the Copy method to every variable type"
ModuleInfo "History: Changed some formatting"
ModuleInfo "History: Version 0.14"
ModuleInfo "History: Changed type tabbing"
ModuleInfo "History: Fixed script output for TStringVariable (quotes are now only added if whitespace is present)"
ModuleInfo "History: Fixed script output for TIdentifier (identifier names can now contain whitespace, in which case they must be quoted)"
ModuleInfo "History: Version 0.13"
ModuleInfo "History: Added the DeserializeUniversal function to TVariable"
ModuleInfo "History: Added Serialize and Deserialize methods to all variable types"
ModuleInfo "History: Version 0.12"
ModuleInfo "History: Changed module name from 'variablemap' to 'variables'"
ModuleInfo "History: Moved all of duct.utilparser here"
ModuleInfo "History: Added the GetTVType function to all variable types"
ModuleInfo "History: Moved TV_* constants from duct.template here"
ModuleInfo "History: Version 0.11"
ModuleInfo "History: Added the ReportType function to all variable types"
ModuleInfo "History: Version 0.10"
ModuleInfo "History: Added the RawToVariable function to TVariable"
ModuleInfo "History: General code cleanup"
ModuleInfo "History: Version 0.09"
ModuleInfo "History: Corrected usage of syntax (in Returns, Cases, News and Selects)"
ModuleInfo "History: Version 0.08"
ModuleInfo "History: Added the TEvalVariable type"
ModuleInfo "History: Initial release"

Import brl.stream

Import duct.etc
Import duct.objectmap

Rem
	bbdoc: Template variable type for the #TIntVariable type.
End Rem
Const TV_INTEGER:Int = 1
Rem
	bbdoc: Template variable type for the #TStringVariable type.
End Rem
Const TV_STRING:Int = 2
Rem
	bbdoc: Template variable type for the #TFloatVariable type.
End Rem
Const TV_FLOAT:Int = 3
Rem
	bbdoc: Template variable type for the #TEvalVariable type.
End Rem
Const TV_EVAL:Int = 4
Rem
	bbdoc: Template variable type for the #TIdentifier type.
End Rem
Const TV_IDEN:Int = 5
Rem
	bbdoc: Template variable type for the #TBoolVariable type.
End Rem
Const TV_BOOL:Int = 6

Rem
	bbdoc: Generic variable type.
	about: This is the base variable type, you should extend from this to use it.
End Rem
Type TVariable Abstract

	Field m_name:String
	Field m_parent:TVariable

'#region Field accessors

	Rem
		bbdoc: Set the variable's name.
		returns: Nothing.
	End Rem
	Method SetName(name:String)
		m_name = name
	End Method

	Rem
		bbdoc: Get the variable's name.
		returns: The variable's name.
	End Rem
	Method GetName:String()
		Return m_name
	End Method

	Rem
		bbdoc: Set the variable's parent.
		returns: Nothing.
	End Rem
	Method SetParent(parent:TVariable)
		m_parent = parent
	End Method

	Rem
		bbdoc: Get the variable's parent.
		returns: The variable's parent.
	End Rem
	Method GetParent:TVariable()
		Return m_parent
	End Method

	Rem
		bbdoc: Base method for setting the value of the variable to the given string (conversion).
	End Rem
	Method SetAmbiguous(value:String) Abstract

	Rem
		bbdoc: Base method for converting variable data to a script-ready string.
	End Rem
	Method ConvToString:String() Abstract

	Rem
		bbdoc: Base method for converting variable data to a printable/usable-in-code string.
	End Rem
	Method ValueAsString:String() Abstract

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable.
		returns: A clone of the variable.
	End Rem
	Method Copy:TVariable() Abstract

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False) Abstract

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TVariable(stream:TStream, tv:Int = True, name:Int = False) Abstract

'#end region (Data handling)

	Rem
		bbdoc: Base method: Get the type of this variable.
	End Rem
	Function ReportType:String() Abstract

	Rem
		bbdoc: Base method: Get the TV_* type of this variable.
	End Rem
	Function GetTVType:Int() Abstract

	Rem
		bbdoc: Convert raw data (raw data being things like: "/e:(a+b/0.4181)*a-b" - a TEvalVariable, "A String variable", 3452134 - a TIntVariable, 1204.00321 - a FloatVariable) into a Variable.
		returns: A new Variable, or Null if something whacky occured.
		about: @etype is optional, it is used to go automagically to one type of a Variable (1 & 4=String (will check for '/e:' - TEvalVariables and TBoolVariables ['true' or 'false']), 2=Integer, 3=Float).

		@varname is also an optional parameter. It will be used as the name of the variable.

		If @allowevalvars (defaults to False) is True, eval variables will be checked for.
	End Rem
	Function RawToVariable:TVariable(vraw:String, etype:Int = 0, varname:String = "", allowevalvars:Int = False)
		Local variable:TVariable
		If vraw = Null
			DebugLog("(TVariable.RawToVariable) @vraw = Null; returning StringVariable (with @varname and Null value)")
			Return New TStringVariable.Create(varname, Null)
		End If
		If etype = 0 ' Determine the value's type (must be either integer, double, or a string with no spaces)
			' ASCII '0' to '9' = 48-57; '-' = 45, '+' = 43; and '.' = 46
			For Local i:Int = 0 To vraw.Length - 1
				Local c:Int = vraw[i]
				If c >= 48 And c <= 57 Or c = 43 Or c = 45
					If etype = 0 ' Leave float and string alone
						etype = 2 ' Integer so far..
					End If
				Else If c = 46
					If etype = 2 ' Already declared as an integer?
						etype = 3
					End If
				Else ' If the character is not numerical there is nothing else to deduce and the value is a string
					etype = 4
					Exit
				End If
			Next

			Select etype
				Case 2 ' Integer
					variable = TVariable(New TIntVariable.Create(varname, Int(vraw)))
				Case 3 ' Double/Float
					variable = TVariable(New TFloatVariable.Create(varname, Float(vraw)))
			End Select
		End If

		If etype = 1 Or etype = 4
			Local vrawlower:String = vraw.ToLower()
			If vrawlower.StartsWith("/e:") = True and allowevalvars = True
				variable = TVariable(New TEvalVariable.Create(varname, vraw[2..]))
			Else If vrawlower = "true" Or vrawlower = "false"
				variable = New TBoolVariable.Create(varname, (vrawlower = "true") And True Or False)
			Else
				variable = TVariable(New TStringVariable.Create(varname, vraw))
			End If
		End If
		' DebugLog("TSNode.LoadScriptFromStream().RawToVariable(); vraw = ~q" + vraw + "~q \" + etype)
		?Debug
		If variable = Null Then DebugLog("( TVariable.RawToVariable() ) Unknown error, 'variable' is Null.")
		?
		Return variable
	End Function

	Rem
		bbdoc: Universally deserialize a variable from the given stream.
		returns: A Deserialized variable.
		about: This will deserialize any variable from the stream.

		This requires the variable to have been serialized with the template type (see #Serialize parameters).

		@name tells the further Deserialize calls if the name should be Deserialized or not.
	End Rem
	Function DeserializeUniversal:TVariable(stream:TStream, name:Int = False)
		Local tv:Int = Int(stream.ReadByte())
		Select tv
			Case TV_INTEGER
				Return New TIntVariable.Deserialize(stream, True, name)
			Case TV_STRING
				Return New TStringVariable.Deserialize(stream, True, name)
			Case TV_FLOAT
				Return New TFloatVariable.Deserialize(stream, True, name)
			Case TV_EVAL
				Return New TEvalVariable.Deserialize(stream, True, name)
			Case TV_IDEN
				Return New TIdentifier.Deserialize(stream, True, name)
			Case TV_BOOL
				Return New TBoolVariable.Deserialize(stream, True, name)
			Default
				DebugLog("(TVariable.DeserializeUniversal) Failed to recognize the TV in the stream: " + tv)
		End Select
		Return Null
	End Function

End Type

Rem
	bbdoc: String variable.
End Rem
Type TStringVariable Extends TVariable

	Global m_alwaysusequotes:Int = True

	Field m_value:String

	Rem
		bbdoc: Create a new StringVariable.
		returns: The new StringVariable (itself).
	End Rem
	Method Create:TStringVariable(name:String = Null, value:String)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:String()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the TStringVariable to the given string (ambiguous).
		returns: Nothing.
		about: For this type, this method is no different than calling #Set.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A String representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		If m_alwaysusequotes = True Or m_value.Contains("~t") Or m_value.Contains(" ") Or m_value = Null
			Return "~q" + m_value + "~q"
		Else
			Return m_value
		End If
	End Method

	Rem
		bbdoc: Get the StringVariable as a String.
		returns: The variable's value converted to a String.
		about: Here for complete-ness, no difference to `instance.Get()`.
	End Rem
	Method ValueAsString:String()
		Return m_value
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TStringVariable()
		Return New TStringVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_STRING)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If

		WriteLString(stream, m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TStringVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If

		m_value = ReadLString(stream)
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("string").
	End Rem
	Function ReportType:String()
		Return "string"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_STRING).
	End Rem
	Function GetTVType:Int()
		Return TV_STRING
	End Function

End Type

Rem
	bbdoc: Float variable.
End Rem
Type TFloatVariable Extends TVariable

	Field m_value:Float

	Rem
		bbdoc: Create a new FloatVariable.
		returns: The new FloatVariable (itself).
	End Rem
	Method Create:TFloatVariable(name:String = Null, value:Float)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:Float)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:Float()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = Float(value)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A scriptable form of the StringVariable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Local conv:String = String(m_value), i:Int, encountered:Int

		For i = conv.Find(".") To conv.Length - 1
			If conv[i] = 48
				If encountered = True
					conv = conv[..i]
					Exit
				End If
			Else If conv[i] <> 46
				encountered = True
			End If
		Next

		Return conv
	End Method

	Rem
		bbdoc: Get the FloatVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return String(m_value)
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TFloatVariable()
		Return New TFloatVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_FLOAT)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If
		stream.WriteFloat(m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TFloatVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If
		m_value = stream.ReadFloat()
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("float").
	End Rem
	Function ReportType:String()
		Return "float"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_FLOAT).
	End Rem
	Function GetTVType:Int()
		Return TV_FLOAT
	End Function

End Type

Rem
	bbdoc: Integer variable.
End Rem
Type TIntVariable Extends TVariable

	Field m_value:Int

	Rem
		bbdoc: Create a new IntVariable.
		returns: The new IntVariable (itself).
	End Rem
	Method Create:TIntVariable(name:String = Null, value:Int)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:Int)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:Int()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = Int(value)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A string representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return String(m_value)
	End Method

	Rem
		bbdoc: Get the IntVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return String(m_value)
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TIntVariable()
		Return New TIntVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_INTEGER)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If

		stream.WriteInt(m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TIntVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If

		m_value = stream.ReadInt()
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("int").
	End Rem
	Function ReportType:String()
		Return "int"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_INTEGER).
	End Rem
	Function GetTVType:Int()
		Return TV_INTEGER
	End Function

End Type

Rem
	bbdoc: Eval variable (for bah.muparser, and the likes).
End Rem
Type TEvalVariable Extends TVariable

	Field m_value:String

	Rem
		bbdoc: Create a new EvalVariable.
		returns: The new EvalVariable (itself).
	End Rem
	Method Create:TEvalVariable(name:String = Null, value:String)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the equation string for the EvalVariable.
		returns: Nothing
	End Rem
	Method Set(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Get the equation string.
		returns: The equation string.
	End Rem
	Method Get:String()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
		about: For this type, this method is no different than calling #Set.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Convert the EvalVariable to a visual representation of its data.
		returns: The scriptable form of the EvalVariable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return "~q/e:" + m_value + "~q"
	End Method

	Rem
		bbdoc: Get the EvalVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return m_value
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TEvalVariable()
		Return New TEvalVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_EVAL)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If

		WriteLString(stream, m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TEvalVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If

		m_value = ReadLString(stream)
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("eval").
	End Rem
	Function ReportType:String()
		Return "eval"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_EVAL).
	End Rem
	Function GetTVType:Int()
		Return TV_EVAL
	End Function

End Type

Rem
	bbdoc: Boolean variable.
End Rem
Type TBoolVariable Extends TVariable

	Field m_value:Int

	Rem
		bbdoc: Create a new BoolVariable.
		returns: The new BoolVariable (itself).
	End Rem
	Method Create:TBoolVariable(name:String = Null, value:Int)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:Int)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:Int()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = Int(value)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A string representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return String(m_value)
	End Method

	Rem
		bbdoc: Get the TBoolVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return String(m_value)
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TBoolVariable()
		Return New TBoolVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_BOOL)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If
		stream.WriteByte(Byte(m_value))
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TBoolVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If
		m_value = Int(stream.ReadByte())
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("bool").
	End Rem
	Function ReportType:String()
		Return "bool"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_BOOL).
	End Rem
	Function GetTVType:Int()
		Return TV_BOOL
	End Function

End Type

Rem
	bbdoc: Identifier (used in parsers, mostly).
End Rem
Type TIdentifier Extends TVariable

	Field m_values:TList

	Method New()
	End Method

	Rem
		bbdoc: Create a new TIdentifier.
		returns: Itself.
	End Rem
	Method Create:TIdentifier()
		m_values = New TList
		Return Self
	End Method

	Rem
		bbdoc: Create a new TIdentifier with the given data.
		returns: The new Identfier (itself).
		about: If the @values parameter is Null a new list will be created.
	End Rem
	Method CreateByData:TIdentifier(name:String, values:TList = Null)
		SetName(name)
		If values = Null
			m_values = New TList
		Else
			m_values = values
		End If
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the identifier's values.
		returns: Nothing.
	End Rem
	Method SetValues(values:TList)
		m_values = values
	End Method

	Rem
		bbdoc: Get the identifier's values.
		returns: A list containing the values which the identifier holds.
	End Rem
	Method GetValues:TList()
		Return m_values
	End Method

	Rem
		bbdoc: Deprecated for this type.
		returns: Nothing.
		about: This does nothing.
	End Rem
	Method SetAmbiguous(value:String)
	End Method

	Rem
		bbdoc: Convert the identifier to a string.
		returns: A string representation of the identifier.
	End Rem
	Method ConvToString:String()
		Local op:String
		If m_name.Contains("~t") = True Or m_name.Contains(" ") = True
			op = "~q" + m_name + "~q "
		Else
			op = m_name + " "
		End If
		For Local variable:TVariable = EachIn m_values
			op:+variable.ConvToString() + " "
		Next
		op = op[..op.Length - 1]
		Return op
	End Method

	Rem
		bbdoc: Get the identifier as a string.
		returns: The identifier contents converted to a string.
		about: Here for complete-ness, simply calls #ConvToString.
	End Rem
	Method ValueAsString:String()
		Return ConvToString()
	End Method

'#end region (Field accessors)

'#region Value handling

	Rem
		bbdoc: Get a value at an index.
		returns: The value in the identifier's list at the given index, or Null if it could not be retrieved.
		about: The index is zero-based.
	End Rem
	Method GetValueAtIndex:TVariable(index:Int)
		If m_values <> Null And index > - 1 And index < m_values.Count()
			Return TVariable(m_values.ValueAtIndex(index))
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the number of values.
		returns: The number of values the identifier contains.
	End Rem
	Method GetValueCount:Int()
		If m_values <> Null
			Return m_values.Count()
		End If
		Return 0
	End Method

	Rem
		bbdoc: Add a value to the identifier.
		returns: True for success, or False for failure.
		about: NOTE: This will set the given value's parent.
	End Rem
	Method AddValue:Int(value:TVariable)
		If m_values <> Null And value <> Null
			m_values.AddLast(value)
			value.SetParent(Self)
			Return True
		End If
		Return False
	End Method

'#end region (Value handling)

'#region Data handling

	Rem
		bbdoc: Create a copy of the identifier
		returns: A clone of the identifier.
	End Rem
	Method Copy:TIdentifier()
		Local clone:TIdentifier
		clone = New TIdentifier.CreateByData(m_name)
		For Local variable:TVariable = EachIn m_values
			clone.AddValue(variable.Copy())
		Next
		Return clone
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.

		In this case @name tells the method whether it should serialize the values' name (the identifier's name is always read/written to the stream).
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		Local variable:TVariable
		If tv = True
			stream.WriteByte(TV_STRING)
		End If
		WriteLString(stream, m_name)
		If m_values = Null
			stream.WriteInt(0)
		Else
			stream.WriteInt(m_values.Count())
			For variable = EachIn m_values
				variable.Serialize(stream, True, name)
			Next
		End If
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.

		In this case @name tells the method whether it should deserialize the values' name (the identifier's name is always read/written to the stream).
	End Rem
	Method Deserialize:TIdentifier(stream:TStream, tv:Int = True, name:Int = False)
		Local count:Int, n:Int
		If tv = True
			stream.ReadByte()
		End If
		m_name = ReadLString(stream)
		count = stream.ReadInt()
		If count > 0
			For n = 0 To count - 1
				DeserializeUniversal(stream, name)
			Next
		End If
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("identifier").
	End Rem
	Function ReportType:String()
		Return "identifier"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_IDEN).
	End Rem
	Function GetTVType:Int()
		Return TV_IDEN
	End Function

End Type

Rem
	bbdoc: Variable map (stores any variable).
EndRem
Type TVariableMap Extends TObjectMap

	Rem
		bbdoc: Create a new TVariableMap.
		returns: Itself.
	End Rem
	Method Create:TVariableMap()
		Return Self
	End Method

'#region Collections

	Rem
		bbdoc: Insert a variable into the map.
		returns: True if the variable was added, or False if it was not (the variable's name is Null).
	End Rem
	Method InsertVariable:Int(variable:TVariable)
		If variable.GetName() <> Null
			_Insert(variable.GetName(), variable)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Remove a variable by the name given.
		returns: True if a variable was removed, or False if it was not (the map contains no variable with the name given).
		about: The name is case-sensitive.
	End Rem
	Method RemoveVariableByName:Int(name:String)
		Return _Remove(name)
	End Method

	Rem
		bbdoc: Get a variable from the map by its name.
		returns: The variable object, or if the variable was not found, Null.
		about: The name is case-sensitive.
	End Rem
	Method GetVariableByName:TVariable(name:String)
		Return TVariable(_ValueByKey(name))
	End Method

'#end region (Collections)

End Type

Rem
Copyright (c) 2010 Tim Howard

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
End Rem

SuperStrict

Rem
bbdoc: Variables module
End Rem
Module duct.variables

ModuleInfo "Version: 0.20"
ModuleInfo "Copyright: Tim Howard"
ModuleInfo "License: MIT"

ModuleInfo "History: Version 0.20"
ModuleInfo "History: Added TBoolVariable support in TVariable.DeserializeUniversal"
ModuleInfo "History: Added TBoolVariable support in TVariable.RawToVariable"
ModuleInfo "History: Fixed an issue with eval variables in TVariable.RawToVariable"
ModuleInfo "History: Corrected some method names and some documentation."
ModuleInfo "History: Version 0.19"
ModuleInfo "History: Added TBoolVariable; documentation correction"
ModuleInfo "History: Version 0.18"
ModuleInfo "History: TIdentifier.AddValue now sets the variable's parent to itself"
ModuleInfo "History: Added SetParent and GetParent to TVaraible"
ModuleInfo "History: Version 0.17"
ModuleInfo "History: General cleanup"
ModuleInfo "History: Version 0.16"
ModuleInfo "History: Added SetAmbiguous to all TVariable types"
ModuleInfo "History: Added option to always use quoting with TStringVariables (on by default)"
ModuleInfo "History: Fixed TStringVariable.ConvToString returning of Null strings"
ModuleInfo "History: Version 0.15"
ModuleInfo "History: Change the Create method definition for easier use"
ModuleInfo "History: Changed the '/eval::' recognizer to '/e:' (as per SNode format change)"
ModuleInfo "History: Added the Copy method to every variable type"
ModuleInfo "History: Changed some formatting"
ModuleInfo "History: Version 0.14"
ModuleInfo "History: Changed type tabbing"
ModuleInfo "History: Fixed script output for TStringVariable (quotes are now only added if whitespace is present)"
ModuleInfo "History: Fixed script output for TIdentifier (identifier names can now contain whitespace, in which case they must be quoted)"
ModuleInfo "History: Version 0.13"
ModuleInfo "History: Added the DeserializeUniversal function to TVariable"
ModuleInfo "History: Added Serialize and Deserialize methods to all variable types"
ModuleInfo "History: Version 0.12"
ModuleInfo "History: Changed module name from 'variablemap' to 'variables'"
ModuleInfo "History: Moved all of duct.utilparser here"
ModuleInfo "History: Added the GetTVType function to all variable types"
ModuleInfo "History: Moved TV_* constants from duct.template here"
ModuleInfo "History: Version 0.11"
ModuleInfo "History: Added the ReportType function to all variable types"
ModuleInfo "History: Version 0.10"
ModuleInfo "History: Added the RawToVariable function to TVariable"
ModuleInfo "History: General code cleanup"
ModuleInfo "History: Version 0.09"
ModuleInfo "History: Corrected usage of syntax (in Returns, Cases, News and Selects)"
ModuleInfo "History: Version 0.08"
ModuleInfo "History: Added the TEvalVariable type"
ModuleInfo "History: Initial release"

Import brl.stream

Import duct.etc
Import duct.objectmap

Rem
	bbdoc: Template variable type for the #TIntVariable type.
End Rem
Const TV_INTEGER:Int = 1
Rem
	bbdoc: Template variable type for the #TStringVariable type.
End Rem
Const TV_STRING:Int = 2
Rem
	bbdoc: Template variable type for the #TFloatVariable type.
End Rem
Const TV_FLOAT:Int = 3
Rem
	bbdoc: Template variable type for the #TEvalVariable type.
End Rem
Const TV_EVAL:Int = 4
Rem
	bbdoc: Template variable type for the #TIdentifier type.
End Rem
Const TV_IDEN:Int = 5
Rem
	bbdoc: Template variable type for the #TBoolVariable type.
End Rem
Const TV_BOOL:Int = 6

Rem
	bbdoc: Generic variable type.
	about: This is the base variable type, you should extend from this to use it.
End Rem
Type TVariable Abstract

	Field m_name:String
	Field m_parent:TVariable

'#region Field accessors

	Rem
		bbdoc: Set the variable's name.
		returns: Nothing.
	End Rem
	Method SetName(name:String)
		m_name = name
	End Method

	Rem
		bbdoc: Get the variable's name.
		returns: The variable's name.
	End Rem
	Method GetName:String()
		Return m_name
	End Method

	Rem
		bbdoc: Set the variable's parent.
		returns: Nothing.
	End Rem
	Method SetParent(parent:TVariable)
		m_parent = parent
	End Method

	Rem
		bbdoc: Get the variable's parent.
		returns: The variable's parent.
	End Rem
	Method GetParent:TVariable()
		Return m_parent
	End Method

	Rem
		bbdoc: Base method for setting the value of the variable to the given string (conversion).
	End Rem
	Method SetAmbiguous(value:String) Abstract

	Rem
		bbdoc: Base method for converting variable data to a script-ready string.
	End Rem
	Method ConvToString:String() Abstract

	Rem
		bbdoc: Base method for converting variable data to a printable/usable-in-code string.
	End Rem
	Method ValueAsString:String() Abstract

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable.
		returns: A clone of the variable.
	End Rem
	Method Copy:TVariable() Abstract

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False) Abstract

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TVariable(stream:TStream, tv:Int = True, name:Int = False) Abstract

'#end region (Data handling)

	Rem
		bbdoc: Base method: Get the type of this variable.
	End Rem
	Function ReportType:String() Abstract

	Rem
		bbdoc: Base method: Get the TV_* type of this variable.
	End Rem
	Function GetTVType:Int() Abstract

	Rem
		bbdoc: Convert raw data (raw data being things like: "/e:(a+b/0.4181)*a-b" - a TEvalVariable, "A String variable", 3452134 - a TIntVariable, 1204.00321 - a FloatVariable) into a Variable.
		returns: A new Variable, or Null if something whacky occured.
		about: @etype is optional, it is used to go automagically to one type of a Variable (1 & 4=String (will check for '/eval::' - EvalVariables), 2=Integer, 3=Float).

		@varname is also an optional parameter. It will be used as the name of the variable.
	End Rem
	Function RawToVariable:TVariable(vraw:String, etype:Int = 0, varname:String = "")
		Local variable:TVariable
		If vraw = Null
			DebugLog("(TVariable.RawToVariable) @vraw = Null; returning StringVariable (with @varname and Null value)")
			Return New TStringVariable.Create(varname, Null)
		End If
		If etype = 0 ' Determine the value's type (must be either integer, double, or a string with no spaces)
			' ASCII '0' to '9' = 48-57; '-' = 45, '+' = 43; and '.' = 46
			For Local i:Int = 0 To vraw.Length - 1
				Local c:Int = vraw[i]
				If c >= 48 And c <= 57 Or c = 43 Or c = 45
					If etype = 0 ' Leave float and string alone
						etype = 2 ' Integer so far..
					End If
				Else If c = 46
					If etype = 2 ' Already declared as an integer?
						etype = 3
					End If
				Else ' If the character is not numerical there is nothing else to deduce and the value is a string
					etype = 4
					Exit
				End If
			Next

			Select etype
				Case 2 ' Integer
					variable = TVariable(New TIntVariable.Create(varname, Int(vraw)))
				Case 3 ' Double/Float
					variable = TVariable(New TFloatVariable.Create(varname, Float(vraw)))
			End Select
		End If

		If etype = 1 Or etype = 4
			Local vrawlower:String = vraw.ToLower()
			Local evaltest:Int = vrawlower.StartsWith("/e:")
			If evaltest >= 0
				variable = TVariable(New TEvalVariable.Create(varname, vraw[evaltest + 3..]))
			Else If vrawlower = "true" Or vrawlower = "false"
				variable = New TBoolVariable.Create(varname, (vrawlower = "true") And True Or False)
			Else
				variable = TVariable(New TStringVariable.Create(varname, vraw))
			End If
		End If
		' DebugLog("TSNode.LoadScriptFromStream().RawToVariable(); vraw = ~q" + vraw + "~q \" + etype)
		?Debug
		If variable = Null Then DebugLog("( TVariable.RawToVariable() ) Unknown error, 'variable' is Null.")
		?
		Return variable
	End Function

	Rem
		bbdoc: Universally deserialize a variable from the given stream.
		returns: A Deserialized variable.
		about: This will deserialize any variable from the stream.

		This requires the variable to have been serialized with the template type (see #Serialize parameters).

		@name tells the further Deserialize calls if the name should be Deserialized or not.
	End Rem
	Function DeserializeUniversal:TVariable(stream:TStream, name:Int = False)
		Local tv:Int = Int(stream.ReadByte())
		Select tv
			Case TV_INTEGER
				Return New TIntVariable.Deserialize(stream, True, name)
			Case TV_STRING
				Return New TStringVariable.Deserialize(stream, True, name)
			Case TV_FLOAT
				Return New TFloatVariable.Deserialize(stream, True, name)
			Case TV_EVAL
				Return New TEvalVariable.Deserialize(stream, True, name)
			Case TV_IDEN
				Return New TIdentifier.Deserialize(stream, True, name)
			Case TV_BOOL
				Return New TBoolVariable.Deserialize(stream, True, name)
			Default
				DebugLog("(TVariable.DeserializeUniversal) Failed to recognize the TV in the stream: " + tv)
		End Select
		Return Null
	End Function

End Type

Rem
	bbdoc: String variable.
End Rem
Type TStringVariable Extends TVariable

	Global m_alwaysusequotes:Int = True

	Field m_value:String

	Rem
		bbdoc: Create a new StringVariable.
		returns: The new StringVariable (itself).
	End Rem
	Method Create:TStringVariable(name:String = Null, value:String)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:String()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the TStringVariable to the given string (ambiguous).
		returns: Nothing.
		about: For this type, this method is no different than calling #Set.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A String representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		If m_alwaysusequotes = True Or m_value.Contains("~t") Or m_value.Contains(" ") Or m_value = Null
			Return "~q" + m_value + "~q"
		Else
			Return m_value
		End If
	End Method

	Rem
		bbdoc: Get the StringVariable as a String.
		returns: The variable's value converted to a String.
		about: Here for complete-ness, no difference to `instance.Get()`.
	End Rem
	Method ValueAsString:String()
		Return m_value
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TStringVariable()
		Return New TStringVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_STRING)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If

		WriteLString(stream, m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TStringVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If

		m_value = ReadLString(stream)
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("string").
	End Rem
	Function ReportType:String()
		Return "string"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_STRING).
	End Rem
	Function GetTVType:Int()
		Return TV_STRING
	End Function

End Type

Rem
	bbdoc: Float variable.
End Rem
Type TFloatVariable Extends TVariable

	Field m_value:Float

	Rem
		bbdoc: Create a new FloatVariable.
		returns: The new FloatVariable (itself).
	End Rem
	Method Create:TFloatVariable(name:String = Null, value:Float)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:Float)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:Float()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = Float(value)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A scriptable form of the StringVariable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Local conv:String = String(m_value), i:Int, encountered:Int

		For i = conv.Find(".") To conv.Length - 1
			If conv[i] = 48
				If encountered = True
					conv = conv[..i]
					Exit
				End If
			Else If conv[i] <> 46
				encountered = True
			End If
		Next

		Return conv
	End Method

	Rem
		bbdoc: Get the FloatVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return String(m_value)
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TFloatVariable()
		Return New TFloatVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_FLOAT)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If
		stream.WriteFloat(m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TFloatVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If
		m_value = stream.ReadFloat()
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("float").
	End Rem
	Function ReportType:String()
		Return "float"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_FLOAT).
	End Rem
	Function GetTVType:Int()
		Return TV_FLOAT
	End Function

End Type

Rem
	bbdoc: Integer variable.
End Rem
Type TIntVariable Extends TVariable

	Field m_value:Int

	Rem
		bbdoc: Create a new IntVariable.
		returns: The new IntVariable (itself).
	End Rem
	Method Create:TIntVariable(name:String = Null, value:Int)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:Int)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:Int()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = Int(value)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A string representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return String(m_value)
	End Method

	Rem
		bbdoc: Get the IntVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return String(m_value)
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TIntVariable()
		Return New TIntVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_INTEGER)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If

		stream.WriteInt(m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TIntVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If

		m_value = stream.ReadInt()
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("int").
	End Rem
	Function ReportType:String()
		Return "int"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_INTEGER).
	End Rem
	Function GetTVType:Int()
		Return TV_INTEGER
	End Function

End Type

Rem
	bbdoc: Eval variable (for bah.muparser, and the likes).
End Rem
Type TEvalVariable Extends TVariable

	Field m_value:String

	Rem
		bbdoc: Create a new EvalVariable.
		returns: The new EvalVariable (itself).
	End Rem
	Method Create:TEvalVariable(name:String = Null, value:String)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the equation string for the EvalVariable.
		returns: Nothing
	End Rem
	Method Set(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Get the equation string.
		returns: The equation string.
	End Rem
	Method Get:String()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
		about: For this type, this method is no different than calling #Set.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = value
	End Method

	Rem
		bbdoc: Convert the EvalVariable to a visual representation of its data.
		returns: The scriptable form of the EvalVariable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return "~q/e:" + m_value + "~q"
	End Method

	Rem
		bbdoc: Get the EvalVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return m_value
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TEvalVariable()
		Return New TEvalVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_EVAL)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If

		WriteLString(stream, m_value)
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TEvalVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If

		m_value = ReadLString(stream)
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("eval").
	End Rem
	Function ReportType:String()
		Return "eval"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_EVAL).
	End Rem
	Function GetTVType:Int()
		Return TV_EVAL
	End Function

End Type

Rem
	bbdoc: Boolean variable.
End Rem
Type TBoolVariable Extends TVariable

	Field m_value:Int

	Rem
		bbdoc: Create a new BoolVariable.
		returns: The new BoolVariable (itself).
	End Rem
	Method Create:TBoolVariable(name:String = Null, value:Int)
		SetName(name)
		Set(value)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the variable's value.
		returns: Nothing.
	End Rem
	Method Set(value:Int)
		m_value = value
	End Method

	Rem
		bbdoc: Get the variable's value.
		returns: The value of the variable.
	End Rem
	Method Get:Int()
		Return m_value
	End Method

	Rem
		bbdoc: Set the value of the variable to the given string (ambiguous).
		returns: Nothing.
	End Rem
	Method SetAmbiguous(value:String)
		m_value = Int(value)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A string representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return String(m_value)
	End Method

	Rem
		bbdoc: Get the TBoolVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return String(m_value)
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:TBoolVariable()
		Return New TBoolVariable.Create(m_name, m_value)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_BOOL)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If
		stream.WriteByte(Byte(m_value))
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method Deserialize:TBoolVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If
		m_value = Int(stream.ReadByte())
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("bool").
	End Rem
	Function ReportType:String()
		Return "bool"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_BOOL).
	End Rem
	Function GetTVType:Int()
		Return TV_BOOL
	End Function

End Type

Rem
	bbdoc: Identifier (used in parsers, mostly).
End Rem
Type TIdentifier Extends TVariable

	Field m_values:TList

	Method New()
	End Method

	Rem
		bbdoc: Create a new TIdentifier.
		returns: Itself.
	End Rem
	Method Create:TIdentifier()
		m_values = New TList
		Return Self
	End Method

	Rem
		bbdoc: Create a new TIdentifier with the given data.
		returns: The new Identfier (itself).
		about: If the @values parameter is Null a new list will be created.
	End Rem
	Method CreateByData:TIdentifier(name:String, values:TList = Null)
		SetName(name)
		If values = Null
			m_values = New TList
		Else
			m_values = values
		End If
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the identifier's values.
		returns: Nothing.
	End Rem
	Method SetValues(values:TList)
		m_values = values
	End Method

	Rem
		bbdoc: Get the identifier's values.
		returns: A list containing the values which the identifier holds.
	End Rem
	Method GetValues:TList()
		Return m_values
	End Method

	Rem
		bbdoc: Deprecated for this type.
		returns: Nothing.
		about: This does nothing.
	End Rem
	Method SetAmbiguous(value:String)
	End Method

	Rem
		bbdoc: Convert the identifier to a string.
		returns: A string representation of the identifier.
	End Rem
	Method ConvToString:String()
		Local op:String
		If m_name.Contains("~t") = True Or m_name.Contains(" ") = True
			op = "~q" + m_name + "~q "
		Else
			op = m_name + " "
		End If
		For Local variable:TVariable = EachIn m_values
			op:+variable.ConvToString() + " "
		Next
		op = op[..op.Length - 1]
		Return op
	End Method

	Rem
		bbdoc: Get the identifier as a string.
		returns: The identifier contents converted to a string.
		about: Here for complete-ness, simply calls #ConvToString.
	End Rem
	Method ValueAsString:String()
		Return ConvToString()
	End Method

'#end region (Field accessors)

'#region Value handling

	Rem
		bbdoc: Get a value at an index.
		returns: The value in the identifier's list at the given index, or Null if it could not be retrieved.
		about: The index is zero-based.
	End Rem
	Method GetValueAtIndex:TVariable(index:Int)
		If m_values <> Null And index > - 1 And index < m_values.Count()
			Return TVariable(m_values.ValueAtIndex(index))
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the number of values.
		returns: The number of values the identifier contains.
	End Rem
	Method GetValueCount:Int()
		If m_values <> Null
			Return m_values.Count()
		End If
		Return 0
	End Method

	Rem
		bbdoc: Add a value to the identifier.
		returns: True for success, or False for failure.
		about: NOTE: This will set the given value's parent.
	End Rem
	Method AddValue:Int(value:TVariable)
		If m_values <> Null And value <> Null
			m_values.AddLast(value)
			value.SetParent(Self)
			Return True
		End If
		Return False
	End Method

'#end region (Value handling)

'#region Data handling

	Rem
		bbdoc: Create a copy of the identifier
		returns: A clone of the identifier.
	End Rem
	Method Copy:TIdentifier()
		Local clone:TIdentifier
		clone = New TIdentifier.CreateByData(m_name)
		For Local variable:TVariable = EachIn m_values
			clone.AddValue(variable.Copy())
		Next
		Return clone
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.

		In this case @name tells the method whether it should serialize the values' name (the identifier's name is always read/written to the stream).
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		Local variable:TVariable
		If tv = True
			stream.WriteByte(TV_STRING)
		End If
		WriteLString(stream, m_name)
		If m_values = Null
			stream.WriteInt(0)
		Else
			stream.WriteInt(m_values.Count())
			For variable = EachIn m_values
				variable.Serialize(stream, True, name)
			Next
		End If
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.

		In this case @name tells the method whether it should deserialize the values' name (the identifier's name is always read/written to the stream).
	End Rem
	Method Deserialize:TIdentifier(stream:TStream, tv:Int = True, name:Int = False)
		Local count:Int, n:Int
		If tv = True
			stream.ReadByte()
		End If
		m_name = ReadLString(stream)
		count = stream.ReadInt()
		If count > 0
			For n = 0 To count - 1
				DeserializeUniversal(stream, name)
			Next
		End If
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("identifier").
	End Rem
	Function ReportType:String()
		Return "identifier"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_IDEN).
	End Rem
	Function GetTVType:Int()
		Return TV_IDEN
	End Function

End Type

Rem
	bbdoc: Variable map (stores any variable).
EndRem
Type TVariableMap Extends TObjectMap

	Rem
		bbdoc: Create a new TVariableMap.
		returns: Itself.
	End Rem
	Method Create:TVariableMap()
		Return Self
	End Method

'#region Collections

	Rem
		bbdoc: Insert a variable into the map.
		returns: True if the variable was added, or False if it was not (the variable's name is Null).
	End Rem
	Method InsertVariable:Int(variable:TVariable)
		If variable.GetName() <> Null
			_Insert(variable.GetName(), variable)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Remove a variable by the name given.
		returns: True if a variable was removed, or False if it was not (the map contains no variable with the name given).
		about: The name is case-sensitive.
	End Rem
	Method RemoveVariableByName:Int(name:String)
		Return _Remove(name)
	End Method

	Rem
		bbdoc: Get a variable from the map by its name.
		returns: The variable object, or if the variable was not found, Null.
		about: The name is case-sensitive.
	End Rem
	Method GetVariableByName:TVariable(name:String)
		Return TVariable(_ValueByKey(name))
	End Method

'#end region (Collections)

End Type


Removed unused code

Removed unused code

refs gh-6

Rem
	bbdoc: Maximus 'install' argument implementation.
End Rem
Type mxInstallImpl Extends dArgumentImplementation

	Field m_instmap:dObjectMap
	Field m_depcheckmap:dObjectMap
	Field m_unmanagedmap:dObjectMap
	Field m_nobuild:Int = False, m_nounpack:Int = False, m_keeptemp:Int = False
	Field m_nothreaded:Int = False, m_makedocs:Int = False, m_forceinstall:Int = False

	Method New()
		init(["install"])
		m_instmap = New dObjectMap
		m_depcheckmap = New dObjectMap
		m_unmanagedmap = New dObjectMap
	End Method

	Rem
		bbdoc: Check the current arguments for errors (according to the specific implementation).
		returns: Nothing.
		about: This method will throw an error if the arguments are invalid.
	End Rem
	Method CheckArgs()
		If GetArgumentCount() = 0
			ThrowCommonError(mxCmdErrors.REQUIRESPARAMS, m_args.GetName())
		End If
		CheckOptions()
	End Method

	Rem
		bbdoc: Get a string describing the typical usage of the argument.
		returns: A string describing the typical usage of the argument.
	End Rem
	Method GetUsage:String()
		Return _s("arg.install.usage")
	End Method

	Rem
		bbdoc: Execute the implementation's operation.
		returns: Nothing.
	End Rem
	Method Execute()
		m_instmap.Clear()
		m_depcheckmap.Clear()
		m_unmanagedmap.Clear()
		If mainapp.m_sourceshandler
			Local nfounds:dObjectMap = New dObjectMap
			For Local svar:dStringVariable = EachIn m_args
				Local verid:String = svar.Get(), modid:String = mxModUtils.GetIDFromVerID(verid)
				Local modul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
				If modul
					Local instmod:mxInstModule = New mxInstModule.Create(verid, modul)
					If instmod.SetVersionFromVerID(verid)
						m_instmap._Insert(modid, instmod)
					Else
						ThrowError(_s("arg.install.notfound.version", [modid, mxModUtils.GetVersionFromVerID(verid)]))
					End If
				Else
					nfounds._Insert(modid, modid)
				End If
			Next
			If nfounds.Count() > 0
				logger.LogError(_s("arg.install.notfound.instmods"))
				Local a:String
				For Local b:String = EachIn nfounds.ValueEnumerator()
					a:+ b + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				Return
			End If
			If CheckDependencies()
				'Make sure we only install/update managed modules
				If CheckVersions()
					DoInstall()
				End If
			End If
		Else
			ThrowError(_s("error.install.nosources"))
		End If
	End Method

	Rem
		bbdoc: Check the options given to the command.
		returns: Nothing.
	End Rem
	Method CheckOptions()
		m_nobuild = False; m_nounpack = False; m_keeptemp = False
		m_nothreaded = False; m_makedocs = False
		For Local opt:dIdentifier = EachIn m_args
			Select opt.GetName().ToLower()
				Case "-nobuild" m_nobuild = True
				Case "-nounpack" m_nounpack = True
				Case "-keeptemp" m_keeptemp = True
				Case "-nothreaded" m_nothreaded = True
				Case "-makedocs" m_makedocs = True
				Case "-force" m_forceinstall = True
				Default ThrowCommonError(mxOptErrors.UNKNOWN, opt.GetName())
			End Select
		Next
	End Method

	Rem
		bbdoc: Check for version conflictions and warn the user.
		returns: True if the installtion should continue, or False if the user halted the operation.
	End Rem
	Method CheckVersions:Int()
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			Local ver:String = mxModUtils.GetInstalledVersionFromVerID(instmod.GetVerID())
			'Make sure we skip unmanaged modules
			If ver = "unmanaged"
				m_instmap._Remove(instmod.GetID())
				m_unmanagedmap._Insert(instmod.GetID(), instmod)
			End If
		Next
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the modules to be installed.
		returns: Nothing.
	End Rem
	Method CheckDependencies:Int()
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			CheckModuleDependencies(instmod, nfounds)
		Next
		If nfounds.Count() > 0
			logger.LogError(_s("arg.install.notfound.instdeps"))
			Local a:String
			For Local b:String = EachIn nfounds.ValueEnumerator()
				a:+ b + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			If m_forceinstall
				m_nobuild = True
				Local resp:String = Input(_s("arg.install.missingdeps") + " ").ToLower()
				If resp = "y" Or resp = "yes"
					Return True
				End If
			End If
			Return False
		End If
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the given module and adapt the install list accordingly.
		returns: Nothing.
	End Rem
	Method CheckModuleDependencies(instmod:mxInstModule, nfounds:dObjectMap)
		Local addlist:TListEx = instmod.CheckCompliance()
		If addlist
			For Local modid:String = EachIn addlist
				If Not m_depcheckmap._Contains(modid)
					Local dmodul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
					m_depcheckmap._Insert(modid, modid)
					If dmodul
						If Not m_instmap._Contains(modid)
							Local dinstmod:mxInstModule = New mxInstModule.Create(modid, dmodul)
							If instmod.GetVersionName().ToLower() = "dev"
								dinstmod.SetVersionFromName("dev")
							Else
								dinstmod.SetVersion(Null)
							End If
							m_instmap._Insert(modid, dinstmod)
							CheckModuleDependencies(dinstmod, nfounds)
						End If
					Else
						nfounds._Insert(modid, modid)
					End If
				End If
			Next
		End If
	End Method

	Rem
		bbdoc: Do the actual installing.
		returns: Nothing.
	End Rem
	Method DoInstall()
		If m_instmap.Count() > 0
			Local a:String, instmod:mxInstModule

			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				For instmod = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				a = ""
			End If

			logger.LogMessage(_s("arg.install.modulestoinstall"))
			For instmod = EachIn m_instmap.ValueEnumerator()
				a:+ instmod.GetVerID() + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			Local resp:String = Input(_s("arg.install.continuewithinstall") + " ").ToLower()
			If resp = "y" Or resp = "yes"
				For instmod = EachIn m_instmap.ValueEnumerator()
					instmod.FetchSourceArchive()
				Next
				If Not m_nounpack
					For instmod = EachIn m_instmap.ValueEnumerator()
						instmod.Unpack()
					Next
				Else
					logger.LogMessage(_s("message.skipping.unpack"))
				End If
				If Not m_keeptemp Then DeleteDir("tmp/", True)
				If Not m_nobuild
					Local scopes:dObjectMap = New dObjectMap
					For instmod = EachIn m_instmap.ValueEnumerator()
						scopes._Insert(instmod.GetModuleScope(), instmod.GetModuleScope())
					Next
					For Local scope:String = EachIn scopes.ValueEnumerator()
						If mxBMKUtils.MakeMods(scope, m_nothreaded ~ 1) <> 0
							ThrowError(_s("error.install.build", [scope]))
						End If
					Next
					If m_makedocs
						'For instmod = EachIn m_instmap.ValueEnumerator()
						'	mxModUtils.DocMods(instmod.GetID())
						'Next
						' Can't build docs for individual modules yet
						mxModUtils.DocMods()
					End If
				Else
					logger.LogMessage(_s("message.skipping.install"))
				End If
			End If
		Else
			logger.LogMessage(_s("arg.install.nomodulestoinstall"))
			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				Local a:String
				For Local instmod:mxInstModule = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
			End If
		End If
	End Method

End Type

Rem
	bbdoc: Maximus install info module.
	about: This type is used for temporary install information on modules.
End Rem
Type mxInstModule

	Field m_id:String
	Field m_version:mxModuleVersion
	Field m_module:mxModule

	Rem
		bbdoc: Create a new mxInstModule.
		returns: Itself.
	End Rem
	Method Create:mxInstModule(verid:String, modul:mxModule)
		SetID(mxModUtils.GetIDFromVerID(verid))
		SetModule(modul)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the modid to be installed.
		returns: Nothing.
	End Rem
	Method SetID(id:String)
		m_id = id
	End Method

	Rem
		bbdoc: Get the modid to be installed.
		returns: The modid to be installed.
	End Rem
	Method GetID:String()
		Return m_id
	End Method

	Rem
		bbdoc: Set the version from the given versioned-module name (e.g. "foo.bar/1.02").
		returns: True if the version was set, or False if the version could not be found (was not found).
		about: The latest version will be set if the given id is Null (or if it isn't versioned (e.g. "foo.bar", would result in the latest version being selected)).
	End Rem
	Method SetVersionFromVerID:Int(verid:String)
		Return SetVersionFromName(mxModUtils.GetVersionFromVerID(verid))
	End Method

	Rem
		bbdoc: Set the version to be installed from the given version name (e.g. "1.02").
		returns: True if the version was set, or False if the version could not be set (was not found).
		about: The latest version will be set if the given name is Null.
	End Rem
	Method SetVersionFromName:Int(name:String)
		If Not name
			SetVersion(Null) ' Set the latest version (the version wasn't forced)
			Return True
		Else
			Local ver:mxModuleVersion = m_module.GetVersionWithName(name)
			If ver
				SetVersion(ver)
				Return True
			End If
		End If
		Return False
	End Method

	Rem
		bbdoc: Set the install version.
		returns: Nothing.
		about: If the given value is Null, it will be automatically set to the latest (non-development) version.
	End Rem
	Method SetVersion(version:mxModuleVersion)
		m_version = version
		If Not m_version
			m_version = m_module.GetLatestVersion()
		End If
	End Method

	Rem
		bbdoc: Get the install version.
		returns: The version to be installed.
	End Rem
	Method GetVersion:mxModuleVersion()
		Return m_version
	End Method

	Rem
		bbdoc: Set the module to be installed.
		returns: Nothing.
	End Rem
	Method SetModule(modul:mxModule)
		m_module = modul
	End Method

	Rem
		bbdoc: Get the module to be installed.
		returns: The module to be installed.
	End Rem
	Method GetModule:mxModule()
		Return m_module
	End Method

	Rem
		bbdoc: Get the module's scope.
		returns: The module's scope.
	End Rem
	Method GetModuleScope:String()
		Return mxModUtils.GetScopeFromID(m_id)
	End Method

	Rem
		bbdoc: Get the module's name.
		returns: The module's name.
	End Rem
	Method GetModuleName:String()
		Return mxModUtils.GetNameFromID(m_id)
	End Method

	Rem
		bbdoc: Get the versioned-module id.
		returns: The exact module to be installed (e.g. "foo.bar/1.02").
	End Rem
	Method GetVerID:String()
		Return m_id + "/" + m_version.GetName()
	End Method

	Rem
		bbdoc: Get the name of the version to be installed.
		returns: The name of the version to be installed.
	End Rem
	Method GetVersionName:String()
		Return m_version.GetName()
	End Method

'#end region Field accessors

	Rem
		bbdoc: Check the dependency compliance of the current module set.
		returns: Null if all dependencies are matched by existing modules, or a list of module ids that are not yet installed.
	End Rem
	Method CheckCompliance:TListEx()
		Return m_version.GetDependencies().CheckCompliance()
	End Method

	Rem
		bbdoc: Fetch the source archive for the version to be installed.
		returns: Nothing.
	End Rem
	Method FetchSourceArchive()
		m_version.FetchSourceArchive()
	End Method

	Rem
		bbdoc: Unpack the version's archive.
		returns: Nothing.
	End Rem
	Method Unpack()
		Local archivepath:String = m_version.GetTemporaryFilePath()
		Local zreader:ZipReader = New ZipReader
		logger.LogMessage(_s("message.unpacking", [archivepath]))
		If zreader.OpenZip(archivepath)
			Local filename:String, outputpath:String
			Local basepath:String = mainapp.m_modpath + "/" + mxModUtils.GetScopeFromID(m_id) + ".mod/"
			For Local fileinfo:SZipFileEntry = EachIn zreader.m_zipFileList.FileList
				filename = fileinfo.zipFileName
				outputpath = basepath + filename
				'DebugLog("Zip outputpath: ~q" + outputpath + "~q")
				If filename[filename.Length - 1] = 47 ' "/"
					CreateDir(outputpath, True)
				Else
					If CreateFileExplicitly(outputpath)
						zreader.ExtractFileToDisk(filename, outputpath, False)
					Else
						zreader.CloseZip()
						ThrowError(_s("error.writeperms", [outputpath]))
					End If
				End If
			Next
			zreader.CloseZip()

			Local metafile:mxMetaFile = New mxMetaFile.Create(basepath + GetModuleName() + ".mod/meta.maximus")
			metafile.SetMetaData(GetModuleScope(), GetModuleName(), GetVersionName())
			metafile.Save()
		Else
			ThrowError(_s("error.install.openarchive", [archivepath]))
		End If
	End Method

End Type

Rem
	bbdoc: Maximus 'install' argument implementation.
End Rem
Type mxInstallImpl Extends dArgumentImplementation

	Field m_instmap:dObjectMap
	Field m_depcheckmap:dObjectMap
	Field m_unmanagedmap:dObjectMap
	Field m_nobuild:Int = False, m_nounpack:Int = False, m_keeptemp:Int = False
	Field m_nothreaded:Int = False, m_makedocs:Int = False, m_forceinstall:Int = False

	Method New()
		init(["install"])
		m_instmap = New dObjectMap
		m_depcheckmap = New dObjectMap
		m_unmanagedmap = New dObjectMap
	End Method

	Rem
		bbdoc: Check the current arguments for errors (according to the specific implementation).
		returns: Nothing.
		about: This method will throw an error if the arguments are invalid.
	End Rem
	Method CheckArgs()
		If GetArgumentCount() = 0
			ThrowCommonError(mxCmdErrors.REQUIRESPARAMS, m_args.GetName())
		End If
		CheckOptions()
	End Method

	Rem
		bbdoc: Get a string describing the typical usage of the argument.
		returns: A string describing the typical usage of the argument.
	End Rem
	Method GetUsage:String()
		Return _s("arg.install.usage")
	End Method

	Rem
		bbdoc: Execute the implementation's operation.
		returns: Nothing.
	End Rem
	Method Execute()
		m_instmap.Clear()
		m_depcheckmap.Clear()
		m_unmanagedmap.Clear()
		If mainapp.m_sourceshandler
			Local nfounds:dObjectMap = New dObjectMap
			For Local svar:dStringVariable = EachIn m_args
				Local verid:String = svar.Get(), modid:String = mxModUtils.GetIDFromVerID(verid)
				Local modul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
				If modul
					Local instmod:mxInstModule = New mxInstModule.Create(verid, modul)
					If instmod.SetVersionFromVerID(verid)
						m_instmap._Insert(modid, instmod)
					Else
						ThrowError(_s("arg.install.notfound.version", [modid, mxModUtils.GetVersionFromVerID(verid)]))
					End If
				Else
					nfounds._Insert(modid, modid)
				End If
			Next
			If nfounds.Count() > 0
				logger.LogError(_s("arg.install.notfound.instmods"))
				Local a:String
				For Local b:String = EachIn nfounds.ValueEnumerator()
					a:+ b + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				Return
			End If
			If CheckDependencies()
				'Make sure we only install/update managed modules
				If CheckVersions()
					DoInstall()
				End If
			End If
		Else
			ThrowError(_s("error.install.nosources"))
		End If
	End Method

	Rem
		bbdoc: Check the options given to the command.
		returns: Nothing.
	End Rem
	Method CheckOptions()
		m_nobuild = False; m_nounpack = False; m_keeptemp = False
		m_nothreaded = False; m_makedocs = False
		For Local opt:dIdentifier = EachIn m_args
			Select opt.GetName().ToLower()
				Case "-nobuild" m_nobuild = True
				Case "-nounpack" m_nounpack = True
				Case "-keeptemp" m_keeptemp = True
				Case "-nothreaded" m_nothreaded = True
				Case "-makedocs" m_makedocs = True
				Case "-force" m_forceinstall = True
				Default ThrowCommonError(mxOptErrors.UNKNOWN, opt.GetName())
			End Select
		Next
	End Method

	Rem
		bbdoc: Check for version conflictions and warn the user.
		returns: True if the installtion should continue, or False if the user halted the operation.
	End Rem
	Method CheckVersions:Int()
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			Local ver:String = mxModUtils.GetInstalledVersionFromVerID(instmod.GetVerID())
			'Make sure we skip unmanaged modules
			If ver = "unmanaged"
				m_instmap._Remove(instmod.GetID())
				m_unmanagedmap._Insert(instmod.GetID(), instmod)
			End If
		Next
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the modules to be installed.
		returns: Nothing.
	End Rem
	Method CheckDependencies:Int()
		Local addlist:TListEx, nfounds:dObjectMap = New dObjectMap
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			CheckModuleDependencies(instmod, nfounds)
		Next
		If nfounds.Count() > 0
			logger.LogError(_s("arg.install.notfound.instdeps"))
			Local a:String
			For Local b:String = EachIn nfounds.ValueEnumerator()
				a:+ b + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			If m_forceinstall
				m_nobuild = True
				Local resp:String = Input(_s("arg.install.missingdeps") + " ").ToLower()
				If resp = "y" Or resp = "yes"
					Return True
				End If
			End If
			Return False
		End If
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the given module and adapt the install list accordingly.
		returns: Nothing.
	End Rem
	Method CheckModuleDependencies(instmod:mxInstModule, nfounds:dObjectMap)
		Local addlist:TListEx = instmod.CheckCompliance()
		If addlist
			For Local modid:String = EachIn addlist
				If Not m_depcheckmap._Contains(modid)
					Local dmodul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
					m_depcheckmap._Insert(modid, modid)
					If dmodul
						If Not m_instmap._Contains(modid)
							Local dinstmod:mxInstModule = New mxInstModule.Create(modid, dmodul)
							If instmod.GetVersionName().ToLower() = "dev"
								dinstmod.SetVersionFromName("dev")
							Else
								dinstmod.SetVersion(Null)
							End If
							m_instmap._Insert(modid, dinstmod)
							CheckModuleDependencies(dinstmod, nfounds)
						End If
					Else
						nfounds._Insert(modid, modid)
					End If
				End If
			Next
		End If
	End Method

	Rem
		bbdoc: Do the actual installing.
		returns: Nothing.
	End Rem
	Method DoInstall()
		If m_instmap.Count() > 0
			Local a:String, instmod:mxInstModule

			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				For instmod = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				a = ""
			End If

			logger.LogMessage(_s("arg.install.modulestoinstall"))
			For instmod = EachIn m_instmap.ValueEnumerator()
				a:+ instmod.GetVerID() + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			Local resp:String = Input(_s("arg.install.continuewithinstall") + " ").ToLower()
			If resp = "y" Or resp = "yes"
				For instmod = EachIn m_instmap.ValueEnumerator()
					instmod.FetchSourceArchive()
				Next
				If Not m_nounpack
					For instmod = EachIn m_instmap.ValueEnumerator()
						instmod.Unpack()
					Next
				Else
					logger.LogMessage(_s("message.skipping.unpack"))
				End If
				If Not m_keeptemp Then DeleteDir("tmp/", True)
				If Not m_nobuild
					Local scopes:dObjectMap = New dObjectMap
					For instmod = EachIn m_instmap.ValueEnumerator()
						scopes._Insert(instmod.GetModuleScope(), instmod.GetModuleScope())
					Next
					For Local scope:String = EachIn scopes.ValueEnumerator()
						If mxBMKUtils.MakeMods(scope, m_nothreaded ~ 1) <> 0
							ThrowError(_s("error.install.build", [scope]))
						End If
					Next
					If m_makedocs
						'For instmod = EachIn m_instmap.ValueEnumerator()
						'	mxModUtils.DocMods(instmod.GetID())
						'Next
						' Can't build docs for individual modules yet
						mxModUtils.DocMods()
					End If
				Else
					logger.LogMessage(_s("message.skipping.install"))
				End If
			End If
		Else
			logger.LogMessage(_s("arg.install.nomodulestoinstall"))
			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				Local a:String
				For Local instmod:mxInstModule = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
			End If
		End If
	End Method

End Type

Rem
	bbdoc: Maximus install info module.
	about: This type is used for temporary install information on modules.
End Rem
Type mxInstModule

	Field m_id:String
	Field m_version:mxModuleVersion
	Field m_module:mxModule

	Rem
		bbdoc: Create a new mxInstModule.
		returns: Itself.
	End Rem
	Method Create:mxInstModule(verid:String, modul:mxModule)
		SetID(mxModUtils.GetIDFromVerID(verid))
		SetModule(modul)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the modid to be installed.
		returns: Nothing.
	End Rem
	Method SetID(id:String)
		m_id = id
	End Method

	Rem
		bbdoc: Get the modid to be installed.
		returns: The modid to be installed.
	End Rem
	Method GetID:String()
		Return m_id
	End Method

	Rem
		bbdoc: Set the version from the given versioned-module name (e.g. "foo.bar/1.02").
		returns: True if the version was set, or False if the version could not be found (was not found).
		about: The latest version will be set if the given id is Null (or if it isn't versioned (e.g. "foo.bar", would result in the latest version being selected)).
	End Rem
	Method SetVersionFromVerID:Int(verid:String)
		Return SetVersionFromName(mxModUtils.GetVersionFromVerID(verid))
	End Method

	Rem
		bbdoc: Set the version to be installed from the given version name (e.g. "1.02").
		returns: True if the version was set, or False if the version could not be set (was not found).
		about: The latest version will be set if the given name is Null.
	End Rem
	Method SetVersionFromName:Int(name:String)
		If Not name
			SetVersion(Null) ' Set the latest version (the version wasn't forced)
			Return True
		Else
			Local ver:mxModuleVersion = m_module.GetVersionWithName(name)
			If ver
				SetVersion(ver)
				Return True
			End If
		End If
		Return False
	End Method

	Rem
		bbdoc: Set the install version.
		returns: Nothing.
		about: If the given value is Null, it will be automatically set to the latest (non-development) version.
	End Rem
	Method SetVersion(version:mxModuleVersion)
		m_version = version
		If Not m_version
			m_version = m_module.GetLatestVersion()
		End If
	End Method

	Rem
		bbdoc: Get the install version.
		returns: The version to be installed.
	End Rem
	Method GetVersion:mxModuleVersion()
		Return m_version
	End Method

	Rem
		bbdoc: Set the module to be installed.
		returns: Nothing.
	End Rem
	Method SetModule(modul:mxModule)
		m_module = modul
	End Method

	Rem
		bbdoc: Get the module to be installed.
		returns: The module to be installed.
	End Rem
	Method GetModule:mxModule()
		Return m_module
	End Method

	Rem
		bbdoc: Get the module's scope.
		returns: The module's scope.
	End Rem
	Method GetModuleScope:String()
		Return mxModUtils.GetScopeFromID(m_id)
	End Method

	Rem
		bbdoc: Get the module's name.
		returns: The module's name.
	End Rem
	Method GetModuleName:String()
		Return mxModUtils.GetNameFromID(m_id)
	End Method

	Rem
		bbdoc: Get the versioned-module id.
		returns: The exact module to be installed (e.g. "foo.bar/1.02").
	End Rem
	Method GetVerID:String()
		Return m_id + "/" + m_version.GetName()
	End Method

	Rem
		bbdoc: Get the name of the version to be installed.
		returns: The name of the version to be installed.
	End Rem
	Method GetVersionName:String()
		Return m_version.GetName()
	End Method

'#end region Field accessors

	Rem
		bbdoc: Check the dependency compliance of the current module set.
		returns: Null if all dependencies are matched by existing modules, or a list of module ids that are not yet installed.
	End Rem
	Method CheckCompliance:TListEx()
		Return m_version.GetDependencies().CheckCompliance()
	End Method

	Rem
		bbdoc: Fetch the source archive for the version to be installed.
		returns: Nothing.
	End Rem
	Method FetchSourceArchive()
		m_version.FetchSourceArchive()
	End Method

	Rem
		bbdoc: Unpack the version's archive.
		returns: Nothing.
	End Rem
	Method Unpack()
		Local archivepath:String = m_version.GetTemporaryFilePath()
		Local zreader:ZipReader = New ZipReader
		logger.LogMessage(_s("message.unpacking", [archivepath]))
		If zreader.OpenZip(archivepath)
			Local filename:String, outputpath:String
			Local basepath:String = mainapp.m_modpath + "/" + mxModUtils.GetScopeFromID(m_id) + ".mod/"
			For Local fileinfo:SZipFileEntry = EachIn zreader.m_zipFileList.FileList
				filename = fileinfo.zipFileName
				outputpath = basepath + filename
				'DebugLog("Zip outputpath: ~q" + outputpath + "~q")
				If filename[filename.Length - 1] = 47 ' "/"
					CreateDir(outputpath, True)
				Else
					If CreateFileExplicitly(outputpath)
						zreader.ExtractFileToDisk(filename, outputpath, False)
					Else
						zreader.CloseZip()
						ThrowError(_s("error.writeperms", [outputpath]))
					End If
				End If
			Next
			zreader.CloseZip()

			Local metafile:mxMetaFile = New mxMetaFile.Create(basepath + GetModuleName() + ".mod/meta.maximus")
			metafile.SetMetaData(GetModuleScope(), GetModuleName(), GetVersionName())
			metafile.Save()
		Else
			ThrowError(_s("error.install.openarchive", [archivepath]))
		End If
	End Method

End Type


Now also compiles under Linux, as iconv is available by default.

SuperStrict

Rem
	bbdoc: htbaapub.xmlrpc
EndRem
Module htbaapub.xmlrpc
ModuleInfo "Name: htbaapub.xmlrpc"
ModuleInfo "Version: 1.0"
ModuleInfo "Author: Christiaan Kras"
ModuleInfo "Git repository: http://github.com/Htbaa/htbaapub.mod/"
ModuleInfo "XMLRPC-EPI: http://xmlrpc-epi.sourceforge.net/"
ModuleInfo "Expat: http://expat.sourceforge.net/"
ModuleInfo "iconv: http://gnuwin32.sourceforge.net/packages/libiconv.htm"
ModuleInfo "iconv-win32: http://www.zlatkovic.com/pub/libxml"

Import brl.blitz
Import brl.basic
Import brl.map
Import brl.reflection
Import brl.socket

Import "src/expat-2.0.1/src/*.h"
ModuleInfo "CC_OPTS: -DHAVE_EXPAT_CONFIG_H"
?ppc
	ModuleInfo "CC_OPTS: -DWORDS_BIGENDIAN"
?

Import "src/expat-2.0.1/src/xmlparse.c"
Import "src/expat-2.0.1/src/xmlrole.c"
Import "src/expat-2.0.1/src/xmltok.c"
Import "src/expat-2.0.1/src/xmltok_impl.c"
Import "src/expat-2.0.1/src/xmltok_ns.c"

Import "src/xmlrpc-epi-0.54/src/*.h"
Import "glue.c"

?Win32
	Import "win32.cpp"
	ModuleInfo "LD_OPTS: -L%PWD%/src/iconv-1.9.2.win32/lib"
	Import "src/iconv-1.9.2.win32/include/*.h"
?

Import "src/xmlrpc-epi-0.54/src/xmlrpc.c"
Import "src/xmlrpc-epi-0.54/src/base64.c"
Import "src/xmlrpc-epi-0.54/src/encodings.c"
Import "src/xmlrpc-epi-0.54/src/queue.c"
Import "src/xmlrpc-epi-0.54/src/simplestring.c"
Import "src/xmlrpc-epi-0.54/src/system_methods.c"
Import "src/xmlrpc-epi-0.54/src/xml_element.c"
Import "src/xmlrpc-epi-0.54/src/xml_to_dandarpc.c"
Import "src/xmlrpc-epi-0.54/src/xml_to_soap.c"
Import "src/xmlrpc-epi-0.54/src/xml_to_xmlrpc.c"
Import "src/xmlrpc-epi-0.54/src/xmlrpc_introspection.c"

Include "wrapper.bmx"
Include "datatypes.bmx"
Include "response.bmx"
Include "transport.bmx"
Include "client.bmx"
Include "server.bmx"

Rem
	bbdoc: General XML-RPC Exception
End Rem
Type TXMLRPC_Exception
	Field message:String
	Rem
		bbdoc: Set message for exception
	End Rem
	Method Create:TXMLRPC_Exception(message:String)
		Self.message = message
		Return Self
	End Method

	Rem
		bbdoc: Return exception message
	End Rem
	Method ToString:String()
		Return Self.message
	End Method
End Type

Rem
	bbdoc: Parameters to be passed to the XML-RPC server when using TXMLRPC_Client.Call()
End Rem
Type TXMLRPC_Call_Parameters
	Field vector:Byte Ptr

	Rem
		bbdoc: Create a TXMLRPC_Call_Parameters object.
		about: vectorType can be: xmlrpc_vector_none, xmlrpc_vector_array, xmlrpc_vector_mixed or xmlrpc_vector_struct
	End Rem
	Function Create:TXMLRPC_Call_Parameters(vectorType:Int = xmlrpc_vector_mixed)
		Local parameters:TXMLRPC_Call_Parameters = New TXMLRPC_Call_Parameters
		parameters.vector = XMLRPC_CreateVector(Null, vectorType)
		Return parameters
	End Function

	Rem
		bbdoc: Append string value
	End Rem
	Method AppendString:Int(id:String, s:String)
		Return XMLRPC_VectorAppendString(Self.vector, id, s)
	End Method

	Rem
		bbdoc: Append Base64 value. Just pass a string, it'll be BASE64 encoded for you
	End Rem
	Method AppendBase64:Int(id:String, s:String)
		Return XMLRPC_VectorAppendBase64(Self.vector, id, s)
	End Method

	Rem
		bbdoc: Append UNIX timestmap value
	End Rem
	Method AppendDateTime:Int(id:String, time:Long)
		Return XMLRPC_VectorAppendDateTime(Self.vector, id, time)
	End Method

	Rem
		bbdoc: Append datetime in ISO8601 format
	End Rem
	Method AppendDateTime_ISO8601:Int(id:String, s:String)
		Return XMLRPC_VectorAppendDateTime_ISO8601(Self.vector, id, s)
	End Method

	Rem
		bbdoc: Append double value
	End Rem
	Method AppendDouble:Int(id:String, f:Double)
		Return XMLRPC_VectorAppendDouble(Self.vector, id, f)
	End Method

	Rem
		bbdoc: Append integer value
	End Rem
	Method AppendInt:Int(id:String, i:Int)
		Return XMLRPC_VectorAppendInt(Self.vector, id, i)
	End Method

	Rem
		bbdoc: Append boolean value
	End Rem
	Method AppendBoolean:Int(id:String, i:Byte)
		Return XMLRPC_VectorAppendBoolean(Self.vector, id, i)
	End Method
End Type

SuperStrict

Rem
	bbdoc: htbaapub.xmlrpc
EndRem
Module htbaapub.xmlrpc
ModuleInfo "Name: htbaapub.xmlrpc"
ModuleInfo "Version: 1.0"
ModuleInfo "Author: Christiaan Kras"
ModuleInfo "Git repository: http://github.com/Htbaa/htbaapub.mod/"
ModuleInfo "XMLRPC-EPI: http://xmlrpc-epi.sourceforge.net/"
ModuleInfo "Expat: http://expat.sourceforge.net/"
ModuleInfo "iconv: http://gnuwin32.sourceforge.net/packages/libiconv.htm"
ModuleInfo "iconv-win32: http://www.zlatkovic.com/pub/libxml"

Import brl.blitz
Import brl.basic
Import brl.map
Import brl.reflection
Import brl.socket

Import "src/expat-2.0.1/src/*.h"
ModuleInfo "CC_OPTS: -DHAVE_EXPAT_CONFIG_H"
?ppc
	ModuleInfo "CC_OPTS: -DWORDS_BIGENDIAN"
?

Import "src/expat-2.0.1/src/xmlparse.c"
Import "src/expat-2.0.1/src/xmlrole.c"
Import "src/expat-2.0.1/src/xmltok.c"
Import "src/expat-2.0.1/src/xmltok_impl.c"
Import "src/expat-2.0.1/src/xmltok_ns.c"

Import "src/xmlrpc-epi-0.54/src/*.h"
Import "glue.c"

?Win32
	Import "win32.cpp"
	ModuleInfo "LD_OPTS: -L%PWD%/src/iconv-1.9.2.win32/lib"
	Import "src/iconv-1.9.2.win32/include/*.h"
?

Import "-liconv"

Import "src/xmlrpc-epi-0.54/src/xmlrpc.c"
Import "src/xmlrpc-epi-0.54/src/base64.c"
Import "src/xmlrpc-epi-0.54/src/encodings.c"
Import "src/xmlrpc-epi-0.54/src/queue.c"
Import "src/xmlrpc-epi-0.54/src/simplestring.c"
Import "src/xmlrpc-epi-0.54/src/system_methods.c"
Import "src/xmlrpc-epi-0.54/src/xml_element.c"
Import "src/xmlrpc-epi-0.54/src/xml_to_dandarpc.c"
Import "src/xmlrpc-epi-0.54/src/xml_to_soap.c"
Import "src/xmlrpc-epi-0.54/src/xml_to_xmlrpc.c"
Import "src/xmlrpc-epi-0.54/src/xmlrpc_introspection.c"

Include "wrapper.bmx"
Include "datatypes.bmx"
Include "response.bmx"
Include "transport.bmx"
Include "client.bmx"
Include "server.bmx"

Rem
	bbdoc: General XML-RPC Exception
End Rem
Type TXMLRPC_Exception
	Field message:String
	Rem
		bbdoc: Set message for exception
	End Rem
	Method Create:TXMLRPC_Exception(message:String)
		Self.message = message
		Return Self
	End Method

	Rem
		bbdoc: Return exception message
	End Rem
	Method ToString:String()
		Return Self.message
	End Method
End Type

Rem
	bbdoc: Parameters to be passed to the XML-RPC server when using TXMLRPC_Client.Call()
End Rem
Type TXMLRPC_Call_Parameters
	Field vector:Byte Ptr

	Rem
		bbdoc: Create a TXMLRPC_Call_Parameters object.
		about: vectorType can be: xmlrpc_vector_none, xmlrpc_vector_array, xmlrpc_vector_mixed or xmlrpc_vector_struct
	End Rem
	Function Create:TXMLRPC_Call_Parameters(vectorType:Int = xmlrpc_vector_mixed)
		Local parameters:TXMLRPC_Call_Parameters = New TXMLRPC_Call_Parameters
		parameters.vector = XMLRPC_CreateVector(Null, vectorType)
		Return parameters
	End Function

	Rem
		bbdoc: Append string value
	End Rem
	Method AppendString:Int(id:String, s:String)
		Return XMLRPC_VectorAppendString(Self.vector, id, s)
	End Method

	Rem
		bbdoc: Append Base64 value. Just pass a string, it'll be BASE64 encoded for you
	End Rem
	Method AppendBase64:Int(id:String, s:String)
		Return XMLRPC_VectorAppendBase64(Self.vector, id, s)
	End Method

	Rem
		bbdoc: Append UNIX timestmap value
	End Rem
	Method AppendDateTime:Int(id:String, time:Long)
		Return XMLRPC_VectorAppendDateTime(Self.vector, id, time)
	End Method

	Rem
		bbdoc: Append datetime in ISO8601 format
	End Rem
	Method AppendDateTime_ISO8601:Int(id:String, s:String)
		Return XMLRPC_VectorAppendDateTime_ISO8601(Self.vector, id, s)
	End Method

	Rem
		bbdoc: Append double value
	End Rem
	Method AppendDouble:Int(id:String, f:Double)
		Return XMLRPC_VectorAppendDouble(Self.vector, id, f)
	End Method

	Rem
		bbdoc: Append integer value
	End Rem
	Method AppendInt:Int(id:String, i:Int)
		Return XMLRPC_VectorAppendInt(Self.vector, id, i)
	End Method

	Rem
		bbdoc: Append boolean value
	End Rem
	Method AppendBoolean:Int(id:String, i:Byte)
		Return XMLRPC_VectorAppendBoolean(Self.vector, id, i)
	End Method
End Type