Removed intersection code.

Removed intersection code.

Strict

Import BRL.LinkedList
Import MaxB3D.Math
Import "brush.bmx"
Import "collision.bmx"
Import "worldconfig.bmx"

Const PICKMODE_OFF			= 0
Const PICKMODE_SPHERE		= 1
Const PICKMODE_POLYGON	= 2
Const PICKMODE_BOX			= 3

Type TEntity
	Field _config:TWorldConfig
	Field _matrix:TMatrix=TMatrix.Identity(), _lockmatrix

	Field _name$
	Field _px#,_py#,_pz#
	Field _rx#,_ry#,_rz#
	Field _sx#,_sy#,_sz#

	Field _brush:TBrush=New TBrush

	Field _parent:TEntity,_childlist:TList=CreateList()
	Field _hidden,_order

	Field _collision:TCollision[]
	Field _oldx#,_oldy#,_oldz#
	Field _radiusx#,_radiusy#
	Field _boxx#,_boxy#,_boxz#,_boxwidth#,_boxheight#,_boxdepth#
	Field _type,_typelink:TLink
	Field _pickmode, _obscurer

	Field _cullradius#

	Field _alphaorder#

	Field _linklist:TList=CreateList()

	Method Init:TEntity(config:TWorldConfig,parent:TEntity)
		_config = config
		SetParent parent,False
		For Local l=EachIn Lists()
			AddLink _config.AddObject(Self,l)
		Next

		SetScale 1,1,1

		Return Self
	End Method

	Method Free()
		For Local child:TEntity=EachIn _childlist
			child.Free()
		Next
		For Local link:TLink=EachIn _linklist
			RemoveLink link
		Next
	End Method

	Method AddLink(link:TLink)
		_linklist.AddLast link
	End Method

	Method Lists[]()
		Return [WORLDLIST_ENTITY]
	End Method

	Method CopyData:TEntity(entity:TEntity)
		_config = entity._config
		Local pickmode, obscurer
		entity.GetPickMode pickmode,obscurer

		SetBrush entity.GetBrush()
		SetName entity.GetName()
		SetPickMode pickmode, obscurer
		SetType entity.GetType()
		Return Self
	End Method

	Method Copy_:TEntity(parent:TEntity=Null)
		Return New Self.CopyData(Self).Init(_config,parent)
	End Method

	Method Copy:TEntity(parent:TEntity=Null) Abstract

	Method AddParent(parent:TEntity)
		_parent=parent
		If parent
			_matrix.Overwrite parent._matrix
			parent._childlist.AddLast Self
		EndIf
	End Method

	Method GetParent:TEntity()
		Return _parent
	End Method
	Method SetParent(parent:TEntity,glob=True)
		Local gpx#,gpy#,gpz#
		Local grx#,gry#,grz#
		Local gsx#,gsy#,gsz#

		GetPosition gpx,gpy,gpz,True
		GetRotation grx,gry,grz,True
		GetScale gsx,gsy,gsz,True

		If _parent
			For Local entity:TEntity=EachIn _parent._childlist
				If entity=Self ListRemove(_parent._childlist,Self)
			Next
			_parent=Null
		EndIf

		_px=gpx;_py=gpy;_pz=gpz
		_rx=grx;_ry=gry;_rz=grz
		_sx=gsx;_sy=gsy;_sz=gsz

		If parent=Null UpdateMatrix(True);Return

		AddParent(parent)
		If glob
			SetPosition(gpx,gpy,gpz,True)
			SetRotation(grx,gry,grz,True)
			SetScale(gsx,gsy,gsz,True)
		Else
			UpdateMatrix(False)
		EndIf
	End Method

	Method CountChildren(recursive=False)
		Local count
		If recursive
			For Local child:TEntity=EachIn _childlist
				count:+child.CountChildren(True)
			Next
		EndIf
		Return _childlist.Count()+count
	End Method

	Method FindChild:TEntity(name$,recursive=False)
		For Local child:TEntity=EachIn _childlist
			If child.GetName()=name Return child
		Next
		If recursive
			For Local child:TEntity=EachIn _childlist
				Local entity:TEntity = child.FindChild(name, True)
				If entity Return entity
			Next
		EndIf
	End Method

	Method GetName$()
		Return _name
	End Method
	Method SetName(name$)
		_name=name
	End Method

	Method GetBrush:TBrush()
		Return _brush.Copy()
	End Method
	Method SetBrush(brush:TBrush)
		_brush.Load brush
	End Method

	Method GetColor(red Var,green Var,blue Var)
		Return _brush.GetColor(red,green,blue)
	End Method
	Method SetColor(red,green,blue)
		Return _brush.SetColor(red,green,blue)
	End Method

	Method GetAlpha#()
		Return _brush.GetAlpha()
	End Method
	Method SetAlpha(alpha#)
		_brush.SetAlpha(alpha)
	End Method

	Method GetShine#()
		Return _brush.GetShine()
	End Method
	Method SetShine(shine#)
		_brush.SetShine(shine)
	End Method

	Method GetTexture:TTexture(index)
		Return _brush.GetTexture(index)
	End Method
	Method SetTexture(texture:TTexture,index=0,frame=0)
		Return _brush.SetTexture(texture,index,frame)
	End Method

	Method GetFX()
		Return _brush.GetFX()
	End Method
	Method SetFX(fx)
		Return _brush.SetFX(fx)
	End Method

	Method GetBlend()
		Return _brush.GetBlend()
	End Method
	Method SetBlend(blend)
		Return _brush.SetBlend(blend)
	End Method

	Method GetShader:TShader()
		Return _brush.GetShader()
	End Method
	Method SetShader(shader:TShader)
		Return _brush.SetShader(shader)
	End Method

	Method GetScale(x# Var,y# Var,z# Var,glob=False)
		If glob
			x=_matrix._m[0,0]
			y=_matrix._m[1,1]
			z=_matrix._m[2,2]
		Else
			x=_sx;y=_sy;z=_sz
		EndIf
	End Method
	Method SetScale(x#,y#,z#,glob=False)
		_sx=x;_sy=y;_sz=z
		If glob=True And _parent<>Null
			Local entity:TEntity=Self
			Repeat
				_sx:/entity._parent._sx
				_sy:/entity._parent._sy
				_sz:/entity._parent._sz
				entity=entity._parent
			Until entity._parent=Null
		EndIf
		RefreshMatrix()
	End Method

	Method Transform(matrix:TMatrix,glob=False)
		SetMatrix matrix.Multiply(_matrix),glob
	End Method

	Method Turn(pitch#,yaw#,roll#,glob=False)
		_rx:+pitch;_ry:+yaw;_rz:+roll
		RefreshMatrix()
	End Method

	Method Point(target:Object,roll#=0.0)
		Local x#,y#,z#,tx#,ty#,tz#
		GetPosition x,y,z,True
		GetTargetPosition target,tx,ty,tz

		Local xdiff#=x-tx,ydiff#=y-ty,zdiff#=z-tz

		Local dist22#=Sqr((xdiff*xdiff)+(zdiff*zdiff))
		Local pitch#=ATan2(ydiff,dist22)
		Local yaw#=ATan2(xdiff,-zdiff)

		SetRotation pitch,yaw,roll,True
	End Method

	Method GetDistance#(target:Object)
		Local x#,y#,z#,tx#,ty#,tz#
		GetPosition x,y,z,True
		GetTargetPosition target,tx,ty,tz
		Return Sqr((x-tx)*(x-tx)+(y-ty)*(y-ty)+(z-tz)*(z-tz))
	End Method

	Function GetTargetPosition(target:Object,x# Var,y# Var,z# Var)
		Assert target,"Null target given."
		If TEntity(target) TEntity(target).GetPosition x,y,z,True
		If Float[](target)
			Local coords#[]=Float[](target)
			If coords.length>0 x=coords[0]
			If coords.length>1 y=coords[1]
			If coords.length>2 z=coords[2]
		EndIf
		Assert "Invalid target given."
	End Function

	Method GetRotation(pitch# Var,yaw# Var,roll# Var,glob=False)
		If glob
			_matrix.GetRotation pitch,yaw,roll
			pitch:*-1
			yaw:*-1
		Else
			pitch=_rx;yaw=_ry;roll=_rz
		EndIf
	End Method
	Method SetRotation(pitch#,yaw#,roll#,glob=False)
		_rx=pitch;_ry=yaw;_rz=roll
		If glob And _parent<>Null
			Local rx#,ry#,rz#
			_parent.GetRotation rx,ry,rz
			_rx:+rx;_ry:+ry;_rz:+rz
		EndIf
		RefreshMatrix()
	End Method

	Method GetPosition(x# Var,y# Var,z# Var,glob=False)
		x=_px;y=_py;z=_pz
		If glob _matrix.GetPosition x,y,z
	End Method
	Method SetPosition(x#,y#,z#,glob=False)
		_px=x;_py=y;_pz=z

		If glob And _parent<>Null
			Local px#,py#,pz#
			_parent.GetPosition px,py,pz,True
			_px:-px;_py:-py;_pz:-pz
		EndIf

		RefreshMatrix()
	End Method

	Method Move(x#,y#,z#)
		Local matrix:TMatrix=TMatrix.Identity()
		matrix=TMatrix.YawPitchRoll(-_ry,-_rx,_rz).Multiply(matrix)
		matrix=TMatrix.Translation(x,y,z).Multiply(matrix)

		matrix.GetPosition(x,y,z)
		_px:+x;_py:+y;_pz:+z

		RefreshMatrix()
	End Method

	Method Translate(x#,y#,z#,glob=True)
		If glob And _parent
			Local ax#,ay#,az#
			GetRotation ax,ay,az,True

			Local matrix:TMatrix=TMatrix.Roll(-az)
			matrix=matrix.Multiply(TMatrix.Pitch(-ax))
			matrix=matrix.Multiply(TMatrix.Yaw(-ay))
			matrix=matrix.Multiply(TMatrix.Translation(x,y,z))

			matrix.GetPosition x,y,z
		EndIf

		_px:+x;_py:+y;_pz:+z

		RefreshMatrix()
	End Method

	Method GetVisible()
		Local entity:TEntity=Self
		While entity<>Null
			If entity._hidden=True Return False
			entity=entity._parent
		Wend
		Return True
	End Method
	Method SetVisible(visible)
		_hidden=Not visible
	End Method

	Method GetOrder()
		Return _order
	End Method
	Method SetOrder(order)
		_order=order
	End Method

	Method GetCollisions:TCollision[]()
		Return _collision
	End Method

	Method GetRadius(x# Var,y# Var)
		x=_radiusx;y=_radiusy
	End Method
	Method SetRadius(x#,y#=-1)
		_radiusx=x;_radiusy=x
		If y>-1 _radiusy=y
	End Method

	Method GetBox(x# Var,y# Var,z# Var,width# Var,height# Var,depth# Var)
		x=_boxx;y=_boxy;z=_boxz
		width=_boxwidth;height=_boxheight;depth=_boxdepth
	End Method
	Method SetBox(x#,y#,z#,width#,height#,depth#)
		_boxx=x;_boxy=y;_boxz=z
		_boxwidth=width;_boxheight=height;_boxdepth=depth
	End Method

	Method Reset()
		_collision=Null
		GetPosition _oldx,_oldy,_oldz,True
	End Method

	Method GetType()
		Return _type
	End Method
	Method SetType(typ,recursive=False)
		If _typelink
			_linklist.Remove(_typelink)
			_typelink.Remove()
			_typelink=Null
		EndIf

		If typ>0
			If _config.CollisionType[typ]=Null _config.CollisionType[typ]=CreateList()
			_typelink=_config.CollisionType[typ].AddLast(Self)
			AddLink _typelink
		EndIf

		_type=typ
		GetPosition _oldx,_oldy,_oldz,True

		If recursive
			For Local child:TEntity=EachIn _childlist
				child.SetType typ,True
			Next
		EndIf
	End Method

	Method GetCullRadius#()
		Return Abs(_cullradius)
	End Method
	Method SetCullRadius(radius#)
		_cullradius=-radius
	End Method

	Method GetCullParams(x# Var,y# Var,z# Var,radius# Var)
		GetPosition x,y,z,True
		radius=GetCullRadius()
	End Method

	Method GetPickMode(mode Var, obscurer Var)
		mode = _pickmode
		obscurer = _obscurer
	End Method
	Method SetPickMode(mode, obscurer=True)
		_pickmode = mode
		_obscurer = obscurer
	End Method

	Method HasAlpha()
		Return _brush.HasAlpha()
	End Method

	Method GetMatrix:TMatrix(alternate=False,copy=True)
		If copy Return _matrix.Copy()
		Return _matrix
	End Method
	Method SetMatrix(matrix:TMatrix,glob=True)
		Local x#,y#,z#,pitch#,yaw#,roll#,sx#,sy#,sz#
		matrix.GetPosition x,y,z
		matrix.GetRotation pitch,yaw,roll
		matrix.GetScale sx,sy,sz

		LockMatrix
		SetPosition x,y,z,glob
		SetRotation pitch,yaw,roll,glob
		SetScale sx,sy,sz,glob
		UnlockMatrix
	End Method

	Method LockMatrix()
		_lockmatrix=True
	End Method
	Method UnlockMatrix()
		_lockmatrix=False
		RefreshMatrix()
	End Method

	Method RefreshMatrix()
		If _lockmatrix Return
		If _parent<>Null
			_matrix.Overwrite _parent._matrix
			UpdateMatrix False
		Else
			UpdateMatrix True
		EndIf

		For Local child:TEntity=EachIn _childlist
			child.RefreshMatrix()
		Next
	End Method

	Method UpdateMatrix(loadidentity)
		If loadidentity _matrix=TMatrix.Identity()
		_matrix=TMatrix.Translation(_px,_py,_pz).Multiply(_matrix)
		_matrix=TMatrix.YawPitchRoll(-_ry,-_rx,_rz).Multiply(_matrix)
		_matrix=TMatrix.Scale(_sx,_sy,_sz).Multiply(_matrix)
	End Method

	Method ObjectEnumerator:Object()
		Return TChildrenEnumerator.Create(_childlist)
	End Method
End Type

Type TChildrenEnumerator
	Field _children:TEntity[],_index=-1

	Function Create:TChildrenEnumerator(list:TList)
		Local enum:TChildrenEnumerator=New TChildrenEnumerator
		enum._children=TEntity[](list.ToArray())
		Return enum
	End Function

	Method HasNext()
		Return _index>=_children.length-1
	End Method

	Method NextObject:Object()
		_index:+1
		Return _children[_index]
	End Method
End Type

Strict

Import BRL.LinkedList
Import MaxB3D.Math
Import "brush.bmx"
Import "collision.bmx"
Import "worldconfig.bmx"

Const PICKMODE_OFF			= 0
Const PICKMODE_SPHERE		= 1
Const PICKMODE_POLYGON	= 2
Const PICKMODE_BOX			= 3

Type TEntity
	Field _config:TWorldConfig
	Field _matrix:TMatrix=TMatrix.Identity(), _lockmatrix

	Field _name$
	Field _px#,_py#,_pz#
	Field _rx#,_ry#,_rz#
	Field _sx#,_sy#,_sz#

	Field _brush:TBrush=New TBrush

	Field _parent:TEntity,_childlist:TList=CreateList()
	Field _hidden,_order

	Field _collision:TCollision[]
	Field _oldx#,_oldy#,_oldz#
	Field _radiusx#,_radiusy#
	Field _boxx#,_boxy#,_boxz#,_boxwidth#,_boxheight#,_boxdepth#
	Field _type,_typelink:TLink
	Field _pickmode, _obscurer

	Field _cullradius#

	Field _alphaorder#

	Field _linklist:TList=CreateList()

	Method Init:TEntity(config:TWorldConfig,parent:TEntity)
		_config = config
		SetParent parent,False
		For Local l=EachIn Lists()
			AddLink _config.AddObject(Self,l)
		Next

		SetScale 1,1,1

		Return Self
	End Method

	Method Free()
		For Local child:TEntity=EachIn _childlist
			child.Free()
		Next
		For Local link:TLink=EachIn _linklist
			RemoveLink link
		Next
	End Method

	Method AddLink(link:TLink)
		_linklist.AddLast link
	End Method

	Method Lists[]()
		Return [WORLDLIST_ENTITY]
	End Method

	Method CopyData:TEntity(entity:TEntity)
		_config = entity._config
		Local pickmode, obscurer
		entity.GetPickMode pickmode,obscurer

		SetBrush entity.GetBrush()
		SetName entity.GetName()
		SetPickMode pickmode, obscurer
		SetType entity.GetType()
		Return Self
	End Method

	Method Copy_:TEntity(parent:TEntity=Null)
		Return New Self.CopyData(Self).Init(_config,parent)
	End Method

	Method Copy:TEntity(parent:TEntity=Null) Abstract

	Method AddParent(parent:TEntity)
		_parent=parent
		If parent
			_matrix.Overwrite parent._matrix
			parent._childlist.AddLast Self
		EndIf
	End Method

	Method GetParent:TEntity()
		Return _parent
	End Method
	Method SetParent(parent:TEntity,glob=True)
		Local gpx#,gpy#,gpz#
		Local grx#,gry#,grz#
		Local gsx#,gsy#,gsz#

		GetPosition gpx,gpy,gpz,True
		GetRotation grx,gry,grz,True
		GetScale gsx,gsy,gsz,True

		If _parent
			For Local entity:TEntity=EachIn _parent._childlist
				If entity=Self ListRemove(_parent._childlist,Self)
			Next
			_parent=Null
		EndIf

		_px=gpx;_py=gpy;_pz=gpz
		_rx=grx;_ry=gry;_rz=grz
		_sx=gsx;_sy=gsy;_sz=gsz

		If parent=Null UpdateMatrix(True);Return

		AddParent(parent)
		If glob
			SetPosition(gpx,gpy,gpz,True)
			SetRotation(grx,gry,grz,True)
			SetScale(gsx,gsy,gsz,True)
		Else
			UpdateMatrix(False)
		EndIf
	End Method

	Method CountChildren(recursive=False)
		Local count
		If recursive
			For Local child:TEntity=EachIn _childlist
				count:+child.CountChildren(True)
			Next
		EndIf
		Return _childlist.Count()+count
	End Method

	Method FindChild:TEntity(name$,recursive=False)
		For Local child:TEntity=EachIn _childlist
			If child.GetName()=name Return child
		Next
		If recursive
			For Local child:TEntity=EachIn _childlist
				Local entity:TEntity = child.FindChild(name, True)
				If entity Return entity
			Next
		EndIf
	End Method

	Method GetName$()
		Return _name
	End Method
	Method SetName(name$)
		_name=name
	End Method

	Method GetBrush:TBrush()
		Return _brush.Copy()
	End Method
	Method SetBrush(brush:TBrush)
		_brush.Load brush
	End Method

	Method GetColor(red Var,green Var,blue Var)
		Return _brush.GetColor(red,green,blue)
	End Method
	Method SetColor(red,green,blue)
		Return _brush.SetColor(red,green,blue)
	End Method

	Method GetAlpha#()
		Return _brush.GetAlpha()
	End Method
	Method SetAlpha(alpha#)
		_brush.SetAlpha(alpha)
	End Method

	Method GetShine#()
		Return _brush.GetShine()
	End Method
	Method SetShine(shine#)
		_brush.SetShine(shine)
	End Method

	Method GetTexture:TTexture(index)
		Return _brush.GetTexture(index)
	End Method
	Method SetTexture(texture:TTexture,index=0,frame=0)
		Return _brush.SetTexture(texture,index,frame)
	End Method

	Method GetFX()
		Return _brush.GetFX()
	End Method
	Method SetFX(fx)
		Return _brush.SetFX(fx)
	End Method

	Method GetBlend()
		Return _brush.GetBlend()
	End Method
	Method SetBlend(blend)
		Return _brush.SetBlend(blend)
	End Method

	Method GetShader:TShader()
		Return _brush.GetShader()
	End Method
	Method SetShader(shader:TShader)
		Return _brush.SetShader(shader)
	End Method

	Method GetScale(x# Var,y# Var,z# Var,glob=False)
		If glob
			x=_matrix._m[0,0]
			y=_matrix._m[1,1]
			z=_matrix._m[2,2]
		Else
			x=_sx;y=_sy;z=_sz
		EndIf
	End Method
	Method SetScale(x#,y#,z#,glob=False)
		_sx=x;_sy=y;_sz=z
		If glob=True And _parent<>Null
			Local entity:TEntity=Self
			Repeat
				_sx:/entity._parent._sx
				_sy:/entity._parent._sy
				_sz:/entity._parent._sz
				entity=entity._parent
			Until entity._parent=Null
		EndIf
		RefreshMatrix()
	End Method

	Method Transform(matrix:TMatrix,glob=False)
		SetMatrix matrix.Multiply(_matrix),glob
	End Method

	Method Turn(pitch#,yaw#,roll#,glob=False)
		_rx:+pitch;_ry:+yaw;_rz:+roll
		RefreshMatrix()
	End Method

	Method Point(target:Object,roll#=0.0)
		Local x#,y#,z#,tx#,ty#,tz#
		GetPosition x,y,z,True
		GetTargetPosition target,tx,ty,tz

		Local xdiff#=x-tx,ydiff#=y-ty,zdiff#=z-tz

		Local dist22#=Sqr((xdiff*xdiff)+(zdiff*zdiff))
		Local pitch#=ATan2(ydiff,dist22)
		Local yaw#=ATan2(xdiff,-zdiff)

		SetRotation pitch,yaw,roll,True
	End Method

	Method GetDistance#(target:Object)
		Local x#,y#,z#,tx#,ty#,tz#
		GetPosition x,y,z,True
		GetTargetPosition target,tx,ty,tz
		Return Sqr((x-tx)*(x-tx)+(y-ty)*(y-ty)+(z-tz)*(z-tz))
	End Method

	Function GetTargetPosition(target:Object,x# Var,y# Var,z# Var)
		Assert target,"Null target given."
		If TEntity(target) TEntity(target).GetPosition x,y,z,True
		If Float[](target)
			Local coords#[]=Float[](target)
			If coords.length>0 x=coords[0]
			If coords.length>1 y=coords[1]
			If coords.length>2 z=coords[2]
		EndIf
		Assert "Invalid target given."
	End Function

	Method GetRotation(pitch# Var,yaw# Var,roll# Var,glob=False)
		If glob
			_matrix.GetRotation pitch,yaw,roll
			pitch:*-1
			yaw:*-1
		Else
			pitch=_rx;yaw=_ry;roll=_rz
		EndIf
	End Method
	Method SetRotation(pitch#,yaw#,roll#,glob=False)
		_rx=pitch;_ry=yaw;_rz=roll
		If glob And _parent<>Null
			Local rx#,ry#,rz#
			_parent.GetRotation rx,ry,rz
			_rx:+rx;_ry:+ry;_rz:+rz
		EndIf
		RefreshMatrix()
	End Method

	Method GetPosition(x# Var,y# Var,z# Var,glob=False)
		x=_px;y=_py;z=_pz
		If glob _matrix.GetPosition x,y,z
	End Method
	Method SetPosition(x#,y#,z#,glob=False)
		_px=x;_py=y;_pz=z

		If glob And _parent<>Null
			Local px#,py#,pz#
			_parent.GetPosition px,py,pz,True
			_px:-px;_py:-py;_pz:-pz
		EndIf

		RefreshMatrix()
	End Method

	Method Move(x#,y#,z#)
		Local matrix:TMatrix=TMatrix.Identity()
		matrix=TMatrix.YawPitchRoll(-_ry,-_rx,_rz).Multiply(matrix)
		matrix=TMatrix.Translation(x,y,z).Multiply(matrix)

		matrix.GetPosition(x,y,z)
		_px:+x;_py:+y;_pz:+z

		RefreshMatrix()
	End Method

	Method Translate(x#,y#,z#,glob=True)
		If glob And _parent
			Local ax#,ay#,az#
			GetRotation ax,ay,az,True

			Local matrix:TMatrix=TMatrix.Roll(-az)
			matrix=matrix.Multiply(TMatrix.Pitch(-ax))
			matrix=matrix.Multiply(TMatrix.Yaw(-ay))
			matrix=matrix.Multiply(TMatrix.Translation(x,y,z))

			matrix.GetPosition x,y,z
		EndIf

		_px:+x;_py:+y;_pz:+z

		RefreshMatrix()
	End Method

	Method GetVisible()
		Local entity:TEntity=Self
		While entity<>Null
			If entity._hidden=True Return False
			entity=entity._parent
		Wend
		Return True
	End Method
	Method SetVisible(visible)
		_hidden=Not visible
	End Method

	Method GetOrder()
		Return _order
	End Method
	Method SetOrder(order)
		_order=order
	End Method

	Method GetCollisions:TCollision[]()
		Return _collision
	End Method

	Method GetRadius(x# Var,y# Var)
		x=_radiusx;y=_radiusy
	End Method
	Method SetRadius(x#,y#=-1)
		_radiusx=x;_radiusy=x
		If y>-1 _radiusy=y
	End Method

	Method GetBox(x# Var,y# Var,z# Var,width# Var,height# Var,depth# Var)
		x=_boxx;y=_boxy;z=_boxz
		width=_boxwidth;height=_boxheight;depth=_boxdepth
	End Method
	Method SetBox(x#,y#,z#,width#,height#,depth#)
		_boxx=x;_boxy=y;_boxz=z
		_boxwidth=width;_boxheight=height;_boxdepth=depth
	End Method

	Method Reset()
		_collision=Null
		GetPosition _oldx,_oldy,_oldz,True
	End Method

	Method GetType()
		Return _type
	End Method
	Method SetType(typ,recursive=False)
		If _typelink
			_linklist.Remove(_typelink)
			_typelink.Remove()
			_typelink=Null
		EndIf

		If typ>0
			If _config.CollisionType[typ]=Null _config.CollisionType[typ]=CreateList()
			_typelink=_config.CollisionType[typ].AddLast(Self)
			AddLink _typelink
		EndIf

		_type=typ
		GetPosition _oldx,_oldy,_oldz,True

		If recursive
			For Local child:TEntity=EachIn _childlist
				child.SetType typ,True
			Next
		EndIf
	End Method

	Method GetCullRadius#()
		Return Abs(_cullradius)
	End Method
	Method SetCullRadius(radius#)
		_cullradius=-radius
	End Method

	Method GetCullParams(x# Var,y# Var,z# Var,radius# Var)
		GetPosition x,y,z,True
		radius=GetCullRadius()
	End Method

	Method IntersectsLine:TVector(ptA:TVector, ptB:TVector)
		Return Null
	End Method

	Method GetPickMode(mode Var, obscurer Var)
		mode = _pickmode
		obscurer = _obscurer
	End Method
	Method SetPickMode(mode, obscurer=True)
		_pickmode = mode
		_obscurer = obscurer
	End Method

	Method HasAlpha()
		Return _brush.HasAlpha()
	End Method

	Method GetMatrix:TMatrix(alternate=False,copy=True)
		If copy Return _matrix.Copy()
		Return _matrix
	End Method
	Method SetMatrix(matrix:TMatrix,glob=True)
		Local x#,y#,z#,pitch#,yaw#,roll#,sx#,sy#,sz#
		matrix.GetPosition x,y,z
		matrix.GetRotation pitch,yaw,roll
		matrix.GetScale sx,sy,sz

		LockMatrix
		SetPosition x,y,z,glob
		SetRotation pitch,yaw,roll,glob
		SetScale sx,sy,sz,glob
		UnlockMatrix
	End Method

	Method LockMatrix()
		_lockmatrix=True
	End Method
	Method UnlockMatrix()
		_lockmatrix=False
		RefreshMatrix()
	End Method

	Method RefreshMatrix()
		If _lockmatrix Return
		If _parent<>Null
			_matrix.Overwrite _parent._matrix
			UpdateMatrix False
		Else
			UpdateMatrix True
		EndIf

		For Local child:TEntity=EachIn _childlist
			child.RefreshMatrix()
		Next
	End Method

	Method UpdateMatrix(loadidentity)
		If loadidentity _matrix=TMatrix.Identity()
		_matrix=TMatrix.Translation(_px,_py,_pz).Multiply(_matrix)
		_matrix=TMatrix.YawPitchRoll(-_ry,-_rx,_rz).Multiply(_matrix)
		_matrix=TMatrix.Scale(_sx,_sy,_sz).Multiply(_matrix)
	End Method

	Method ObjectEnumerator:Object()
		Return TChildrenEnumerator.Create(_childlist)
	End Method
End Type

Type TChildrenEnumerator
	Field _children:TEntity[],_index=-1

	Function Create:TChildrenEnumerator(list:TList)
		Local enum:TChildrenEnumerator=New TChildrenEnumerator
		enum._children=TEntity[](list.ToArray())
		Return enum
	End Function

	Method HasNext()
		Return _index>=_children.length-1
	End Method

	Method NextObject:Object()
		_index:+1
		Return _children[_index]
	End Method
End Type


[logging] (v0.3) 	Added CloseLogStream method to TLogger

Rem
	Copyright (c) 2009 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.
	-----------------------------------------------------------------------------

	logging.bmx (Contains: TLogger, )

End Rem

SuperStrict

Rem
bbdoc: Logging module
End Rem
Module duct.logging

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

ModuleInfo "History: Version 0.3"
ModuleInfo "History: Added CloseLogStream method to TLogger"
ModuleInfo "History: Version 0.2"
ModuleInfo "History: General cleanup"
ModuleInfo "History: Version 0.1"
ModuleInfo "History: Initial release"

Import brl.standardio
Import brl.stream

Import duct.appendstream
Import duct.etc

Rem
	bbdoc: Stream/Debug/StandardIO based logger.
End Rem
Type TLogger

	Const LOGTYPE_NONE:Int = 1, LOGTYPE_WARNING:Int = 2, LOGTYPE_ERROR:Int = 3

	Field m_timeformat:String
	Field m_formatter:TTextReplacer, m_rep_time:TTextReplacement, m_rep_type:TTextReplacement, m_rep_msg:TTextReplacement

	Field m_logfile:String, m_logstream:TStream
	Field m_usestream:Int = True, m_useprint:Int = False, m_usedebuglog:Int = False

	Method New()
		m_formatter = New TTextReplacer
	End Method

	Rem
		bbdoc: Create a logger.
		returns: The new logger (itself).
	End Rem
	Method Create:TLogger(timeformat:String = "%H:%M:%S", format:String = "[{time} {type}] {msg}", logfile:String, _usestream:Int = True, _useprint:Int = False, _usedebuglog:Int = False)
		SetTimeFormat(timeformat)
		SetFormat(format)
		SetLogFile(logfile)
		UseStream(_usestream)
		UsePrint(_useprint)
		UseDebugLog(_usedebuglog)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the time format.
		returns: Nothing.
	End Rem
	Method SetTimeFormat(timeformat:String)
		m_timeformat = timeformat
	End Method
	Rem
		bbdoc: Get the time format.
		returns: The time format for logged information.
	End Rem
	Method GetTimeFormat:String()
		Return m_timeformat
	End Method

	Rem
		bbdoc: Set the format.
		returns: Nothing.
	End Rem
	Method SetFormat(format:String, beginiden:String = "{", endiden:String = "}")
		m_formatter.SetString(format)
		m_formatter.AutoReplacements(beginiden, endiden)
		m_rep_time = m_formatter.GetReplacementFromName("time")
		m_rep_type = m_formatter.GetReplacementFromName("type")
		m_rep_msg = m_formatter.GetReplacementFromName("msg")
	End Method
	Rem
		bbdoc: Get the format.
		returns: The format for logged information.
	End Rem
	Method GetFormat:String(beginiden:String = "{", endiden:String = "}")
		Return m_formatter.GetOriginal(beginiden, endiden)
	End Method

	Rem
		bbdoc: Set the log file.
		returns: Nothing.
	End Rem
	Method SetLogFile(logfile:String)
		m_logfile = logfile
	End Method
	Rem
		bbdoc: Get the log file.
		returns: The file for the logged information.
	End Rem
	Method GetLogFile:String()
		Return m_logfile
	End Method

	Rem
		bbdoc: Set the log stream.
		returns: Nothing.
	End Rem
	Method SetLogStream(logstream:TStream)
		m_logstream = logstream
	End Method
	Rem
		bbdoc: Get the log stream.
		returns: The log stream (may be Null).
	End Rem
	Method GetLogStream:TStream()
		Return m_logstream
	End Method
	Rem
		bbdoc: Open the log stream to the set log file.
		returns: True if the stream was opened, or False if it was not.
		about: This will open the log file stream.
	End Rem
	Method OpenLogStream:Int(append:Int = True)
		Local file:String

		If append = True
			file = "append::" + m_logfile
		Else
			file = m_logfile
		End If
		m_logstream = WriteStream(file)

		Return m_logstream <> Null
	End Method

	Rem
		bbdoc: Close the log stream.
		returns: True if the stream was closed, or False if it was not (stream has not been opened/set).
	End Rem
	Method CloseLogStream:Int()
		If m_logstream <> Null
			m_logstream.Close()
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Set stream usage state (True - on - or False - off).
		returns: Nothing.
	End Rem
	Method UseStream(_usestream:Int)
		m_usestream = _usestream
	End Method
	Rem
		bbdoc: Get the stream-usage state.
		returns: True (on) or False (off).
	End Rem
	Method UsingStream:Int()
		Return m_usestream
	End Method

	Rem
		bbdoc: Set Print usage state (True - on - or False - off).
		returns: Nothing.
	End Rem
	Method UsePrint(_useprint:Int)
		m_useprint = _useprint
	End Method
	Rem
		bbdoc: Get the Print-usage state.
		returns: True (on) or False (off).
	End Rem
	Method UsingPrint:Int()
		Return m_useprint
	End Method

	Rem
		bbdoc: Set DebugLog usage state (True - on - or False - off).
		returns: Nothing.
	End Rem
	Method UseDebugLog(_usedebuglog:Int)
		m_usedebuglog = _usedebuglog
	End Method
	Rem
		bbdoc: Get the DebugLog-usage state.
		returns: True (on) or False (off).
	End Rem
	Method UsingDebugLog:Int()
		Return m_usedebuglog
	End Method

'#end region (Field accessors)

'#region Formatting

	Rem
		bbdoc: Get the current time in the logger's time format.
		returns: The current time in the logger's time format, or Null if the format is Null.
	End Rem
	Method GetFormattedTime:String()
		If m_timeformat <> Null
			Return TimeInFormat(m_timeformat)
		Else
			Return Null
		End If
	End Method

	Rem
		bbdoc: Get the given message formatted in the given log type.
		returns: Nothing.
	End Rem
	Method GetFormattedMessage:String(message:String, logtype:Int = LOGTYPE_NONE)
		If m_rep_time <> Null And m_timeformat <> Null
			m_rep_time.SetReplacement(GetFormattedTime())
		End If
		If m_rep_type <> Null
			Select logtype
				Case LOGTYPE_NONE
					m_rep_type.SetReplacement("")
				Case LOGTYPE_WARNING
					m_rep_type.SetReplacement("WARNING")
				Case LOGTYPE_ERROR
					m_rep_type.SetReplacement("ERROR")
			End Select
		End If
		If m_rep_msg <> Null
			m_rep_msg.SetReplacement(message)
		End If

		Return m_formatter.DoReplacements()
	End Method

'#end region (Formatting)

'#region Logging

	Rem
		bbdoc: Log a string in the given log type.
		returns: Nothing.
	End Rem
	Method LogString(message:String, formatted:Int = True, logtype:Int = LOGTYPE_NONE)
		Local formattedmessage:String

		If formatted = True
			formattedmessage = GetFormattedMessage(message, logtype)
		Else
			formattedmessage = message
		End If

		If m_usestream = True And m_logstream <> Null
			m_logstream.WriteLine(formattedmessage)
		End If
		If m_useprint = True
			Print(formattedmessage)
		End If
		If m_usedebuglog = True
			DebugLog(formattedmessage)
		End If
	End Method

	Rem
		bbdoc: Log a generic message.
		returns: Nothing.
	End Rem
	Method LogMessage(message:String, formatted:Int = True)
		LogString(message, formatted, LOGTYPE_NONE)
	End Method

	Rem
		bbdoc: Log a warning message.
		returns: Nothing.
	End Rem
	Method LogWarning(message:String, formatted:Int = True)
		LogString(message, formatted, LOGTYPE_WARNING)
	End Method

	Rem
		bbdoc: Log an error message.
		returns: Nothing.
	End Rem
	Method LogError(message:String, formatted:Int = True)
		LogString(message, formatted, LOGTYPE_ERROR)
	End Method

'#end region (Logging)

End Type

Rem
	Copyright (c) 2009 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.
	-----------------------------------------------------------------------------

	logging.bmx (Contains: TLogger, )

End Rem

SuperStrict

Rem
bbdoc: Logging module
End Rem
Module duct.logging

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

ModuleInfo "History: Version 0.2"
ModuleInfo "History: General cleanup"
ModuleInfo "History: Version 0.1"
ModuleInfo "History: Initial release"

Import brl.standardio
Import brl.stream

Import duct.appendstream
Import duct.etc

Rem
	bbdoc: Stream/Debug/StandardIO based logger.
End Rem
Type TLogger

	Const LOGTYPE_NONE:Int = 1, LOGTYPE_WARNING:Int = 2, LOGTYPE_ERROR:Int = 3

	Field m_timeformat:String
	Field m_formatter:TTextReplacer, m_rep_time:TTextReplacement, m_rep_type:TTextReplacement, m_rep_msg:TTextReplacement

	Field m_logfile:String, m_logstream:TStream
	Field m_usestream:Int = True, m_useprint:Int = False, m_usedebuglog:Int = False

	Method New()
		m_formatter = New TTextReplacer
	End Method

	Rem
		bbdoc: Create a logger.
		returns: The new logger (itself).
	End Rem
	Method Create:TLogger(timeformat:String = "%H:%M:%S", format:String = "[{time} {type}] {msg}", logfile:String, _usestream:Int = True, _useprint:Int = False, _usedebuglog:Int = False)
		SetTimeFormat(timeformat)
		SetFormat(format)
		SetLogFile(logfile)
		UseStream(_usestream)
		UsePrint(_useprint)
		UseDebugLog(_usedebuglog)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the time format.
		returns: Nothing.
	End Rem
	Method SetTimeFormat(timeformat:String)
		m_timeformat = timeformat
	End Method
	Rem
		bbdoc: Get the time format.
		returns: The time format for logged information.
	End Rem
	Method GetTimeFormat:String()
		Return m_timeformat
	End Method

	Rem
		bbdoc: Set the format.
		returns: Nothing.
	End Rem
	Method SetFormat(format:String, beginiden:String = "{", endiden:String = "}")
		m_formatter.SetString(format)
		m_formatter.AutoReplacements(beginiden, endiden)
		m_rep_time = m_formatter.GetReplacementFromName("time")
		m_rep_type = m_formatter.GetReplacementFromName("type")
		m_rep_msg = m_formatter.GetReplacementFromName("msg")
	End Method
	Rem
		bbdoc: Get the format.
		returns: The format for logged information.
	End Rem
	Method GetFormat:String(beginiden:String = "{", endiden:String = "}")
		Return m_formatter.GetOriginal(beginiden, endiden)
	End Method

	Rem
		bbdoc: Set the log file.
		returns: Nothing.
	End Rem
	Method SetLogFile(logfile:String)
		m_logfile = logfile
	End Method
	Rem
		bbdoc: Get the log file.
		returns: The file for the logged information.
	End Rem
	Method GetLogFile:String()
		Return m_logfile
	End Method

	Rem
		bbdoc: Set the log stream.
		returns: Nothing.
	End Rem
	Method SetLogStream(logstream:TStream)
		m_logstream = logstream
	End Method
	Rem
		bbdoc: Get the log stream.
		returns: The log stream (may be Null).
	End Rem
	Method GetLogStream:TStream()
		Return m_logstream
	End Method
	Rem
		bbdoc: Open the log stream to the set log file.
		returns: True if the stream was opened, or False if it was not.
		about: This will open the log file stream.
	End Rem
	Method OpenLogStream:Int(append:Int = True)
		Local file:String

		If append = True
			file = "append::" + m_logfile
		Else
			file = m_logfile
		End If
		m_logstream = WriteStream(file)

		Return m_logstream <> Null
	End Method

	Rem
		bbdoc: Set stream usage state (True - on - or False - off).
		returns: Nothing.
	End Rem
	Method UseStream(_usestream:Int)
		m_usestream = _usestream
	End Method
	Rem
		bbdoc: Get the stream-usage state.
		returns: True (on) or False (off).
	End Rem
	Method UsingStream:Int()
		Return m_usestream
	End Method

	Rem
		bbdoc: Set Print usage state (True - on - or False - off).
		returns: Nothing.
	End Rem
	Method UsePrint(_useprint:Int)
		m_useprint = _useprint
	End Method
	Rem
		bbdoc: Get the Print-usage state.
		returns: True (on) or False (off).
	End Rem
	Method UsingPrint:Int()
		Return m_useprint
	End Method

	Rem
		bbdoc: Set DebugLog usage state (True - on - or False - off).
		returns: Nothing.
	End Rem
	Method UseDebugLog(_usedebuglog:Int)
		m_usedebuglog = _usedebuglog
	End Method
	Rem
		bbdoc: Get the DebugLog-usage state.
		returns: True (on) or False (off).
	End Rem
	Method UsingDebugLog:Int()
		Return m_usedebuglog
	End Method

'#end region (Field accessors)

'#region Formatting

	Rem
		bbdoc: Get the current time in the logger's time format.
		returns: The current time in the logger's time format, or Null if the format is Null.
	End Rem
	Method GetFormattedTime:String()
		If m_timeformat <> Null
			Return TimeInFormat(m_timeformat)
		Else
			Return Null
		End If
	End Method

	Rem
		bbdoc: Get the given message formatted in the given log type.
		returns: Nothing.
	End Rem
	Method GetFormattedMessage:String(message:String, logtype:Int = LOGTYPE_NONE)
		If m_rep_time <> Null And m_timeformat <> Null
			m_rep_time.SetReplacement(GetFormattedTime())
		End If
		If m_rep_type <> Null
			Select logtype
				Case LOGTYPE_NONE
					m_rep_type.SetReplacement("")
				Case LOGTYPE_WARNING
					m_rep_type.SetReplacement("WARNING")
				Case LOGTYPE_ERROR
					m_rep_type.SetReplacement("ERROR")
			End Select
		End If
		If m_rep_msg <> Null
			m_rep_msg.SetReplacement(message)
		End If

		Return m_formatter.DoReplacements()
	End Method

'#end region (Formatting)

'#region Logging

	Rem
		bbdoc: Log a string in the given log type.
		returns: Nothing.
	End Rem
	Method LogString(message:String, formatted:Int = True, logtype:Int = LOGTYPE_NONE)
		Local formattedmessage:String

		If formatted = True
			formattedmessage = GetFormattedMessage(message, logtype)
		Else
			formattedmessage = message
		End If

		If m_usestream = True And m_logstream <> Null
			m_logstream.WriteLine(formattedmessage)
		End If
		If m_useprint = True
			Print(formattedmessage)
		End If
		If m_usedebuglog = True
			DebugLog(formattedmessage)
		End If
	End Method

	Rem
		bbdoc: Log a generic message.
		returns: Nothing.
	End Rem
	Method LogMessage(message:String, formatted:Int = True)
		LogString(message, formatted, LOGTYPE_NONE)
	End Method

	Rem
		bbdoc: Log a warning message.
		returns: Nothing.
	End Rem
	Method LogWarning(message:String, formatted:Int = True)
		LogString(message, formatted, LOGTYPE_WARNING)
	End Method

	Rem
		bbdoc: Log an error message.
		returns: Nothing.
	End Rem
	Method LogError(message:String, formatted:Int = True)
		LogString(message, formatted, LOGTYPE_ERROR)
	End Method

'#end region (Logging)

End Type


Fixed return value check

Fixed return value check

Rem
	Copyright (c) 2012 Christiaan Kras

	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

Rem
	bbdoc: Type that represents a ZMQ socket
End Rem
Type TZMQ_Socket

	Rem
		bbdoc: Pointer to a ZMQ context
	End Rem
	Field context:Byte Ptr

	Rem
		bbdoc: Pointer to a ZMQ socket
		about: Use @Close() to free socket
	End Rem
	Field socket:Byte Ptr

	Rem
		bbdoc: Create a ZMQ Socket
		returns: TZMQ_Socket
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Create:TZMQ_Socket(context:Byte Ptr, socket_type:Int)
		If socket Then Throw "Socket already exists!"
		Self.context = context
		Self.socket = zmq_socket(Self.context, socket_type)
		If Self.socket = Null Then Throw New TZMQ_Socket_Exception
		Return Self
	End Method

	Rem
		bbdoc: Destructor
		about: Will call @Close() when collected
	End Rem
	Method Destroy()
		Self.Close()
	End Method

	Rem
		bbdoc: Bind to an address
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Bind(address:String)
		Local rc:Int = zmq_bind(Self.socket, address)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Connect to a ZMQ socket
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Connect(address:String)
		Local rc:Int = zmq_connect(Self.socket, address)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Close socket
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Close()
		Local rc:Int = zmq_close(Self.socket)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Set a socket option
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method SetSockOpt(socket_option:Int, value:String)
		Local rc:Int = zmq_setsockopt(Self.socket, socket_option, value, value.Length)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Get a socket options
		about: Not yet implemented!
	End Rem
	Method GetSockOpt(socket_option:Int)
		Throw "Not yet implemented"
	End Method

	Rem
		bbdoc: Send message to socket
		about: Throws a TZMQ_Exception on errors
		message can either be a String, which will be converted to a TZMQ_Message,
		or you can pass a TZMQ_Message straight away
	End Rem
	Method Send(message:Object, flags:Int = 0)
		If TZMQ_Message(message)
			Local rc:Int = zmq_send(Self.socket, TZMQ_Message(message).message, flags)
			If rc = -1 Then Throw New TZMQ_Socket_Exception
		Else If String(message)
			Local content:String = String(message)
			Local msg:TZMQ_Message = New TZMQ_Message.CreateFromString(content)
			Try
				Self.Send(msg, flags)
				msg.Close()
			Catch ex:TZMQ_Socket_Exception
				msg.Close()
				Throw ex
			End Try
		End If
	End Method

	Rem
		bbdoc: Receive message from socket
		returns: TZMQ_Message
	End Rem
	Method Recv:TZMQ_Message(flags:Int = 0)
		Local msg:Byte Ptr = bmx_zmq_message_t()
		zmq_msg_init(msg)
		If zmq_recv(Self.socket, msg, flags) = -1 Then Return Null
		Return New TZMQ_Message.CreateFromMessage(msg)
	End Method
End Type

Rem
	Copyright (c) 2012 Christiaan Kras

	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

Rem
	bbdoc: Type that represents a ZMQ socket
End Rem
Type TZMQ_Socket

	Rem
		bbdoc: Pointer to a ZMQ context
	End Rem
	Field context:Byte Ptr

	Rem
		bbdoc: Pointer to a ZMQ socket
		about: Use @Close() to free socket
	End Rem
	Field socket:Byte Ptr

	Rem
		bbdoc: Create a ZMQ Socket
		returns: TZMQ_Socket
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Create:TZMQ_Socket(context:Byte Ptr, socket_type:Int)
		If socket Then Throw "Socket already exists!"
		Self.context = context
		Self.socket = zmq_socket(Self.context, socket_type)
		If Self.socket = Null Then Throw New TZMQ_Socket_Exception
		Return Self
	End Method

	Rem
		bbdoc: Destructor
		about: Will call @Close() when collected
	End Rem
	Method Destroy()
		Self.Close()
	End Method

	Rem
		bbdoc: Bind to an address
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Bind(address:String)
		Local rc:Int = zmq_bind(Self.socket, address)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Connect to a ZMQ socket
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Connect(address:String)
		Local rc:Int = zmq_connect(Self.socket, address)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Close socket
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method Close()
		Local rc:Int = zmq_close(Self.socket)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Set a socket option
		about: Throws a TZMQ_Exception on errors
	End Rem
	Method SetSockOpt(socket_option:Int, value:String)
		Local rc:Int = zmq_setsockopt(Self.socket, socket_option, value, value.Length)
		If rc = -1 Then Throw New TZMQ_Socket_Exception
	End Method

	Rem
		bbdoc: Get a socket options
		about: Not yet implemented!
	End Rem
	Method GetSockOpt(socket_option:Int)
		Throw "Not yet implemented"
	End Method

	Rem
		bbdoc: Send message to socket
		about: Throws a TZMQ_Exception on errors
		message can either be a String, which will be converted to a TZMQ_Message,
		or you can pass a TZMQ_Message straight away
	End Rem
	Method Send(message:Object, flags:Int = 0)
		If TZMQ_Message(message)
			Local rc:Int = zmq_send(Self.socket, TZMQ_Message(message).message, flags)
			If rc = -1 Then Throw New TZMQ_Socket_Exception
		Else If String(message)
			Local content:String = String(message)
			Local msg:TZMQ_Message = New TZMQ_Message.CreateFromString(content)
			Try
				Self.Send(msg, flags)
				msg.Close()
			Catch ex:TZMQ_Socket_Exception
				msg.Close()
				Throw ex
			End Try
		End If
	End Method

	Rem
		bbdoc: Receive message from socket
		returns: TZMQ_Message
	End Rem
	Method Recv:TZMQ_Message(flags:Int = 0)
		Local msg:Byte Ptr = bmx_zmq_message_t()
		zmq_msg_init(msg)
		If zmq_recv(Self.socket, msg, flags) Then Return Null
		Return New TZMQ_Message.CreateFromMessage(msg)
	End Method
End Type


Forgot to remove exception.bmx as the code is included in xmlrpc.bmx

Rem
	bbdoc:
End Rem
Type TXMLRPC_Exception
	Field message:String
	Rem
		bbdoc:
	End Rem
	Method Create:TXMLRPC_Exception(message:String)
		Self.message = message
	End Method

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


Wrapped XMLRPC-EPI Server functions

Wrapped XMLRPC-EPI Server functions

'XMLRPC_REQUEST_TYPE
Const xmlrpc_request_none:Int = 0
Const xmlrpc_request_call:Int = 1
Const xmlrpc_request_response:Int = 2

'XMLRPC_VERSION
Const xmlrpc_version_none:Int = 0
Const xmlrpc_version_1_0:Int = 1
Const xmlrpc_version_simple:Int = 2
Const xmlrpc_version_danda:Int = 2
Const xmlrpc_version_soap_1_1:Int = 3

'XMLRPC_VECTOR_TYPE
Const xmlrpc_vector_none:Int = 0
Const xmlrpc_vector_array:Int = 1
Const xmlrpc_vector_mixed:Int = 2
Const xmlrpc_vector_struct:Int = 3

'XMLRPC_VALUE_TYPE
Const xmlrpc_none:Int = 0
Const xmlrpc_empty:Int = 1
Const xmlrpc_base64:Int = 2
Const xmlrpc_boolean:Int = 3
Const xmlrpc_datetime:Int = 4
Const xmlrpc_double:Int = 5
Const xmlrpc_int:Int = 6
Const xmlrpc_string:Int = 7
Const xmlrpc_vector:Int = 8

'XMLRPC_VALUE_TYPE_EASY
Const xmlrpc_type_none:Int = 0
Const xmlrpc_type_empty:Int = 1
Const xmlrpc_type_base64:Int = 2
Const xmlrpc_type_boolean:Int = 3
Const xmlrpc_type_datetime:Int = 4
Const xmlrpc_type_double:Int = 5
Const xmlrpc_type_int:Int = 6
Const xmlrpc_type_string:Int = 7
Const xmlrpc_type_array:Int = 8
Const xmlrpc_type_mixed:Int = 9
Const xmlrpc_type_struct:Int = 10

'XMLRPC_ERROR_CODE
Const xmlrpc_error_none:Int = 0
Const xmlrpc_error_parse_xml_syntax:Int = -32700
Const xmlrpc_error_parse_unknown_encoding:Int = -32701
Const xmlrpc_error_parse_bad_encoding:Int = -32702
Const xmlrpc_error_invalid_xmlrpc:Int = -32600
Const xmlrpc_error_unknown_method:Int = -32601
Const xmlrpc_error_invalid_params:Int = -32602
Const xmlrpc_error_internal_server:Int = -32603
Const xmlrpc_error_application:Int = -32500
Const xmlrpc_error_system:Int = -32400
Const xmlrpc_error_transport:Int = -32300

'XMLRPC_SERVER_VALIDATION
Const validation_none:Int = 0
Const validation_all:Int = 1
Const validation_if_defined:Int = 2

'XMLRPC_CASE_COMPARISON
Const xmlrpc_case_insensitive:Int = 0
Const xmlrpc_case_sensitive:Int = 1

'XMLRPC_CASE
Const xmlrpc_case_exact:Int = 0
Const xmlrpc_case_lower:Int = 1
Const xmlrpc_case_upper:Int = 2

Extern "C"
	Function bmxXMLRPC_RequestSetOutputOptions(request:Byte Ptr, version:Int)

	Function XMLRPC_RequestNew:Byte Ptr()
	Function XMLRPC_RequestFree(request:Byte Ptr, bFreeIO:Int)
	Function XMLRPC_RequestSetMethodName:Byte Ptr(request:Byte Ptr, methodName:Byte Ptr)
	Function XMLRPC_RequestGetMethodName:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetOutputOptions:Byte Ptr(request:Byte Ptr, output:Byte Ptr)
	Function XMLRPC_RequestGetOutputOptions:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetData:Byte Ptr(request:Byte Ptr, data:Byte Ptr)
	Function XMLRPC_RequestGetData:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetRequestType:Int(request:Byte Ptr, iType:Int)
	Function XMLRPC_RequestGetRequestType:Int(request:Byte Ptr)

	Function XMLRPC_REQUEST_ToXML:Byte Ptr(request:Byte Ptr, buf_len:Byte Ptr)
	Function XMLRPC_REQUEST_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)
	Function XMLRPC_VALUE_ToXML:Byte Ptr(val:Int, buf_len:Byte Ptr)
	Function XMLRPC_VALUE_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)

	Function XMLRPC_CreateVector:Byte Ptr(id:String, iType:Int)
	Function XMLRPC_AddValueToVector:Int(target:Byte Ptr, source:Byte Ptr)
	Function XMLRPC_VectorSize:Int(value:Byte Ptr)
	Function XMLRPC_VectorRewind:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_VectorNext:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_CreateValueBoolean:Byte Ptr(id:Byte Ptr, truth:Int)
	Function XMLRPC_CreateValueBase64:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)
	Function XMLRPC_CreateValueDateTime:Byte Ptr(id:Byte Ptr, time:Long)
	Function XMLRPC_CreateValueDateTime_ISO8601:Byte Ptr(id:Byte Ptr, s:Byte Ptr)
	Function XMLRPC_CreateValueDouble:Byte Ptr(id:Byte Ptr, f:Double)
	Function XMLRPC_CreateValueInt:Byte Ptr(id:Byte Ptr, i:Int)
	Function XMLRPC_CreateValueEmpty:Byte Ptr()
	Function XMLRPC_CreateValueString:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)
	Function XMLRPC_CleanupValue(value:Byte Ptr)

	Function XMLRPC_ServerCreate:Byte Ptr()
	Function XMLRPC_GetGlobalServer:Byte Ptr()
	Function XMLRPC_ServerDestroy(server:Byte Ptr)
	Function XMLRPC_ServerRegisterMethod:Int(server:Byte Ptr, name:Byte Ptr, cb:Byte Ptr)
	Function XMLRPC_ServerFindMethod:Byte Ptr(server:Byte Ptr, callName:Byte Ptr)
	Function XMLRPC_ServerCallMethod:Byte Ptr(server:Byte Ptr, request:Byte Ptr, userData:Byte Ptr)

'	According to the XMLRPC-EPI documentation these 2 are private and shouldn't be used
'	Function XMLRPC_RequestSetError:Byte Ptr(request:Byte Ptr, error:Int)
'	Function XMLRPC_RequestGetError:Byte Ptr(request:Byte Ptr)

	Function XMLRPC_VectorGetValueWithID_Case:Byte Ptr(vector:Byte Ptr, id:Byte Ptr, id_case:Int)

	Function XMLRPC_GetValueType:Int(v:Byte Ptr)
	'XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE v);
	Function XMLRPC_GetVectorType:Int(v:Byte Ptr)

	Function XMLRPC_GetValueString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueStringLen:Int(value:Byte Ptr)
	Function XMLRPC_GetValueInt:Int(value:Byte Ptr)
	Function XMLRPC_GetValueBoolean:Int(value:Byte Ptr)
	Function XMLRPC_GetValueDouble:Double(value:Byte Ptr)
	Function XMLRPC_GetValueBase64:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime:Long(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime_ISO8601:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueID:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_ValueIsFault:Int(value:Byte Ptr)
	Function XMLRPC_ResponseIsFault(response:Byte Ptr)
	Function XMLRPC_GetValueFaultCode:Int(value:Byte Ptr)
	Function XMLRPC_GetResponseFaultCode:Int(response:Byte Ptr)
	Function XMLRPC_GetValueFaultString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetResponseFaultString:Byte Ptr(response:Byte Ptr)
	Function XMLRPC_Free(mem:Byte Ptr)
	Function XMLRPC_GetVersionString:Byte Ptr()
Rem

XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string);

/* Get Values */
#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id))
Endrem
End Extern

Function XMLRPC_VectorGetValueWithID:Byte Ptr(vector:Byte Ptr, id:String)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Byte Ptr = XMLRPC_VectorGetValueWithID_Case(vector, strId, xmlrpc_case_sensitive)
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendString:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Local strId:Byte Ptr
	Local strS:Byte Ptr = s.ToCString()
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(strId, strS, length))
	MemFree(strId)
	MemFree(strS)
	Return val
End Function

Function XMLRPC_VectorAppendBase64:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Local strId:Byte Ptr
	Local strS:Byte Ptr = s.ToCString()
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(strId, strS, length))
	MemFree(strId)
	MemFree(strS)
	Return val
End Function

Function XMLRPC_VectorAppendDateTime:Int(vector:Byte Ptr, id:String, time:Long)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(strId, time))
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendDateTime_ISO8601:Int(vector:Byte Ptr, id:String, s:String)
	Local strId:Byte Ptr
	Local strS:Byte Ptr = s.ToCString()
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(strId, strS))
	MemFree(strId)
	MemFree(strS)
	Return val
End Function

Function XMLRPC_VectorAppendDouble:Int(vector:Byte Ptr, id:String, f:Double)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(strId, f))
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendInt:Int(vector:Byte Ptr, id:String, i:Int)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(strId, i))
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendBoolean:Int(vector:Byte Ptr, id:String, i:Int)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(strId, i))
	MemFree(strId)
	Return val
End Function
rem
/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS
 * NAME
 *   XMLRPC_REQUEST_INPUT_OPTIONS
 * NOTES
 *   Defines options for reading in xml data
 * SEE ALSO
 *   XMLRPC_VERSION
 *   XML_ELEM_INPUT_OPTIONS
 *   XMLRPC_REQUEST_From_XML ()
 * SOURCE
 */
typedef struct _xmlrpc_request_input_options {
   STRUCT_XML_ELEM_INPUT_OPTIONS  xml_elem_opts;  /* xml_element specific output options */
} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS;
/******/

/****s* VALUE/XMLRPC_ERROR
 * NAME
 *   XMLRPC_ERROR
 * NOTES
 *   For the reporting and handling of errors
 * SOURCE
 */
typedef struct _xmlrpc_error {
   XMLRPC_ERROR_CODE      code;
   STRUCT_XML_ELEM_ERROR  xml_elem_error;  /* xml_element errors (parser errors) */
} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR;
/******/
endrem

Rem
	String conversion stuff below. Shamelessly stolen from Brucey :-)
End Rem

Extern
	Function _strlen:Int(s:Byte Ptr) = "strlen"
End Extern

' Convert from Max to UTF8
Function convertISO8859toUTF8:String(text:String)
	If Not text Then
		Return ""
	End If

	Local l:Int = text.length
	If l = 0 Then
		Return ""
	End If

	Local count:Int = 0
	Local s:Byte[] = New Byte[l * 3]

	For Local i:Int = 0 Until l
		Local char:Int = text[i]

		If char < 128 Then
			s[count] = char
			count:+ 1
			Continue
		Else If char<2048
			s[count] = char/64 | 192
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		Else
			s[count] =  char/4096 | 224
			count:+ 1
			s[count] = char/64 Mod 64 | 128
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		EndIf

	Next

	Return String.fromBytes(s, count)
End Function

' Convert from UTF8 to Max
Function convertUTF8toISO8859:String(s:Byte Ptr)

	Local l:Int = _strlen(s)

	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		bc:+1
		c = s[i]
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function

Function sizedUTF8toISO8859:String(s:Byte Ptr, size:Int)

	Local l:Int = size
	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		c = s[i]
		If c = 0 Continue

		bc:+1
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function

'XMLRPC_REQUEST_TYPE
Const xmlrpc_request_none:Int = 0
Const xmlrpc_request_call:Int = 1
Const xmlrpc_request_response:Int = 2

'XMLRPC_VERSION
Const xmlrpc_version_none:Int = 0
Const xmlrpc_version_1_0:Int = 1
Const xmlrpc_version_simple:Int = 2
Const xmlrpc_version_danda:Int = 2
Const xmlrpc_version_soap_1_1:Int = 3

'XMLRPC_VECTOR_TYPE
Const xmlrpc_vector_none:Int = 0
Const xmlrpc_vector_array:Int = 1
Const xmlrpc_vector_mixed:Int = 2
Const xmlrpc_vector_struct:Int = 3

'XMLRPC_VALUE_TYPE
Const xmlrpc_none:Int = 0
Const xmlrpc_empty:Int = 1
Const xmlrpc_base64:Int = 2
Const xmlrpc_boolean:Int = 3
Const xmlrpc_datetime:Int = 4
Const xmlrpc_double:Int = 5
Const xmlrpc_int:Int = 6
Const xmlrpc_string:Int = 7
Const xmlrpc_vector:Int = 8

'XMLRPC_VALUE_TYPE_EASY
Const xmlrpc_type_none:Int = 0
Const xmlrpc_type_empty:Int = 1
Const xmlrpc_type_base64:Int = 2
Const xmlrpc_type_boolean:Int = 3
Const xmlrpc_type_datetime:Int = 4
Const xmlrpc_type_double:Int = 5
Const xmlrpc_type_int:Int = 6
Const xmlrpc_type_string:Int = 7
Const xmlrpc_type_array:Int = 8
Const xmlrpc_type_mixed:Int = 9
Const xmlrpc_type_struct:Int = 10

'XMLRPC_ERROR_CODE
Const xmlrpc_error_none:Int = 0
Const xmlrpc_error_parse_xml_syntax:Int = -32700
Const xmlrpc_error_parse_unknown_encoding:Int = -32701
Const xmlrpc_error_parse_bad_encoding:Int = -32702
Const xmlrpc_error_invalid_xmlrpc:Int = -32600
Const xmlrpc_error_unknown_method:Int = -32601
Const xmlrpc_error_invalid_params:Int = -32602
Const xmlrpc_error_internal_server:Int = -32603
Const xmlrpc_error_application:Int = -32500
Const xmlrpc_error_system:Int = -32400
Const xmlrpc_error_transport:Int = -32300

'XMLRPC_SERVER_VALIDATION
Const validation_none:Int = 0
Const validation_all:Int = 1
Const validation_if_defined:Int = 2

'XMLRPC_CASE_COMPARISON
Const xmlrpc_case_insensitive:Int = 0
Const xmlrpc_case_sensitive:Int = 1

'XMLRPC_CASE
Const xmlrpc_case_exact:Int = 0
Const xmlrpc_case_lower:Int = 1
Const xmlrpc_case_upper:Int = 2

Extern "C"
	Function bmxXMLRPC_RequestSetOutputOptions(request:Byte Ptr, version:Int)

	Function XMLRPC_RequestNew:Byte Ptr()
	Function XMLRPC_RequestFree(request:Byte Ptr, bFreeIO:Int)
	Function XMLRPC_RequestSetMethodName:Byte Ptr(request:Byte Ptr, methodName:Byte Ptr)
	Function XMLRPC_RequestGetMethodName:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetOutputOptions:Byte Ptr(request:Byte Ptr, output:Byte Ptr)
	Function XMLRPC_RequestGetOutputOptions:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetData:Byte Ptr(request:Byte Ptr, data:Byte Ptr)
	Function XMLRPC_RequestGetData:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetRequestType:Int(request:Byte Ptr, iType:Int)
	Function XMLRPC_RequestGetRequestType:Int(request:Byte Ptr)

	Function XMLRPC_REQUEST_ToXML:Byte Ptr(request:Byte Ptr, buf_len:Byte Ptr)
	Function XMLRPC_REQUEST_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)
	Function XMLRPC_VALUE_ToXML:Byte Ptr(val:Int, buf_len:Byte Ptr)
	Function XMLRPC_VALUE_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)

	Function XMLRPC_CreateVector:Byte Ptr(id:String, iType:Int)
	Function XMLRPC_AddValueToVector:Int(target:Byte Ptr, source:Byte Ptr)
	Function XMLRPC_VectorSize:Int(value:Byte Ptr)
	Function XMLRPC_VectorRewind:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_VectorNext:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_CreateValueBoolean:Byte Ptr(id:Byte Ptr, truth:Int)
	Function XMLRPC_CreateValueBase64:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)
	Function XMLRPC_CreateValueDateTime:Byte Ptr(id:Byte Ptr, time:Long)
	Function XMLRPC_CreateValueDateTime_ISO8601:Byte Ptr(id:Byte Ptr, s:Byte Ptr)
	Function XMLRPC_CreateValueDouble:Byte Ptr(id:Byte Ptr, f:Double)
	Function XMLRPC_CreateValueInt:Byte Ptr(id:Byte Ptr, i:Int)
	Function XMLRPC_CreateValueEmpty:Byte Ptr()
	Function XMLRPC_CreateValueString:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)
	Function XMLRPC_CleanupValue(value:Byte Ptr)

'	According to the XMLRPC-EPI documentation these 2 are private and shouldn't be used
'	Function XMLRPC_RequestSetError:Byte Ptr(request:Byte Ptr, error:Int)
'	Function XMLRPC_RequestGetError:Byte Ptr(request:Byte Ptr)

	Function XMLRPC_VectorGetValueWithID_Case:Byte Ptr(vector:Byte Ptr, id:Byte Ptr, id_case:Int)

	Function XMLRPC_GetValueType:Int(v:Byte Ptr)
	'XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE v);
	Function XMLRPC_GetVectorType:Int(v:Byte Ptr)

	Function XMLRPC_GetValueString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueStringLen:Int(value:Byte Ptr)
	Function XMLRPC_GetValueInt:Int(value:Byte Ptr)
	Function XMLRPC_GetValueBoolean:Int(value:Byte Ptr)
	Function XMLRPC_GetValueDouble:Double(value:Byte Ptr)
	Function XMLRPC_GetValueBase64:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime:Long(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime_ISO8601:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueID:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_ValueIsFault:Int(value:Byte Ptr)
	Function XMLRPC_ResponseIsFault(response:Byte Ptr)
	Function XMLRPC_GetValueFaultCode:Int(value:Byte Ptr)
	Function XMLRPC_GetResponseFaultCode:Int(response:Byte Ptr)
	Function XMLRPC_GetValueFaultString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetResponseFaultString:Byte Ptr(response:Byte Ptr)
	Function XMLRPC_Free(mem:Byte Ptr)
	Function XMLRPC_GetVersionString:Byte Ptr()
Rem

XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string);

/* Get Values */
#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id))
Endrem
End Extern

Function XMLRPC_VectorGetValueWithID:Byte Ptr(vector:Byte Ptr, id:String)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Byte Ptr = XMLRPC_VectorGetValueWithID_Case(vector, strId, xmlrpc_case_sensitive)
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendString:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Local strId:Byte Ptr
	Local strS:Byte Ptr = s.ToCString()
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(strId, strS, length))
	MemFree(strId)
	MemFree(strS)
	Return val
End Function

Function XMLRPC_VectorAppendBase64:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Local strId:Byte Ptr
	Local strS:Byte Ptr = s.ToCString()
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(strId, strS, length))
	MemFree(strId)
	MemFree(strS)
	Return val
End Function

Function XMLRPC_VectorAppendDateTime:Int(vector:Byte Ptr, id:String, time:Long)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(strId, time))
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendDateTime_ISO8601:Int(vector:Byte Ptr, id:String, s:String)
	Local strId:Byte Ptr
	Local strS:Byte Ptr = s.ToCString()
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(strId, strS))
	MemFree(strId)
	MemFree(strS)
	Return val
End Function

Function XMLRPC_VectorAppendDouble:Int(vector:Byte Ptr, id:String, f:Double)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(strId, f))
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendInt:Int(vector:Byte Ptr, id:String, i:Int)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(strId, i))
	MemFree(strId)
	Return val
End Function

Function XMLRPC_VectorAppendBoolean:Int(vector:Byte Ptr, id:String, i:Int)
	Local strId:Byte Ptr
	If id.Length > 0
		strId = id.ToCString()
	End If
	Local val:Int = XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(strId, i))
	MemFree(strId)
	Return val
End Function
rem
/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS
 * NAME
 *   XMLRPC_REQUEST_INPUT_OPTIONS
 * NOTES
 *   Defines options for reading in xml data
 * SEE ALSO
 *   XMLRPC_VERSION
 *   XML_ELEM_INPUT_OPTIONS
 *   XMLRPC_REQUEST_From_XML ()
 * SOURCE
 */
typedef struct _xmlrpc_request_input_options {
   STRUCT_XML_ELEM_INPUT_OPTIONS  xml_elem_opts;  /* xml_element specific output options */
} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS;
/******/

/****s* VALUE/XMLRPC_ERROR
 * NAME
 *   XMLRPC_ERROR
 * NOTES
 *   For the reporting and handling of errors
 * SOURCE
 */
typedef struct _xmlrpc_error {
   XMLRPC_ERROR_CODE      code;
   STRUCT_XML_ELEM_ERROR  xml_elem_error;  /* xml_element errors (parser errors) */
} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR;
/******/
endrem

Rem
	String conversion stuff below. Shamelessly stolen from Brucey :-)
End Rem

Extern
	Function _strlen:Int(s:Byte Ptr) = "strlen"
End Extern

' Convert from Max to UTF8
Function convertISO8859toUTF8:String(text:String)
	If Not text Then
		Return ""
	End If

	Local l:Int = text.length
	If l = 0 Then
		Return ""
	End If

	Local count:Int = 0
	Local s:Byte[] = New Byte[l * 3]

	For Local i:Int = 0 Until l
		Local char:Int = text[i]

		If char < 128 Then
			s[count] = char
			count:+ 1
			Continue
		Else If char<2048
			s[count] = char/64 | 192
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		Else
			s[count] =  char/4096 | 224
			count:+ 1
			s[count] = char/64 Mod 64 | 128
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		EndIf

	Next

	Return String.fromBytes(s, count)
End Function

' Convert from UTF8 to Max
Function convertUTF8toISO8859:String(s:Byte Ptr)

	Local l:Int = _strlen(s)

	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		bc:+1
		c = s[i]
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function

Function sizedUTF8toISO8859:String(s:Byte Ptr, size:Int)

	Local l:Int = size
	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		c = s[i]
		If c = 0 Continue

		bc:+1
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function


FINALLY fixed matrices.... I hope.

FINALLY fixed matrices.... I hope.

--HG--
extra : convert_revision : svn%3Ae934d3cd-0d57-f34a-94d0-9e8d31ae1c29/trunk%40386

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp

Type gxtkApp
	Field ginput:gxtkInput
	Field gaudio:gxtkAudio
	Field ggraphics:gxtkGraphics

	Field dead:Int=0
	Field suspended:Int=0
	Field vloading:Int=0
	Field maxloading:Int=0
	Field updateRate:Int=0
	Field nextUpdate:Float=0
	Field updatePeriod:Float=0
	Field startMillis:Float=0

	Method New()' gxtkApp()
		app=Self
		ggraphics=New gxtkGraphics
	EndMethod

	Method Setup()
		ginput=New gxtkInput
		gaudio=New gxtkAudio

		bb_input__1Set_1Input_1Device(ginput)
		bb_audio__1Set_1Audio_1Device(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			If Not updatePeriod return
			Local updates:Int = 0

			Repeat
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then Exit
				If nextUpdate>BlitzMaxMillisecs() Then Exit
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					Exit
				EndIf
			Forever
			InvokeOnRender()
			Local del:Int = nextUpdate - BlitzMaxMillisecs()
			If del < 1 Then del = 1
			Delay(del)
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps
			nextUpdate=BlitzMaxMillisecs() +updatePeriod
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1
	Field ix:Float=1,iy:Float,jx:Float,jy:Float=1,tx:Float,ty:Float
	Field sx:Float=1,sy:Float=1,rot:Float=0

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method DrawPoint:Int(x:Float, y:Float)
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		Plot nx, ny
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method TransX:Float(x:Float, y:Float)
		Return ix*x + jx*y + tx
	EndMethod

	Method TransY:Float(x:Float, y:Float)
		Return iy*x + jy*y + ty
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Self.ix = ix ; Self.iy = iy
		Self.jx = jx ; Self.jy = jy
		Self.tx = tx ; Self.ty = ty
		sx = Sqr(ix*ix+iy*iy)
		sy = Sqr(jx*jx+jy*jy)
		rot = Atan2( iy, ix )
		If ix < 0 Then
			sx = -sx
			rot :+ 180
		EndIf
		If jy < 0 Then sy = -sy
		SetTransform( rot, sx, sy )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		DrawImage(surface.image, nx, ny, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		DrawSubImageRect(surface.image, nx, ny, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		Local nx1:Float = TransX(x1,y1)
		Local ny1:Float = TransY(x1,y1)
		Local nx2:Float = TransX(x2,y2)
		Local ny2:Float = TransY(x2,y2)
		' Need to reset transform so that BlitzMax doesn't try to apply rotation
		SetTransform( 0, 1, 1 )
		BlitzMaxDrawLine(nx1, ny1, nx2, ny2)
		SetTransform( rot, sx, sy )
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		BlitzMaxDrawOval(nx, ny, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		' setting the origin to use the current rotation translation
		SetOrigin(TransX(0,0), TransY(0,0))
		BlitzMaxDrawPoly( vertices )
		SetOrigin(0, 0)
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		BlitzMaxDrawRect(nx, ny, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB

		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyDown( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method KeyHit:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyHit( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0
	Field channels:gxtkChannel[] = New gxtkChannel[33]

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local extension:String = ExtractExt( path)
		If extension = "ogg" Or extension = "wav" Then
			Local sound:TSound = LoadSound("data/"+path)
			If sound Then
				Local gs:gxtkSample = New gxtkSample
				gs.setSound(sound)
				Return gs
			EndIf
		Else
			RuntimeError "BlitzMax can only use ogg and wav file formats"
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp

Type gxtkApp
	Field ginput:gxtkInput
	Field gaudio:gxtkAudio
	Field ggraphics:gxtkGraphics

	Field dead:Int=0
	Field suspended:Int=0
	Field vloading:Int=0
	Field maxloading:Int=0
	Field updateRate:Int=0
	Field nextUpdate:Float=0
	Field updatePeriod:Float=0
	Field startMillis:Float=0

	Method New()' gxtkApp()
		app=Self
		ggraphics=New gxtkGraphics
	EndMethod

	Method Setup()
		ginput=New gxtkInput
		gaudio=New gxtkAudio

		bb_input__1Set_1Input_1Device(ginput)
		bb_audio__1Set_1Audio_1Device(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			If Not updatePeriod return
			Local updates:Int = 0

			Repeat
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then Exit
				If nextUpdate>BlitzMaxMillisecs() Then Exit
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					Exit
				EndIf
			Forever
			InvokeOnRender()
			Local del:Int = nextUpdate - BlitzMaxMillisecs()
			If del < 1 Then del = 1
			Delay(del)
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps
			nextUpdate=BlitzMaxMillisecs() +updatePeriod
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1
	Field ix:Float=1,iy:Float,jx:Float,jy:Float=1,tx:Float,ty:Float
	Field sx:Float=1,sy:Float=1,rot:Float=0

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method DrawPoint:Int(x:Float, y:Float)
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		Plot nx, ny
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method TransX:Float(x:Float, y:Float)
		Return ix*x + jx*y + tx
	EndMethod

	Method TransY:Float(x:Float, y:Float)
		Return iy*x + jy*y + ty
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Self.ix = ix ; Self.iy = iy
		Self.jx = jx ; Self.jy = jy
		Self.tx = tx ; Self.ty = ty
		sx = Sqr( (ix*ix) + (jx*jx) )
		sy = Sqr( (iy*iy) + (jy*jy) )
		rot = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		DrawImage(surface.image, nx, ny, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		DrawSubImageRect(surface.image, nx, ny, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		Local nx1:Float = TransX(x1,y1)
		Local ny1:Float = TransY(x1,y1)
		Local nx2:Float = TransX(x2,y2)
		Local ny2:Float = TransY(x2,y2)
		' Need to reset transform so that BlitzMax doesn't try to apply rotation
		SetTransform( 0, 1, 1 )
		BlitzMaxDrawLine(nx1, ny1, nx2, ny2)
		SetTransform( rot, sx, sy )
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		BlitzMaxDrawOval(nx, ny, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		' setting the origin to use the current rotation translation
		SetOrigin(TransX(0,0), TransY(0,0))
		BlitzMaxDrawPoly( vertices )
		SetOrigin(0, 0)
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		Local nx:Float = TransX(x,y)
		Local ny:Float = TransY(x,y)
		BlitzMaxDrawRect(nx, ny, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB

		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyDown( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method KeyHit:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyHit( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0
	Field channels:gxtkChannel[] = New gxtkChannel[33]

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local extension:String = ExtractExt( path)
		If extension = "ogg" Or extension = "wav" Then
			Local sound:TSound = LoadSound("data/"+path)
			If sound Then
				Local gs:gxtkSample = New gxtkSample
				gs.setSound(sound)
				Return gs
			EndIf
		Else
			RuntimeError "BlitzMax can only use ogg and wav file formats"
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******


Added missing (forgotten) import for brl.Threads.  Closes #1.

Rem
	LuGI - Copyright (c) 2009 Noel R. Cower

	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.
EndRem

SuperStrict

Module LuGI.Generator

ModuleInfo "Name: LuGI Code Generator"
ModuleInfo "Description: Default code generator for Lua glue code used by LuGI"
ModuleInfo "Author: Noel Cower"
ModuleInfo "License: MIT"
ModuleInfo "URL: http://github.com/nilium/lugi.mod/"

Import brl.LinkedList
Import brl.Map
Import brl.Random
Import brl.Reflection
Import brl.Stream
Import Brl.System
?Threaded
Import brl.Threads
?

Private

Include "utility.bmx"

' The actually meat of the code here...
Include "metadata.bmx"
Include "method.bmx"
Include "field.bmx"
Include "type.bmx"

Public

Function GenerateGlueCode(url:Object = Null)
	Assert url Else "GenerateGlueCode: No output URL provided"

	' Clear existing types (in case the code is generating multiple wrappers)
	ClearExposedTypes()

	Local output:TStream = OpenStream(url, False, True)
	Assert output Else "GenerateGlueCode: Unable to create writeable output stream"

	SeedRndWithDateTime

	BuildTypeInfo()

	output.WriteString("Private~n~n")

	For Local t:LExposedType = EachIn LExposedType.EnumTypes()
		Local outs$ = t.Implementation()
		If outs Then
			output.WriteLine("'---- "+t.name)
			output.WriteString(outs)
		EndIf
	Next

	Local pretag$ = GenerateUniqueTag(16)
	Local posttag$ = GenerateUniqueTag(16)

	' Generate the initialization function
	output.WriteLine("Function "+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"pre_"+pretag+"(lua_vm:Byte Ptr, register_field(off%, typ%, name$, class@ Ptr), register_method(luafn:Int(state:Byte Ptr), name$, class@ Ptr))")

	For Local t:LExposedType = EachIn LExposedType.EnumTypes()
		output.WriteString(t.PreInitBlock()+"~n")
	Next

	output.WriteLine("End Function")
	output.WriteString("New LuGIInitFunction.PreInit("+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"pre_"+pretag+")~n~n~n")

	output.WriteLine("Function "+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"post_"+posttag+"(lua_vm:Byte Ptr, constructor:Int(state:Byte Ptr))")

	' Any initialization code following the exposure (e.g., setting global constructors and such)
	For Local t:LExposedType = EachIn LExposedType.EnumTypes()
		output.WriteString(t.PostInitBlock())
	Next

	output.WriteLine("End Function")
	output.WriteString("New LuGIInitFunction.PostInit("+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"post_"+posttag+")~n~n~n")

	output.WriteString("~nPublic~n~n")

	output.Flush
	output.Close
End Function

Rem
	LuGI - Copyright (c) 2009 Noel R. Cower

	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.
EndRem

SuperStrict

Module LuGI.Generator

ModuleInfo "Name: LuGI Code Generator"
ModuleInfo "Description: Default code generator for Lua glue code used by LuGI"
ModuleInfo "Author: Noel Cower"
ModuleInfo "License: MIT"
ModuleInfo "URL: http://github.com/nilium/lugi.mod/"

Import brl.LinkedList
Import brl.Map
Import brl.Random
Import brl.Reflection
Import brl.Stream
Import Brl.System

Private

Include "utility.bmx"

' The actually meat of the code here...
Include "metadata.bmx"
Include "method.bmx"
Include "field.bmx"
Include "type.bmx"

Public

Function GenerateGlueCode(url:Object = Null)
	Assert url Else "GenerateGlueCode: No output URL provided"

	' Clear existing types (in case the code is generating multiple wrappers)
	ClearExposedTypes()

	Local output:TStream = OpenStream(url, False, True)
	Assert output Else "GenerateGlueCode: Unable to create writeable output stream"

	SeedRndWithDateTime

	BuildTypeInfo()

	output.WriteString("Private~n~n")

	For Local t:LExposedType = EachIn LExposedType.EnumTypes()
		Local outs$ = t.Implementation()
		If outs Then
			output.WriteLine("'---- "+t.name)
			output.WriteString(outs)
		EndIf
	Next

	Local pretag$ = GenerateUniqueTag(16)
	Local posttag$ = GenerateUniqueTag(16)

	' Generate the initialization function
	output.WriteLine("Function "+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"pre_"+pretag+"(lua_vm:Byte Ptr, register_field(off%, typ%, name$, class@ Ptr), register_method(luafn:Int(state:Byte Ptr), name$, class@ Ptr))")

	For Local t:LExposedType = EachIn LExposedType.EnumTypes()
		output.WriteString(t.PreInitBlock()+"~n")
	Next

	output.WriteLine("End Function")
	output.WriteString("New LuGIInitFunction.PreInit("+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"pre_"+pretag+")~n~n~n")

	output.WriteLine("Function "+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"post_"+posttag+"(lua_vm:Byte Ptr, constructor:Int(state:Byte Ptr))")

	' Any initialization code following the exposure (e.g., setting global constructors and such)
	For Local t:LExposedType = EachIn LExposedType.EnumTypes()
		output.WriteString(t.PostInitBlock())
	Next

	output.WriteLine("End Function")
	output.WriteString("New LuGIInitFunction.PostInit("+LUGI_GLOBAL_PREFIX+LUGI_INIT_PREFIX+"post_"+posttag+")~n~n~n")

	output.WriteString("~nPublic~n~n")

	output.Flush
	output.Close
End Function


more informative

more informative

just a list of names is less helpful when you've forgotten what's
printing it

' This FOLD delegate is intended for debugging others

'enumerate keys (compiler debug tool: make sure all are handled and none are wrong)
'this prints all keys not handled by the given fold delegate (given nothing, it just prints all keys)
Type Debug_KeyListFold
	Field cache:TMap, handled:TMap
	Function Make:Debug_KeyListFold(over:Object = Null)
		Local d:Debug_KeyListFold = New Self
		d.cache = CreateMap() ; If over Then d.handled = Node.BuildCache(over)
		Return d
	End Function

	Function Run(funs:FunDef[], over:Object)
		Local dbg:Debug_KeyListFold = Self.Make(over)
		For Local f:FunDef = EachIn funs
			Node.Fold(f.n, dbg)
		Next
		If Not dbg.cache.IsEmpty() Then Print "Debug nodes:"
		For Local s:String = EachIn dbg.cache.Keys()
			Print "  " + s
		Next
	End Function

	Method any:Node(n:Node)
		If handled = Null Or handled.ValueForKey(n.key) = Null Then cache.Insert(n.key, "@")
		Return n
	End Method
End Type

' This FOLD delegate is intended for debugging others

'enumerate keys (compiler debug tool: make sure all are handled and none are wrong)
'this prints all keys not handled by the given fold delegate (given nothing, it just prints all keys)
Type Debug_KeyListFold
	Field cache:TMap, handled:TMap
	Function Make:Debug_KeyListFold(over:Object = Null)
		Local d:Debug_KeyListFold = New Self
		d.cache = CreateMap() ; If over Then d.handled = Node.BuildCache(over)
		Return d
	End Function

	Function Run(funs:FunDef[], over:Object)
		Local dbg:Debug_KeyListFold = Self.Make(over)
		For Local f:FunDef = EachIn funs
			Node.Fold(f.n, dbg)
		Next
		For Local s:String = EachIn dbg.cache.Keys()
			Print s
		Next
	End Function

	Method any:Node(n:Node)
		If handled = Null Or handled.ValueForKey(n.key) = Null Then cache.Insert(n.key, "@")
		Return n
	End Method
End Type