Added support for native flags for the Mojo backend.

Strict

Public

' Only bother if we have a game-target implementation:
#If BRL_GAMETARGET_IMPLEMENTED

' Preprocessor related:
#If (TARGET = "glfw" Or TARGET = "sexy") And (Not GLFW_VERSION Or GLFW_VERSION = 2)
	#SCREEN_GLFW2_TARGET = True
#End

#If SCREEN_GLFW2_TARGET
	#DESKTOP_WINDOW_TRANSITION = True
#End

#If SCREEN_GLFW2_TARGET Or TARGET = "xna"
	#SCREEN_SUPPORTED = True
#End

#If SCREEN_GLFW2_TARGET Or TARGET = "xna"
	#APPTITLE_IMPLEMENTED = True
#End

#If SCREEN_GLFW2_TARGET And HOST = "winnt"
	#CONFINE_CURSOR_IMPLEMENTED = True
	#TOGGLE_CURSOR_AVAILABLE = True
#End

' Imports:

' Preprocessor:
Import preprocessor.defaults
Import preprocessor.flags

' General:
Import util
Import vector
Import ioelement

' BRL:
Import brl.stream

' Mojo (Public):
' Nothing so far.

' Mojo (Private):
Private

Import mojo.app

Public

' Preprocessor related:
#If SCREEN_GLFW2_TARGET And HOST = "winnt"
	#TOGGLE_CURSOR_IMPLEMENTED = True
#End

' Internal:
Import external
Import fallbacks

' Global variable(s):
'#If FLAG_DESKTOP
' If we're compiling for a desktop target, setup a "Last window screen-mode".
Global CurrentWindowMode:DesktopScreenMode = Null
'#End

' Check if there was already a window created:
#If TARGET <> "xna" And ((DEFAULT_DESKTOP_WINDOW_W > 0 And DEFAULT_DESKTOP_WINDOW_H > 0) Or (SCREEN_GLFW2_TARGET And GLFW_WINDOW_WIDTH > 0 And GLFW_WINDOW_WIDTH > 0))
	Global WindowCreated:Bool = True
#Else
	Global WindowCreated:Bool = False
#End

' Classes:
Class DesktopScreenMode Implements SerializableElement
	' Constant variable(s):

	' General:

	' Flags (Only useful when using the standard Mojo backend):
	Const FLAG_FULLSCREEN:Int = 1
	Const FLAG_RESIZABLE:Int = 2
	Const FLAG_DECORATED:Int = 4
	Const FLAG_FLOATING:Int = 8
	Const FLAG_DEPTH_BUFFERED:Int = 16
	Const FLAG_SINGLE_BUFFERED:Int = 32
	Const FLAG_SECOND_MONITOR:Int = 64

	' Color related:
	Const COLOR_ARRAY_LENGTH:Int = 6

	Const COLOR_RPOS:Int			= 0
	Const COLOR_GPOS:Int			= 1
	Const COLOR_BPOS:Int			= 2
	Const COLOR_APOS:Int			= 3
	Const COLOR_DEPTHPOS:Int		= 4
	Const COLOR_STENCILPOS:Int		= 5

	' I/O related:
	Const ZERO:Int = 0

	Const IOVersion:Int = 2

	' Global variable(s):
	' Nothing so far.

	' Constant variable(s):
	#If DEFAULT_DESKTOP_FULLSCREEN
		Const Default_Fullscreen:Bool = True
	#Else
		Const Default_Fullscreen:Bool = False
	#End

	Const Default_Transition:Bool = False

	Const Default_AASamples:Int = 0
	Const Default_Framerate:Int = 60

	' Constructor(s) (Public):
	Method New()
		ConstructDSM(0, 0, Default_Fullscreen, Default_AASamples, Default_Framerate, Default_Transition, Null)
	End

	Method New(Width:Int, Height:Int, Fullscreen:Bool=Default_Fullscreen, AASamples:Int=Default_AASamples, Framerate:Int=Default_Framerate, Transition:Bool=Default_Transition, Position:Vector2D<IntObject>=Null, Color:Int[]=[])
		ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, Position, Color)
	End

	Method New(Width:Int, Height:Int, Fullscreen:Bool, AASamples:Int, Framerate:Int, Transition:Bool, X:IntObject, Y:IntObject, Color:Int[]=[])
		ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, New Vector2D<IntObject>(X, Y), Color)
	End

	Method ConstructDSM:Void(Width:Int, Height:Int, Fullscreen:Bool=Default_Fullscreen, AASamples:Int=Default_AASamples, Framerate:Int=Default_Framerate, Transition:Bool=Default_Transition, Position:Vector2D<IntObject>=Null, Color:Int[]=[])
		Self.Width = Width
		Self.Height = Height

		Self.Fullscreen = Fullscreen
		Self.AASamples = AASamples
		Self.Framerate = Framerate
		Self.Transition = Transition

		Self.Position = Position

		' If we don't have a position vector to work with, create one:
		If (Self.Position = Null) Then
			Self.Position = New Vector2D<IntObject>(Null, Null)
		Endif

		Self._Color = Color

		Return
	End

	' Constructor(s) (Private):
	Private

	' Nothing so far.

	Public

	' Methods:
	Method Clone:DesktopScreenMode()
		Return CloneByRef(New DesktopScreenMode())
	End

	Method CloneByRef:DesktopScreenMode(A:DesktopScreenMode)
		A.ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, Vector2D<IntObject>(Position.Clone()), _Color)

		Return A
	End

	Method ColorAvailable:Bool()
		Return (_Color.Length() > 0)
	End

	Method ToDisplayMode:DisplayMode()
		Return New DisplayMode(Width, Height)
	End

	' I/O related:
	Method Load:Bool(S:Stream)
		' Check for errors:
		If (S = Null) Then Return False
		If (S.Eof()) Then Return False

		' Local variable(s):

		Local IOVersion:Int = DesktopScreenMode.IOVersion

		' Position related:
		Local X:IntObject, Y:IntObject

		' Screen related:
		Local Width:Int, Height:Int
		Local AASamples:Int, Framerate:Int
		Local Color:Int[]

		' Flags:
		Local PositionAvailable:Bool
		Local Fullscreen:Bool
		Local Transition:Bool
		Local ColorAvail:Bool

		IOVersion = S.ReadInt()

		' The display size:
		Width = S.ReadInt()
		Height = S.ReadInt()

		' Meta-data:
		Fullscreen = IOElement.ReadBool(S)
		Transition = IOElement.ReadBool(S)

		' Anti-aliasing samples.
		AASamples = S.ReadByte()

		' Target frame-rate.
		Framerate = S.ReadInt()

		#If SCREEN_GLFW2_TARGET
			If (Framerate < 0) Then
				Framerate = Default_Framerate
			Endif
		#End

		' Read the position (If there is one):
		PositionAvailable = IOElement.ReadBool(S)

		If (PositionAvailable) Then
			If (IOElement.ReadBool(S)) Then
				X = S.ReadInt()
			Endif

			If (IOElement.ReadBool(S)) Then
				Y = S.ReadInt()
			Endif
		Endif

		' Check if we have a color-setting:
		ColorAvail = IOElement.ReadBool(S)

		If (ColorAvail) Then
			' Read the color from the stream.
			Color = New Int[COLOR_ARRAY_LENGTH]

			For Local I:Int = 0 Until Color.Length()
				Color[I] = S.ReadByte()
			Next
		Endif

		' Construct the screen-mode.
		ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, New Vector2D<IntObject>(X, Y), Color)

		' Return the default response.
		Return True
	End

	Method Save:Bool(S:Stream)
		' Check for errors:
		If (S = Null) Then Return False
		'If (S.Eof()) Then Return False

		' This issue has been fixed, there's no need for this check:
		#Rem
		If (Color.Length() > COLOR_ARRAY_LENGTH) Then
			Return False
		Endif
		#End

		' Local variable(s):
		Local PositionAvailable:Bool = (Self.Position <> Null And (Self.Position.X <> Null Or Self.Position.Y <> Null))

		' Write the I/O version.
		S.WriteInt(IOVersion)

		' Resolution:
		S.WriteInt(Self.Width)
		S.WriteInt(Self.Height)

		' Flags:
		IOElement.WriteBool(S, Self.Fullscreen)
		IOElement.WriteBool(S, Self.Transition)

		' General:

		' Anti-aliasing samples.
		S.WriteByte(AASamples)

		' The target frame-rate.
		S.WriteInt(Framerate)

		' Position:
		IOElement.WriteBool(S, PositionAvailable)

		' The extra check here (In the 'If' statement) isn't needed, but makes this a bit more efficient.
		If (PositionAvailable) Then
			IOElement.WriteBool(S, (Self.Position.X <> Null))

			If (Self.Position.X <> Null) Then
				S.WriteInt(Self.Position.X)
			Endif

			IOElement.WriteBool(S, (Self.Position.Y <> Null))

			If (Self.Position.Y <> Null) Then
				S.WriteInt(Self.Position.Y)
			Endif
		Endif

		' Color settings:
		IOElement.WriteBool(S, ColorAvailable())

		If (ColorAvailable()) Then
			' Write the color-array to the stream.
			For Local I:Int = 0 Until Min(Color.Length(), COLOR_ARRAY_LENGTH)
				S.WriteByte(Color[I])
			Next

			' Pad-out the rest of the bytes of the array.
			For Local Padding:= 0 Until Max(COLOR_ARRAY_LENGTH-Color.Length(), 0)
				S.WriteByte(ZERO)
			Next
		Endif

		' Return the default response.
		Return True
	End

	' Properties (Public):

	' If the input is too small, a padded clone will be made.
	Method Color:Void(Input:Int[], BlankUndefined:Bool=False) Property
		' Local variable(s):
		Local InputLength:= Input.Length()

		If (Not ColorAvailable()) Then
			If (InputLength < COLOR_ARRAY_LENGTH) Then
				Input = Input.Resize(COLOR_ARRAY_LENGTH)
			Endif

			Self._Color = Input
		Else
			For Local I:Int = 0 Until Min(COLOR_ARRAY_LENGTH, InputLength)
				Self._Color[I] = Input[I]
			Next

			If (BlankUndefined) Then
				For Local I:Int = 0 Until Max(COLOR_ARRAY_LENGTH-InputLength, 0)
					Self._Color[InputLength+I] = 0
				Next
			Endif
		Endif

		Return
	End

	Method Color:Int[]() Property
		If (Not ColorAvailable()) Then
			Self._Color = New Int[COLOR_ARRAY_LENGTH]
		Endif

		Return Self._Color
	End

	Method X:IntObject() Property
		If (Self.Position <> Null) Then
			Return Position.X
		Endif

		Return 0
	End

	Method Y:IntObject() Property
		If (Self.Position <> Null) Then
			Return Position.Y
		Endif

		Return 0
	End

	Method W:Int() Property
		Return Width
	End

	Method W:Void(Input:Int) Property
		Width = Input

		Return
	End

	Method H:Int() Property
		Return Height
	End

	Method H:Void(Input:Int)
		Height = Input

		Return
	End

	' Color related:
	Method R:Int() Property
		Return Color[COLOR_RPOS]
	End

	Method R:Void(Input:Int) Property
		Color[COLOR_RPOS] = Input

		Return
	End

	Method G:Int() Property
		Return Color[COLOR_GPOS]
	End

	Method G:Void(Input:Int) Property
		Color[COLOR_GPOS] = Input

		Return
	End

	Method B:Int() Property
		Return Color[COLOR_BPOS]
	End

	Method B:Void(Input:Int) Property
		Color[COLOR_BPOS] = Input

		Return
	End

	Method A:Int() Property
		Return Color[COLOR_APOS]
	End

	Method A:Void(Input:Int) Property
		Color[COLOR_APOS] = Input

		Return
	End

	Method Depth:Int() Property
		Return Color[COLOR_DEPTHPOS]
	End

	Method Depth:Void(Input:Int) Property
		Color[COLOR_DEPTHPOS] = Input

		Return
	End

	Method Stencil:Int() Property
		Return Color[COLOR_STENCILPOS]
	End

	Method Stencil:Void(Input:Int) Property
		Color[COLOR_STENCILPOS] = Input

		Return
	End

	' Properties (Private):
	Private

	' Nothing so far.

	Public

	' Fields (Public):

	' These are pretty self-explanatory:
	Field Fullscreen:Bool
	Field Transition:Bool

	' The default placement of a window.
	Field Position:Vector2D<IntObject>

	' The actual resolution:
	Field Width:Int
	Field Height:Int

	' Other:
	Field AASamples:Int
	Field Framerate:Int

	Field Flags:Int

	' Fields (Private):
	Private

	Field _Color:Int[]

	' Flags:
	' Nothing so far.

	Public
End

' Functions:
Function ResizeWindow:Bool(Resolution:DesktopScreenMode)
	Return InitWindow(Resolution, True)
End

Function InitWindow:Bool(Resolution:DesktopScreenMode, Force:Bool=False)
	' Check if the window has already been created.
	If (WindowCreated And Not Force) Then Return False

	' Local variable(s):
	Local Response:Bool = False

	' Change the resolution if possible:
	#If FLAG_DESKTOP
		#If SCREEN_GLFW2_TARGET
			' Local variable(s):
			Local GlfwInstance:= GlfwGame.GetGlfwGame()
		#End

		#If DESKTOP_WINDOW_TRANSITION
			If (Resolution <> Null) Then
				' Check for a transition.
				If (Resolution.Transition) Then
					' Local variable(s):
					Local IsFullscreen:Bool = False

					If (WindowCreated) Then
						If (CurrentWindowMode = Null) Then
							#If (SCREEN_GLFW2_TARGET And GLFW_WINDOW_FULLSCREEN) Or (DEFAULT_DESKTOP_FULLSCREEN)
								IsFullscreen = True
							#Else
								IsFullscreen = False
							#End
						Else
							IsFullscreen = CurrentWindowMode.Fullscreen
						Endif

						If (Not IsFullscreen) Then
							MoveWindow_ToDestination()

							If (Resolution.Fullscreen) Then
								Delay(250)
							Endif
						Endif
					Endif
				Endif
			Endif
		#End

		#If SCREEN_GLFW2_TARGET
			' Color related:
			Local RBits:Int = 0
			Local GBits:Int = 0
			Local BBits:Int = 0
			Local ABits:Int = 0

			Local Depth:Int = 0
			Local Stencil:Int = 0

			' Screen related:
			Local Width:Int = -1
			Local Height:Int = -1

			' Flags:
			Local Fullscreen:Bool = True
			Local Transition:Bool = False

			' Other:
			Local Framerate:Int = DesktopScreenMode.Default_Framerate
			Local AASamples:Int = DesktopScreenMode.Default_AASamples

			If (Resolution <> Null) Then
				Width = Resolution.Width
				Height = Resolution.Height
				Fullscreen = Resolution.Fullscreen
				AASamples = Resolution.AASamples
			Endif

			If (Width < 0 Or Height < 0) Then
				Local DesktopMode:= GlfwInstance.GetGlfwDesktopMode()

				Width = DesktopMode.Width
				Height = DesktopMode.Height
				Fullscreen = True

				RBits = DesktopMode.RedBits
				GBits = DesktopMode.GreenBits
				BBits = DesktopMode.BlueBits
			ElseIf (Resolution <> Null) Then
				If (Resolution.ColorAvailable()) Then
					RBits = Resolution.R
					GBits = Resolution.G
					BBits = Resolution.B
					ABits = Resolution.A

					Depth = Resolution.Depth
					Stencil = Resolution.Stencil
				Else
					RBits = GlfwInstance.GetGlfwDesktopMode().RedBits
					GBits = GlfwInstance.GetGlfwDesktopMode().GreenBits
					BBits = GlfwInstance.GetGlfwDesktopMode().BlueBits

					'ABits = 0
					'Depth = 0
					'Stencil = 0
				Endif
			Endif

			GlfwOpenWindowHint(SCREEN_GLFW_FSAA_SAMPLES, AASamples)

			GlfwInstance.SetGlfwWindow(Width, Height, RBits, GBits, BBits, ABits, Depth, Stencil, Fullscreen)
		#Else
			Local Width:Int = -1
			Local Height:Int = -1
			Local Fullscreen:Bool = True
			Local AASamples:Int = DesktopScreenMode.Default_AASamples

			If (Resolution <> Null) Then
				Width = Resolution.Width
				Height = Resolution.Height
				Fullscreen = Resolution.Fullscreen
				AASamples = Resolution.AASamples
			Endif

			If (Width < 0 Or Height < 0) Then
				Local Desktop:= DesktopMode()

				Width = Desktop.Width
				Height = Desktop.Height

				Fullscreen = True
			Endif

			#If TARGET = "xna"
				XNA_ToggleAA((AASamples > 0))
			#End

			If (Resolution.Flags <> 0) Then
				SetDeviceWindow(Width, Height, Resolution.Flags)
			Else
				SetDeviceWindow(Width, Height, Int(Fullscreen)|DesktopScreenMode.FLAG_RESIZABLE|DesktopScreenMode.FLAG_DECORATED) ' FLAG_FULLSCREEN
			Endif
		#End

		' Set the response to true.
		Response = True

		#If DESKTOP_WINDOW_TRANSITION
			If (Resolution <> Null) Then
				If (Resolution.Transition) Then
					MoveWindow_SetResolution(Resolution.W, Resolution.H)

					If (Not Resolution.Fullscreen) Then
						MoveWindow_ToCenter()

						' Local variable(s):
						Local Response:Bool = False
						Local RX:Int = NOVAR
						Local RY:Int = NOVAR

						If (Resolution.X <> Null) Then
							RX = Resolution.X
							Response = True
						Endif

						If (Resolution.Y <> Null) Then
							RY = Resolution.Y
							Response = True
						Endif

						If (Response) Then
							MoveWindow_ToDestination(NOVAR, NOVAR, RX, RY, True)
						Endif
					Endif
				Else
					If (Resolution.X <> Null And Resolution.Y <> Null) Then
						MoveWindow(Resolution.X, Resolution.Y)
					Endif
				Endif
			Endif
		#End

		WindowCreated = True

		#Rem
		'#If TARGET = "xna"
			Local XnaInstance:= XnaGame.GetXnaGame()

			' Keeping this blank for now.

			'WindowCreated = True
		#End

		' If the change was successful, set the last window screen-mode to a clone of the input.
		If (Response) Then
			If (Resolution <> Null) Then
				CurrentWindowMode = Resolution.Clone()
			Else
				' Local variable(s):
				Local Color:Int[]

				' If we have a color setup, use it:
				#If SCREEN_GLFW2_TARGET
					If (RBits <> 0 Or GBits <> 0 Or BBits <> 0 Or ABits <> 0 Or Depth <> 0 Or Stencil <> 0) Then
						Color = [RBits, GBits, BBits, ABits, Depth, Stencil]
					Endif
				#Else
					Local Fullscreen:Bool = True
					Local Transition:Bool = False
					Local Framerate:Int = DesktopScreenMode.Default_Framerate
				#End

				CurrentWindowMode = New DesktopScreenMode(Width, Height, Fullscreen, AASamples, Framerate, Transition, Null, Color)
				'New(Width:Int, Height:Int, Fullscreen:Bool, AASamples:Int, Framerate:Int, Transition:Bool, X:IntObject, Y:IntObject, Color:Int[]=[])
			Endif
		Endif

		#If CONFINE_CURSOR_IMPLEMENTED
			'BeginConfineCursor()
			'EndConfineCursor()
		#End
	#Else
		' Nothing so far.
	#End

	' Platform/target independent code:
	'SetCursor(CursorMode) <- I may look into this later.

	Return Response
End
#End

Strict

Public

' Only bother if we have a game-target implementation:
#If BRL_GAMETARGET_IMPLEMENTED

' Preprocessor related:
#If (TARGET = "glfw" Or TARGET = "sexy") And (Not GLFW_VERSION Or GLFW_VERSION = 2)
	#SCREEN_GLFW2_TARGET = True
#End

#If SCREEN_GLFW2_TARGET
	#DESKTOP_WINDOW_TRANSITION = True
#End

#If SCREEN_GLFW2_TARGET Or TARGET = "xna"
	#SCREEN_SUPPORTED = True
#End

#If SCREEN_GLFW2_TARGET Or TARGET = "xna"
	#APPTITLE_IMPLEMENTED = True
#End

#If SCREEN_GLFW2_TARGET And HOST = "winnt"
	#CONFINE_CURSOR_IMPLEMENTED = True
	#TOGGLE_CURSOR_AVAILABLE = True
#End

' Imports:

' Preprocessor:
Import preprocessor.defaults
Import preprocessor.flags

' General:
Import util
Import vector
Import ioelement

' BRL:
Import brl.stream

' Mojo (Public):
' Nothing so far.

' Mojo (Private):
Private

Import mojo.app

Public

' Preprocessor related:
#If SCREEN_GLFW2_TARGET And HOST = "winnt"
	#TOGGLE_CURSOR_IMPLEMENTED = True
#End

' Internal:
Import external
Import fallbacks

' Global variable(s):
'#If FLAG_DESKTOP
' If we're compiling for a desktop target, setup a "Last window screen-mode".
Global CurrentWindowMode:DesktopScreenMode = Null
'#End

' Check if there was already a window created:
#If TARGET <> "xna" And ((DEFAULT_DESKTOP_WINDOW_W > 0 And DEFAULT_DESKTOP_WINDOW_H > 0) Or (SCREEN_GLFW2_TARGET And GLFW_WINDOW_WIDTH > 0 And GLFW_WINDOW_WIDTH > 0))
	Global WindowCreated:Bool = True
#Else
	Global WindowCreated:Bool = False
#End

' Classes:
Class DesktopScreenMode Implements SerializableElement
	' Constant variable(s):

	' General:

	' Color related:
	Const COLOR_ARRAY_LENGTH:Int = 6

	Const COLOR_RPOS:Int			= 0
	Const COLOR_GPOS:Int			= 1
	Const COLOR_BPOS:Int			= 2
	Const COLOR_APOS:Int			= 3
	Const COLOR_DEPTHPOS:Int		= 4
	Const COLOR_STENCILPOS:Int		= 5

	' I/O related:
	Const ZERO:Int = 0

	Const IOVersion:Int = 2

	' Global variable(s):
	' Nothing so far.

	' Constant variable(s):
	#If DEFAULT_DESKTOP_FULLSCREEN
		Const Default_Fullscreen:Bool = True
	#Else
		Const Default_Fullscreen:Bool = False
	#End

	Const Default_Transition:Bool = False

	Const Default_AASamples:Int = 0
	Const Default_Framerate:Int = 60

	' Constructor(s) (Public):
	Method New()
		ConstructDSM(0, 0, Default_Fullscreen, Default_AASamples, Default_Framerate, Default_Transition, Null)
	End

	Method New(Width:Int, Height:Int, Fullscreen:Bool=Default_Fullscreen, AASamples:Int=Default_AASamples, Framerate:Int=Default_Framerate, Transition:Bool=Default_Transition, Position:Vector2D<IntObject>=Null, Color:Int[]=[])
		ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, Position, Color)
	End

	Method New(Width:Int, Height:Int, Fullscreen:Bool, AASamples:Int, Framerate:Int, Transition:Bool, X:IntObject, Y:IntObject, Color:Int[]=[])
		ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, New Vector2D<IntObject>(X, Y), Color)
	End

	Method ConstructDSM:Void(Width:Int, Height:Int, Fullscreen:Bool=Default_Fullscreen, AASamples:Int=Default_AASamples, Framerate:Int=Default_Framerate, Transition:Bool=Default_Transition, Position:Vector2D<IntObject>=Null, Color:Int[]=[])
		Self.Width = Width
		Self.Height = Height

		Self.Fullscreen = Fullscreen
		Self.AASamples = AASamples
		Self.Framerate = Framerate
		Self.Transition = Transition

		Self.Position = Position

		' If we don't have a position vector to work with, create one:
		If (Self.Position = Null) Then
			Self.Position = New Vector2D<IntObject>(Null, Null)
		Endif

		Self._Color = Color

		Return
	End

	' Constructor(s) (Private):
	Private

	' Nothing so far.

	Public

	' Methods:
	Method Clone:DesktopScreenMode()
		Return CloneByRef(New DesktopScreenMode())
	End

	Method CloneByRef:DesktopScreenMode(A:DesktopScreenMode)
		A.ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, Vector2D<IntObject>(Position.Clone()), _Color)

		Return A
	End

	Method ColorAvailable:Bool()
		Return (_Color.Length() > 0)
	End

	Method ToDisplayMode:DisplayMode()
		Return New DisplayMode(Width, Height)
	End

	' I/O related:
	Method Load:Bool(S:Stream)
		' Check for errors:
		If (S = Null) Then Return False
		If (S.Eof()) Then Return False

		' Local variable(s):

		Local IOVersion:Int = DesktopScreenMode.IOVersion

		' Position related:
		Local X:IntObject, Y:IntObject

		' Screen related:
		Local Width:Int, Height:Int
		Local AASamples:Int, Framerate:Int
		Local Color:Int[]

		' Flags:
		Local PositionAvailable:Bool
		Local Fullscreen:Bool
		Local Transition:Bool
		Local ColorAvail:Bool

		IOVersion = S.ReadInt()

		' The display size:
		Width = S.ReadInt()
		Height = S.ReadInt()

		' Meta-data:
		Fullscreen = IOElement.ReadBool(S)
		Transition = IOElement.ReadBool(S)

		' Anti-aliasing samples.
		AASamples = S.ReadByte()

		' Target frame-rate.
		Framerate = S.ReadInt()

		#If SCREEN_GLFW2_TARGET
			If (Framerate < 0) Then
				Framerate = Default_Framerate
			Endif
		#End

		' Read the position (If there is one):
		PositionAvailable = IOElement.ReadBool(S)

		If (PositionAvailable) Then
			If (IOElement.ReadBool(S)) Then
				X = S.ReadInt()
			Endif

			If (IOElement.ReadBool(S)) Then
				Y = S.ReadInt()
			Endif
		Endif

		' Check if we have a color-setting:
		ColorAvail = IOElement.ReadBool(S)

		If (ColorAvail) Then
			' Read the color from the stream.
			Color = New Int[COLOR_ARRAY_LENGTH]

			For Local I:Int = 0 Until Color.Length()
				Color[I] = S.ReadByte()
			Next
		Endif

		' Construct the screen-mode.
		ConstructDSM(Width, Height, Fullscreen, AASamples, Framerate, Transition, New Vector2D<IntObject>(X, Y), Color)

		' Return the default response.
		Return True
	End

	Method Save:Bool(S:Stream)
		' Check for errors:
		If (S = Null) Then Return False
		'If (S.Eof()) Then Return False

		' This issue has been fixed, there's no need for this check:
		#Rem
		If (Color.Length() > COLOR_ARRAY_LENGTH) Then
			Return False
		Endif
		#End

		' Local variable(s):
		Local PositionAvailable:Bool = (Self.Position <> Null And (Self.Position.X <> Null Or Self.Position.Y <> Null))

		' Write the I/O version.
		S.WriteInt(IOVersion)

		' Resolution:
		S.WriteInt(Self.Width)
		S.WriteInt(Self.Height)

		' Flags:
		IOElement.WriteBool(S, Self.Fullscreen)
		IOElement.WriteBool(S, Self.Transition)

		' General:

		' Anti-aliasing samples.
		S.WriteByte(AASamples)

		' The target frame-rate.
		S.WriteInt(Framerate)

		' Position:
		IOElement.WriteBool(S, PositionAvailable)

		' The extra check here (In the 'If' statement) isn't needed, but makes this a bit more efficient.
		If (PositionAvailable) Then
			IOElement.WriteBool(S, (Self.Position.X <> Null))

			If (Self.Position.X <> Null) Then
				S.WriteInt(Self.Position.X)
			Endif

			IOElement.WriteBool(S, (Self.Position.Y <> Null))

			If (Self.Position.Y <> Null) Then
				S.WriteInt(Self.Position.Y)
			Endif
		Endif

		' Color settings:
		IOElement.WriteBool(S, ColorAvailable())

		If (ColorAvailable()) Then
			' Write the color-array to the stream.
			For Local I:Int = 0 Until Min(Color.Length(), COLOR_ARRAY_LENGTH)
				S.WriteByte(Color[I])
			Next

			' Pad-out the rest of the bytes of the array.
			For Local Padding:= 0 Until Max(COLOR_ARRAY_LENGTH-Color.Length(), 0)
				S.WriteByte(ZERO)
			Next
		Endif

		' Return the default response.
		Return True
	End

	' Properties (Public):

	' If the input is too small, a padded clone will be made.
	Method Color:Void(Input:Int[], BlankUndefined:Bool=False) Property
		' Local variable(s):
		Local InputLength:= Input.Length()

		If (Not ColorAvailable()) Then
			If (InputLength < COLOR_ARRAY_LENGTH) Then
				Input = Input.Resize(COLOR_ARRAY_LENGTH)
			Endif

			Self._Color = Input
		Else
			For Local I:Int = 0 Until Min(COLOR_ARRAY_LENGTH, InputLength)
				Self._Color[I] = Input[I]
			Next

			If (BlankUndefined) Then
				For Local I:Int = 0 Until Max(COLOR_ARRAY_LENGTH-InputLength, 0)
					Self._Color[InputLength+I] = 0
				Next
			Endif
		Endif

		Return
	End

	Method Color:Int[]() Property
		If (Not ColorAvailable()) Then
			Self._Color = New Int[COLOR_ARRAY_LENGTH]
		Endif

		Return Self._Color
	End

	Method X:IntObject() Property
		If (Self.Position <> Null) Then
			Return Position.X
		Endif

		Return 0
	End

	Method Y:IntObject() Property
		If (Self.Position <> Null) Then
			Return Position.Y
		Endif

		Return 0
	End

	Method W:Int() Property
		Return Width
	End

	Method W:Void(Input:Int) Property
		Width = Input

		Return
	End

	Method H:Int() Property
		Return Height
	End

	Method H:Void(Input:Int)
		Height = Input

		Return
	End

	' Color related:
	Method R:Int() Property
		Return Color[COLOR_RPOS]
	End

	Method R:Void(Input:Int) Property
		Color[COLOR_RPOS] = Input

		Return
	End

	Method G:Int() Property
		Return Color[COLOR_GPOS]
	End

	Method G:Void(Input:Int) Property
		Color[COLOR_GPOS] = Input

		Return
	End

	Method B:Int() Property
		Return Color[COLOR_BPOS]
	End

	Method B:Void(Input:Int) Property
		Color[COLOR_BPOS] = Input

		Return
	End

	Method A:Int() Property
		Return Color[COLOR_APOS]
	End

	Method A:Void(Input:Int) Property
		Color[COLOR_APOS] = Input

		Return
	End

	Method Depth:Int() Property
		Return Color[COLOR_DEPTHPOS]
	End

	Method Depth:Void(Input:Int) Property
		Color[COLOR_DEPTHPOS] = Input

		Return
	End

	Method Stencil:Int() Property
		Return Color[COLOR_STENCILPOS]
	End

	Method Stencil:Void(Input:Int) Property
		Color[COLOR_STENCILPOS] = Input

		Return
	End

	' Properties (Private):
	Private

	' Nothing so far.

	Public

	' Fields (Public):

	' These are pretty self-explanatory:
	Field Fullscreen:Bool
	Field Transition:Bool

	' The default placement of a window.
	Field Position:Vector2D<IntObject>

	' The actual resolution:
	Field Width:Int
	Field Height:Int

	' Other:
	Field AASamples:Int
	Field Framerate:Int

	' Fields (Private):
	Private

	Field _Color:Int[]

	' Flags:
	' Nothing so far.

	Public
End

' Functions:
Function ResizeWindow:Bool(Resolution:DesktopScreenMode)
	Return InitWindow(Resolution, True)
End

Function InitWindow:Bool(Resolution:DesktopScreenMode, Force:Bool=False)
	' Check if the window has already been created.
	If (WindowCreated And Not Force) Then Return False

	' Local variable(s):
	Local Response:Bool = False

	' Change the resolution if possible:
	#If FLAG_DESKTOP
		#If SCREEN_GLFW2_TARGET
			' Local variable(s):
			Local GlfwInstance:= GlfwGame.GetGlfwGame()
		#End

		#If DESKTOP_WINDOW_TRANSITION
			If (Resolution <> Null) Then
				' Check for a transition.
				If (Resolution.Transition) Then
					' Local variable(s):
					Local IsFullscreen:Bool = False

					If (WindowCreated) Then
						If (CurrentWindowMode = Null) Then
							#If (SCREEN_GLFW2_TARGET And GLFW_WINDOW_FULLSCREEN) Or (DEFAULT_DESKTOP_FULLSCREEN)
								IsFullscreen = True
							#Else
								IsFullscreen = False
							#End
						Else
							IsFullscreen = CurrentWindowMode.Fullscreen
						Endif

						If (Not IsFullscreen) Then
							MoveWindow_ToDestination()

							If (Resolution.Fullscreen) Then
								Delay(250)
							Endif
						Endif
					Endif
				Endif
			Endif
		#End

		#If SCREEN_GLFW2_TARGET
			' Color related:
			Local RBits:Int = 0
			Local GBits:Int = 0
			Local BBits:Int = 0
			Local ABits:Int = 0

			Local Depth:Int = 0
			Local Stencil:Int = 0

			' Screen related:
			Local Width:Int = -1
			Local Height:Int = -1

			' Flags:
			Local Fullscreen:Bool = True
			Local Transition:Bool = False

			' Other:
			Local Framerate:Int = DesktopScreenMode.Default_Framerate
			Local AASamples:Int = DesktopScreenMode.Default_AASamples

			If (Resolution <> Null) Then
				Width = Resolution.Width
				Height = Resolution.Height
				Fullscreen = Resolution.Fullscreen
				AASamples = Resolution.AASamples
			Endif

			If (Width < 0 Or Height < 0) Then
				Local DesktopMode:= GlfwInstance.GetGlfwDesktopMode()

				Width = DesktopMode.Width
				Height = DesktopMode.Height
				Fullscreen = True

				RBits = DesktopMode.RedBits
				GBits = DesktopMode.GreenBits
				BBits = DesktopMode.BlueBits
			ElseIf (Resolution <> Null) Then
				If (Resolution.ColorAvailable()) Then
					RBits = Resolution.R
					GBits = Resolution.G
					BBits = Resolution.B
					ABits = Resolution.A

					Depth = Resolution.Depth
					Stencil = Resolution.Stencil
				Else
					RBits = GlfwInstance.GetGlfwDesktopMode().RedBits
					GBits = GlfwInstance.GetGlfwDesktopMode().GreenBits
					BBits = GlfwInstance.GetGlfwDesktopMode().BlueBits

					'ABits = 0
					'Depth = 0
					'Stencil = 0
				Endif
			Endif

			GlfwOpenWindowHint(SCREEN_GLFW_FSAA_SAMPLES, AASamples)

			GlfwInstance.SetGlfwWindow(Width, Height, RBits, GBits, BBits, ABits, Depth, Stencil, Fullscreen)
		#Else
			Local Width:Int = -1
			Local Height:Int = -1
			Local Fullscreen:Bool = True
			Local AASamples:Int = DesktopScreenMode.Default_AASamples

			If (Resolution <> Null) Then
				Width = Resolution.Width
				Height = Resolution.Height
				Fullscreen = Resolution.Fullscreen
				AASamples = Resolution.AASamples
			Endif

			If (Width < 0 Or Height < 0) Then
				Local Desktop:= DesktopMode()

				Width = Desktop.Width
				Height = Desktop.Height

				Fullscreen = True
			Endif

			#If TARGET = "xna"
				XNA_ToggleAA((AASamples > 0))
			#End

			SetDeviceWindow(Width, Height, Int(Fullscreen))
		#End

		' Set the response to true.
		Response = True

		#If DESKTOP_WINDOW_TRANSITION
			If (Resolution <> Null) Then
				If (Resolution.Transition) Then
					MoveWindow_SetResolution(Resolution.W, Resolution.H)

					If (Not Resolution.Fullscreen) Then
						MoveWindow_ToCenter()

						' Local variable(s):
						Local Response:Bool = False
						Local RX:Int = NOVAR
						Local RY:Int = NOVAR

						If (Resolution.X <> Null) Then
							RX = Resolution.X
							Response = True
						Endif

						If (Resolution.Y <> Null) Then
							RY = Resolution.Y
							Response = True
						Endif

						If (Response) Then
							MoveWindow_ToDestination(NOVAR, NOVAR, RX, RY, True)
						Endif
					Endif
				Else
					If (Resolution.X <> Null And Resolution.Y <> Null) Then
						MoveWindow(Resolution.X, Resolution.Y)
					Endif
				Endif
			Endif
		#End

		WindowCreated = True

		#Rem
		'#If TARGET = "xna"
			Local XnaInstance:= XnaGame.GetXnaGame()

			' Keeping this blank for now.

			'WindowCreated = True
		#End

		' If the change was successful, set the last window screen-mode to a clone of the input.
		If (Response) Then
			If (Resolution <> Null) Then
				CurrentWindowMode = Resolution.Clone()
			Else
				' Local variable(s):
				Local Color:Int[]

				' If we have a color setup, use it:
				#If SCREEN_GLFW2_TARGET
					If (RBits <> 0 Or GBits <> 0 Or BBits <> 0 Or ABits <> 0 Or Depth <> 0 Or Stencil <> 0) Then
						Color = [RBits, GBits, BBits, ABits, Depth, Stencil]
					Endif
				#Else
					Local Fullscreen:Bool = True
					Local Transition:Bool = False
					Local Framerate:Int = DesktopScreenMode.Default_Framerate
				#End

				CurrentWindowMode = New DesktopScreenMode(Width, Height, Fullscreen, AASamples, Framerate, Transition, Null, Color)
				'New(Width:Int, Height:Int, Fullscreen:Bool, AASamples:Int, Framerate:Int, Transition:Bool, X:IntObject, Y:IntObject, Color:Int[]=[])
			Endif
		Endif

		#If CONFINE_CURSOR_IMPLEMENTED
			'BeginConfineCursor()
			'EndConfineCursor()
		#End
	#Else
		' Nothing so far.
	#End

	' Platform/target independent code:
	'SetCursor(CursorMode) <- I may look into this later.

	Return Response
End
#End


add statements to verify reader works correctly

let reduce = fn (arr, initial, f) {
    let iter = fn (arr, result) {
        if (len(arr) == 0) {
            result
        } else {
            iter(tail(arr) , f(result , first (arr)))
        }
    };
    iter(arr, initial);
};

let sum = fn(arr) {
    reduce(arr, 0, fn(initial, el) {
        initial + el
    });
};

let array = [1, 2, 3, 4, 5];
let sum = sum(array);
puts("Sum: " + sum);
puts(push(array, 6));

let addNums = fn(x, y) {
    return x + y;
}

puts(addNums(1, 3 % 2));

2 + 2;
return 3 + 2;


Changed IComparableWrapper.Compare to remove some unnecessary dynamic casts.  Seems to slightly improve performance with sorting. There are some more places that IComparable checks can be cached, but they don't seem to improve performance at all.

#Rem
Copyright (c) 2011 Steve Revill and Shane Woolcock
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
Header: Monkey Containers Framework
The Containers framework supersedes the old Collections framework and allows cross-compatibility
with the official Monkey data structures (Stack, List, and Set).
#End

Strict
Public

' Need to alias some of the official classes since Enumerator exists in both the monkey.stack and monkey.list modules
Alias MapValueEnumerator = monkey.map.ValueEnumerator
Alias MapKeyEnumerator = monkey.map.KeyEnumerator
Alias StackEnumerator = monkey.stack.Enumerator
Alias ListEnumerator = monkey.list.Enumerator

' Importing containers will give you all the container classes
Import diddystack
Import diddyset
Import diddylist
Import diddypool
Import exception

#Rem
Summary: Indicates that an object of this class should be able to compare itself with another instance, primarily for sorting.
IComparable can't be generic at this point, because it's likely the developer would want to declare a cyclic declaration.
Cyclic declarations are currently unsupported by Monkey.
#End
Interface IComparable
#Rem
Summary: Compares this instance of the class with another instance.
Return 0 if the objects are equal, a negative value if the passed object is "less than" this object,
or a positive value if the passed object is "greater than" this object.
#End
	Method CompareTo:Int(other:Object)
End

#Rem
Summary: Indicates that this class is able to compare two instances of the generic type, T.
The comparison contract is identical to that of IComparable, as if lhs.CompareTo(rhs)
#End
Interface IComparator<T>
#Rem
Summary: Compares two instances of the generic type, T.
Return 0 if the objects are equal, a negative value if rhs is "less than" lhs,
or a positive value if rhs is "greater than" lhs.
#End
	Method Compare:Int(lhs:T, rhs:T)
End

#Rem
Summary: Provides the HasNext() and NextObject() methods that the EachIn operator expects.
#End
Interface IEnumerator<T>
#Rem
Summary: Called once every EachIn loop to check if there are more objects available.
Returns False if the next call to NextObject() will fail.
#End
	Method HasNext:Bool()

#Rem
Summary: Called once every EachIn loop to retrieve the next available object.
#End
	Method NextObject:T()
End

#Rem
Summary: Indicates that the class can be used in an EachIn loop.
When the Monkey compiler reaches an EachIn statement such as:
[code]
For Local val := EachIn lst
	' your code
Next
[/code]
It will internally convert it to:
[code]
Local oe:=lst.ObjectEnumerator()
While oe.HasNext()
	Local val:=oe.NextObject()
	' your code
End
[/code]
#End
Interface IEnumerable<T>
#Rem
Summary: Returns an instance of IEnumerator that the EachIn operator can loop with.
#End
	Method ObjectEnumerator:IEnumerator<T>()
End

#Rem
Summary: A class that implements IPredicate should act as a pseudo-lambda for predicate operations on the Container classes.
#End
Interface IPredicate<T>
	Method Evaluate:Bool(arg:T)
End

#Rem
Summary: If a class implements IPoolable, Reset() will be called when it is freed within a DiddyPool or GlobalPool.
#End
Interface IPoolable
	Method Reset:Void()
End

#Rem
Summary: A class that implements IContainer<T> must be able to interact with all of Stack, List, and Set.
#End
Interface IContainer<T>
#Rem
Summary: Getter for the current sorting comparator.
Allows sorting without implementing IComparable.
Unsupported in Set.
#End
	Method Comparator:IComparator<T>() Property

#Rem
Summary: Setter for the current sorting comparator.
Allows sorting without implementing IComparable.
Unsupported in Set.
#End
	Method Comparator:Void(comparator:IComparator<T>) Property

#Rem
Summary: Adds the entire contents of the passed Stack to the container.
AddAll should throw an IllegalArgumentException if src is Null.
#End
	Method AddAll:Void(src:Stack<T>)

#Rem
Summary: Adds the entire contents of the passed List to the container.
AddAll should throw an IllegalArgumentException if src is Null.
#End
	Method AddAll:Void(src:List<T>)

#Rem
Summary: Adds the entire contents of the passed Set to the container.
AddAll should throw an IllegalArgumentException if src is Null.
#End
	Method AddAll:Void(src:Set<T>)

#Rem
Summary: Adds the entire contents of another container to this container.
AddContainer should throw an IllegalArgumentException if src is Null.
#End
	Method AddContainer:Void(src:IContainer<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed Stack.
RemoveAll should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveAll:Void(src:Stack<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed List.
RemoveAll should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveAll:Void(src:List<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed Set.
RemoveAll should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveAll:Void(src:Set<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed container.
RemoveContainer should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveContainer:Void(src:IContainer<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed Stack.
RetainAll should throw an IllegalArgumentException if src is Null.
#End
	Method RetainAll:Void(src:Stack<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed List.
RetainAll should throw an IllegalArgumentException if src is Null.
#End
	Method RetainAll:Void(src:List<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed Set.
RetainAll should throw an IllegalArgumentException if src is Null.
#End
	Method RetainAll:Void(src:Set<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed container.
RetainContainer should throw an IllegalArgumentException if src is Null.
#End
	Method RetainContainer:Void(src:IContainer<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed Stack.
ContainsAll should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsAll:Bool(src:Stack<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed List.
ContainsAll should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsAll:Bool(src:List<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed Set.
ContainsAll should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsAll:Bool(src:Set<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed container.
ContainsContainer should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsContainer:Bool(src:IContainer<T>)

#Rem
Summary: Adds the passed item to the container.
No error should be thrown if the item already exists since Lists and Stacks allow duplicates.
Sets should do nothing if the item already exists.
A passed value of Null is implementation-dependent.
#End
	Method AddItem:Void(val:T)

#Rem
Summary: Removes the passed item from the container, if it exists.
No error should be thrown if the item does not exist.
A passed value of Null is implementation-dependent.
#End
	Method RemoveItem:Void(val:T)

#Rem
Summary: Adds the passed item to the container at the requested index.
No error should be thrown if the item already exists since Lists and Stacks allow duplicates.
Sets are unordered and should throw an UnsupportedOperationException.
A passed value of Null is implementation-dependent.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index <= Count()
#End
	Method InsertItem:Void(index:Int, val:T)

#Rem
Summary: Deletes the item from the container at the requested index.
No error should be thrown if the item already exists since Lists and Stacks allow duplicates.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index < Count()
#End
	Method DeleteItem:T(index:Int)

#Rem
Summary: Removes all items from the container.
#End
	Method ClearAll:Void()

#Rem
Summary: Returns True if the container contains the passed value.
A passed value of Null is implementation-dependent.
#End
	Method ContainsItem:Bool(val:T)

#Rem
Summary: Gets the item at the given index.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index < Count()
#End
	Method GetItem:T(index:Int)

#Rem
Summary: Sets the item at a given index to be the passed value.
A passed value of Null is implementation-dependent.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index < Count()
#End
	Method SetItem:Void(index:Int, value:T)

#Rem
Summary: Finds the first reference to the passed item and returns the index.
A value of -1 should be returned if the item is not found.
A passed value of Null is implementation-dependent.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method FindItem:Int(value:T)

#Rem
Summary: Sorts the items based on the current Comparator or T's implementation of IComparable.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method SortItems:Void(ascending:Bool = True) ' unsupported in Set

#Rem
Summary: Reverses the order of elements in the container.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method Reverse:Void()

#Rem
Summary: Randomises the order of elements in the container.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method Shuffle:Void()

#Rem
Summary: Returns a custom IEnumerable that optionally supports a predicate.
#End
	Method Items:IEnumerable<T>(pred:IPredicate<T>=Null)

#Rem
Summary: Swaps the values of two given indices.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if either index is outside the range 0 <= index < Count()
#End
	Method SwapItems:Void(index1:Int, index2:Int)

#Rem
Summary: Reduces the number of items in the container to be no more than the passed size.
If there are less or equal elements in the container than the passed size, nothing happens.
If there are more elements in the container than the passed size, elements should be removed from the end
until Count() = the requested size.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the requested size < 0.
#End
	Method Truncate:Void(size:Int)

#Rem
Summary: Returns the number of items in the container.
#End
	Method Count:Int()

#Rem
Summary: Returns a new array containing all the items in the container.
The array length will be equal to Count().
Sets are unordered, therefore the order of the elements in the array will be undefined.
#End
	Method ToArray:T[]()

#Rem
Summary: Fills the passed array with the items in the container, if there is space.
If the array length is < Count(), an IllegalArgumentException should be thrown.
The first Count() items will contain the elements of the container, and any elements after that should be set to Null.
The method returns the number of elements actually filled.
Sets are unordered, therefore the order of the elements in the array will be undefined.
#End
	Method FillArray:Int(arr:T[]) ' undefined order in Set

#Rem
Summary: Compares two instances of the generic type, T.
Return 0 if the objects are equal, a negative value if rhs is "less than" lhs,
or a positive value if rhs is "greater than" lhs.
A Null value should always be considered "less than" a non-Null value.
Sets are unordered and cannot be sorted, so they should throw an UnsupportedOperationException.
#End
	Method Compare:Int(lhs:T, rhs:T)

#Rem
Summary: Performs an equality test of lhs and rhs.
Returns True if they are logically equal, otherwise False.
#End
	Method Equals:Bool(lhs:T, rhs:T)

#Rem
Summary: Returns True if there are no elements in the container, otherwise False.
#End
	Method IsEmpty:Bool()

#Rem
Summary: Returns a read-only wrapper on this container.
#End
	Method ReadOnly:ReadOnlyContainer<T>()
End

#Rem
Summary: IComparableWrapper is essentially a hack to get around Monkey's inability to cast primitives to objects.
#End
Class IComparableWrapper
#Rem
Summary: Returns True if the passed object implements the IComparable interface.
#End
	Function IsComparable:Bool(src:Object)
		Return IComparable(src) <> Null
	End

#Rem
Summary: Returns False, because an Int can never implement an interface.
#End
	Function IsComparable:Bool(src:Int)
		Return False
	End

#Rem
Summary: Returns False, because a Float can never implement an interface.
#End
	Function IsComparable:Bool(src:Float)
		Return False
	End

#Rem
Summary: Returns False, because a String can never implement an interface.
#End
	Function IsComparable:Bool(src:String)
		Return False
	End

#Rem
Summary: Performs a comparison on the passed objects.
If both objects are Null, returns 0.
If either object does not implement IComparable, returns 0.
Otherwise, IComparable.CompareTo() is called.
#End
	Function Compare:Int(lhs:Object, rhs:Object)
		' we check both lhs and rhs because we always want to compare if at least one value is non-null
		Local cmp:IComparable = IComparable(lhs)
		If cmp Then
			' normal comparison if lhs is not null
			Return cmp.CompareTo(rhs)
		End
		cmp = IComparable(rhs)
		If cmp Then
			' reverse comparison if lhs is null but rhs is not
			Return -cmp.CompareTo(lhs)
		End
		Return 0
	End

#Rem
Summary: Returns 0, because an Int can never implement an interface.
#End
	Function Compare:Int(lhs:Int, rhs:Int)
		Return 0
	End

#Rem
Summary: Returns 0, because a Float can never implement an interface.
#End
	Function Compare:Int(lhs:Float, rhs:Float)
		Return 0
	End

#Rem
Summary: Returns 0, because a String can never implement an interface.
#End
	Function Compare:Int(lhs:String, rhs:String)
		Return 0
	End
End

#Rem
Summary: Abstract class to provide predicate support to subclassed enumerators.
#End
Class PredicateEnumerator<T> Implements IEnumerator<T> Abstract
Private
	Global NIL:T

	Field pred:IPredicate<T>
	Field atEnd:Bool = False
	Field peeked:Bool = False
	Field nextVal:T = NIL
	Field nextValSet:Bool = False

Public
#Rem
Summary: Constructor with an optional predicate.
#End
	Method New(pred:IPredicate<T>=Null)
		Self.pred = pred
	End

#Rem
Summary: Implements IEnumerator<T>.HasNext().
If no predicate is assigned, it immediately delegates to CallHasNext()
If there is a predicate, it delegates to CallHasNext() and CallNextObject() as required.
#End
	Method HasNext:Bool()
		If Not pred Then Return CallHasNext()
		If atEnd Then Return False
		If nextValSet Then Return True
		peeked = True
		While CallHasNext()
			Local n:T = CallNextObject()
			If pred.Evaluate(n) Then
				nextVal = n
				nextValSet = True
				Return True
			End
		End
		atEnd = True
		Return False
	End

#Rem
Summary: Implements IEnumerator<T>.NextObject().
If no predicate is assigned, it immediately delegates to CallNextObject()
If there is a predicate, it delegates to HasNext() and CallNextObject() as required.
#End
	Method NextObject:T()
		If Not pred Then Return CallNextObject()
		If Not nextValSet And Not HasNext() Then Return NIL
		Local result:T = nextVal
		nextVal = NIL
		nextValSet = False
		peeked = False
		Return result
	End

#Rem
Summary: Abstract method that should delegate to the child enumerator's HasNext() method.
#End
	Method CallHasNext:Bool() Abstract

#Rem
Summary: Abstract method that should delegate to the child enumerator's NextObject() method.
#End
	Method CallNextObject:T() Abstract
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap MapValueEnumerator<String,T> as an IEnumerator<T>.
#End
Class WrappedMapValueEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:MapValueEnumerator<String,T>

Public
#Rem
Summary: Constructor to wrap a MapValueEnumerator<String,T> with an optional IPredicate<T>.
#End
	Method New(en:MapValueEnumerator<String,T>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to MapValueEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to MapValueEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps Map and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedMapValueEnumerable<K,V> Implements IEnumerable<V>
Private
	Field m:Map<K,V>
	Field pred:IPredicate<V>

Public
#Rem
Summary: Constructor to wrap a Map<K,V> enumerator as an IEnumerable<V> with an optional IPredicate<V>.
#End
	Method New(m:Map<K,V>, pred:IPredicate<V>=Null)
		Self.m = m
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedMapValueEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<V>()
		Return New WrappedMapValueEnumerator<V>(m.ObjectEnumerator(), pred)
	End
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap StackEnumerator<T> as an IEnumerator<T>.
#End
Class WrappedStackEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:StackEnumerator<T>

Public
#Rem
Summary: Constructor to wrap a StackEnumerator<T> with an optional IPredicate<T>.
#End
	Method New(en:StackEnumerator<T>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to StackEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to StackEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps Stack and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedStackEnumerable<T> Implements IEnumerable<T>
Private
	Field s:Stack<T>
	Field pred:IPredicate<T>

Public
#Rem
Summary: Constructor to wrap a Stack<T> enumerator as an IEnumerable<T> with an optional IPredicate<T>.
#End
	Method New(s:Stack<T>, pred:IPredicate<T>=Null)
		Self.s = s
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedStackEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<T>()
		Return New WrappedStackEnumerator<T>(s.ObjectEnumerator(), pred)
	End
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap ListEnumerator<T> as an IEnumerator<T>.
#End
Class WrappedListEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:ListEnumerator<T>

Public
#Rem
Summary: Constructor to wrap a ListEnumerator<T> with an optional IPredicate<T>.
#End
	Method New(en:ListEnumerator<T>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to ListEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to ListEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps List and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedListEnumerable<T> Implements IEnumerable<T>
Private
	Field l:List<T>
	Field pred:IPredicate<T>

Public
#Rem
Summary: Constructor to wrap a List<T> enumerator as an IEnumerable<T> with an optional IPredicate<T>.
#End
	Method New(l:List<T>, pred:IPredicate<T>=Null)
		Self.l = l
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedListEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<T>()
		Return New WrappedListEnumerator<T>(l.ObjectEnumerator(), pred)
	End
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap MapKeyEnumerator<T,Object> as an IEnumerator<T>.
#End
Class WrappedSetEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:MapKeyEnumerator<T,Object>

Public
#Rem
Summary: Constructor to wrap a MapKeyEnumerator<T,Object> with an optional IPredicate<T>.
#End
	Method New(en:MapKeyEnumerator<T,Object>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to MapKeyEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to MapKeyEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps Set and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedSetEnumerable<T> Implements IEnumerable<T>
Private
	Field s:Set<T>
	Field pred:IPredicate<T>

Public
#Rem
Summary: Constructor to wrap a Set<T> enumerator as an IEnumerable<T> with an optional IPredicate<T>.
#End
	Method New(s:Set<T>, pred:IPredicate<T>)
		Self.s = s
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedSetEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<T>()
		Return New WrappedSetEnumerator<T>(s.ObjectEnumerator(), pred)
	End
End

Class ReadOnlyContainer<T> Implements IContainer<T> Final
Private
	Global NIL:T

	Field source:IContainer<T>

	Method ThrowReadOnly:Void()
		Throw New UnsupportedOperationException("The container is read only.")
	End

Public
	Method New(source:IContainer<T>)
		If Not source Then Throw New IllegalArgumentException("ReadOnlyContainer.New: Source container must not be null")
		Self.source = source
	End

	Method Comparator:IComparator<T>() Property
		Return source.Comparator
	end

	Method Comparator:Void(comparator:IComparator<T>) Property
		ThrowReadOnly()
	End

	Method AddAll:Void(src:Stack<T>)
		ThrowReadOnly()
	End

	Method AddAll:Void(src:List<T>)
		ThrowReadOnly()
	End

	Method AddAll:Void(src:Set<T>)
		ThrowReadOnly()
	End

	Method AddContainer:Void(src:IContainer<T>)
		ThrowReadOnly()
	End

	Method RemoveAll:Void(src:Stack<T>)
		ThrowReadOnly()
	End

	Method RemoveAll:Void(src:List<T>)
		ThrowReadOnly()
	End

	Method RemoveAll:Void(src:Set<T>)
		ThrowReadOnly()
	End

	Method RemoveContainer:Void(src:IContainer<T>)
		ThrowReadOnly()
	End

	Method RetainAll:Void(src:Stack<T>)
		ThrowReadOnly()
	End

	Method RetainAll:Void(src:List<T>)
		ThrowReadOnly()
	End

	Method RetainAll:Void(src:Set<T>)
		ThrowReadOnly()
	End

	Method RetainContainer:Void(src:IContainer<T>)
		ThrowReadOnly()
	End

	Method ContainsAll:Bool(src:Stack<T>)
		Return source.ContainsAll(src)
	End

	Method ContainsAll:Bool(src:List<T>)
		Return source.ContainsAll(src)
	End

	Method ContainsAll:Bool(src:Set<T>)
		Return source.ContainsAll(src)
	End

	Method ContainsContainer:Bool(src:IContainer<T>)
		Return source.ContainsContainer(src)
	End

	Method AddItem:Void(val:T)
		ThrowReadOnly()
	End

	Method RemoveItem:Void(val:T)
		ThrowReadOnly()
	End

	Method InsertItem:Void(index:Int, val:T)
		ThrowReadOnly()
	End

	Method DeleteItem:T(index:Int)
		ThrowReadOnly()
		Return NIL
	End

	Method ClearAll:Void()
		ThrowReadOnly()
	End

	Method ContainsItem:Bool(val:T)
		Return source.ContainsItem(val)
	End

	Method GetItem:T(index:Int)
		Return source.GetItem(index)
	End

	Method SetItem:Void(index:Int, value:T)
		ThrowReadOnly()
	End

	Method FindItem:Int(value:T)
		Return source.FindItem(value)
	End

	Method SortItems:Void(ascending:Bool = True)
		ThrowReadOnly()
	End

	Method Reverse:Void()
		ThrowReadOnly()
	End

	Method Shuffle:Void()
		ThrowReadOnly()
	End

	Method Items:IEnumerable<T>(pred:IPredicate<T>=Null)
		Return source.Items(pred)
	End

	Method SwapItems:Void(index1:Int, index2:Int)
		ThrowReadOnly()
	End

	Method Truncate:Void(size:Int)
		ThrowReadOnly()
	End

	Method Count:Int()
		Return source.Count()
	End

	Method ToArray:T[]()
		Return source.ToArray()
	End

	Method FillArray:Int(arr:T[])
		Return source.FillArray(arr)
	End

	Method Compare:Int(lhs:T, rhs:T)
		Return source.Compare(lhs, rhs)
	End

	Method Equals:Bool(lhs:T, rhs:T)
		Return source.Equals(lhs, rhs)
	End

	Method IsEmpty:Bool()
		Return source.IsEmpty()
	End

	Method ReadOnly:ReadOnlyContainer<T>()
		Return New ReadOnlyContainer<T>(Self)
	End
End

#Rem
Copyright (c) 2011 Steve Revill and Shane Woolcock
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
Header: Monkey Containers Framework
The Containers framework supersedes the old Collections framework and allows cross-compatibility
with the official Monkey data structures (Stack, List, and Set).
#End

Strict
Public

' Need to alias some of the official classes since Enumerator exists in both the monkey.stack and monkey.list modules
Alias MapValueEnumerator = monkey.map.ValueEnumerator
Alias MapKeyEnumerator = monkey.map.KeyEnumerator
Alias StackEnumerator = monkey.stack.Enumerator
Alias ListEnumerator = monkey.list.Enumerator

' Importing containers will give you all the container classes
Import diddystack
Import diddyset
Import diddylist
Import diddypool
Import exception

#Rem
Summary: Indicates that an object of this class should be able to compare itself with another instance, primarily for sorting.
IComparable can't be generic at this point, because it's likely the developer would want to declare a cyclic declaration.
Cyclic declarations are currently unsupported by Monkey.
#End
Interface IComparable
#Rem
Summary: Compares this instance of the class with another instance.
Return 0 if the objects are equal, a negative value if the passed object is "less than" this object,
or a positive value if the passed object is "greater than" this object.
#End
	Method CompareTo:Int(other:Object)
End

#Rem
Summary: Indicates that this class is able to compare two instances of the generic type, T.
The comparison contract is identical to that of IComparable, as if lhs.CompareTo(rhs)
#End
Interface IComparator<T>
#Rem
Summary: Compares two instances of the generic type, T.
Return 0 if the objects are equal, a negative value if rhs is "less than" lhs,
or a positive value if rhs is "greater than" lhs.
#End
	Method Compare:Int(lhs:T, rhs:T)
End

#Rem
Summary: Provides the HasNext() and NextObject() methods that the EachIn operator expects.
#End
Interface IEnumerator<T>
#Rem
Summary: Called once every EachIn loop to check if there are more objects available.
Returns False if the next call to NextObject() will fail.
#End
	Method HasNext:Bool()

#Rem
Summary: Called once every EachIn loop to retrieve the next available object.
#End
	Method NextObject:T()
End

#Rem
Summary: Indicates that the class can be used in an EachIn loop.
When the Monkey compiler reaches an EachIn statement such as:
[code]
For Local val := EachIn lst
	' your code
Next
[/code]
It will internally convert it to:
[code]
Local oe:=lst.ObjectEnumerator()
While oe.HasNext()
	Local val:=oe.NextObject()
	' your code
End
[/code]
#End
Interface IEnumerable<T>
#Rem
Summary: Returns an instance of IEnumerator that the EachIn operator can loop with.
#End
	Method ObjectEnumerator:IEnumerator<T>()
End

#Rem
Summary: A class that implements IPredicate should act as a pseudo-lambda for predicate operations on the Container classes.
#End
Interface IPredicate<T>
	Method Evaluate:Bool(arg:T)
End

#Rem
Summary: If a class implements IPoolable, Reset() will be called when it is freed within a DiddyPool or GlobalPool.
#End
Interface IPoolable
	Method Reset:Void()
End

#Rem
Summary: A class that implements IContainer<T> must be able to interact with all of Stack, List, and Set.
#End
Interface IContainer<T>
#Rem
Summary: Getter for the current sorting comparator.
Allows sorting without implementing IComparable.
Unsupported in Set.
#End
	Method Comparator:IComparator<T>() Property

#Rem
Summary: Setter for the current sorting comparator.
Allows sorting without implementing IComparable.
Unsupported in Set.
#End
	Method Comparator:Void(comparator:IComparator<T>) Property

#Rem
Summary: Adds the entire contents of the passed Stack to the container.
AddAll should throw an IllegalArgumentException if src is Null.
#End
	Method AddAll:Void(src:Stack<T>)

#Rem
Summary: Adds the entire contents of the passed List to the container.
AddAll should throw an IllegalArgumentException if src is Null.
#End
	Method AddAll:Void(src:List<T>)

#Rem
Summary: Adds the entire contents of the passed Set to the container.
AddAll should throw an IllegalArgumentException if src is Null.
#End
	Method AddAll:Void(src:Set<T>)

#Rem
Summary: Adds the entire contents of another container to this container.
AddContainer should throw an IllegalArgumentException if src is Null.
#End
	Method AddContainer:Void(src:IContainer<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed Stack.
RemoveAll should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveAll:Void(src:Stack<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed List.
RemoveAll should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveAll:Void(src:List<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed Set.
RemoveAll should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveAll:Void(src:Set<T>)

#Rem
Summary: Removes from this container any objects that also appear in the passed container.
RemoveContainer should throw an IllegalArgumentException if src is Null.
#End
	Method RemoveContainer:Void(src:IContainer<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed Stack.
RetainAll should throw an IllegalArgumentException if src is Null.
#End
	Method RetainAll:Void(src:Stack<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed List.
RetainAll should throw an IllegalArgumentException if src is Null.
#End
	Method RetainAll:Void(src:List<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed Set.
RetainAll should throw an IllegalArgumentException if src is Null.
#End
	Method RetainAll:Void(src:Set<T>)

#Rem
Summary: Removes from this container any objects that DON'T appear in the passed container.
RetainContainer should throw an IllegalArgumentException if src is Null.
#End
	Method RetainContainer:Void(src:IContainer<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed Stack.
ContainsAll should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsAll:Bool(src:Stack<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed List.
ContainsAll should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsAll:Bool(src:List<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed Set.
ContainsAll should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsAll:Bool(src:Set<T>)

#Rem
Summary: Returns True if this container contains ALL of the elements in the passed container.
ContainsContainer should throw an IllegalArgumentException if src is Null.
#End
	Method ContainsContainer:Bool(src:IContainer<T>)

#Rem
Summary: Adds the passed item to the container.
No error should be thrown if the item already exists since Lists and Stacks allow duplicates.
Sets should do nothing if the item already exists.
A passed value of Null is implementation-dependent.
#End
	Method AddItem:Void(val:T)

#Rem
Summary: Removes the passed item from the container, if it exists.
No error should be thrown if the item does not exist.
A passed value of Null is implementation-dependent.
#End
	Method RemoveItem:Void(val:T)

#Rem
Summary: Adds the passed item to the container at the requested index.
No error should be thrown if the item already exists since Lists and Stacks allow duplicates.
Sets are unordered and should throw an UnsupportedOperationException.
A passed value of Null is implementation-dependent.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index <= Count()
#End
	Method InsertItem:Void(index:Int, val:T)

#Rem
Summary: Deletes the item from the container at the requested index.
No error should be thrown if the item already exists since Lists and Stacks allow duplicates.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index < Count()
#End
	Method DeleteItem:T(index:Int)

#Rem
Summary: Removes all items from the container.
#End
	Method ClearAll:Void()

#Rem
Summary: Returns True if the container contains the passed value.
A passed value of Null is implementation-dependent.
#End
	Method ContainsItem:Bool(val:T)

#Rem
Summary: Gets the item at the given index.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index < Count()
#End
	Method GetItem:T(index:Int)

#Rem
Summary: Sets the item at a given index to be the passed value.
A passed value of Null is implementation-dependent.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the index is outside the range 0 <= index < Count()
#End
	Method SetItem:Void(index:Int, value:T)

#Rem
Summary: Finds the first reference to the passed item and returns the index.
A value of -1 should be returned if the item is not found.
A passed value of Null is implementation-dependent.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method FindItem:Int(value:T)

#Rem
Summary: Sorts the items based on the current Comparator or T's implementation of IComparable.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method SortItems:Void(ascending:Bool = True) ' unsupported in Set

#Rem
Summary: Reverses the order of elements in the container.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method Reverse:Void()

#Rem
Summary: Randomises the order of elements in the container.
Sets are unordered and should throw an UnsupportedOperationException.
#End
	Method Shuffle:Void()

#Rem
Summary: Returns a custom IEnumerable that optionally supports a predicate.
#End
	Method Items:IEnumerable<T>(pred:IPredicate<T>=Null)

#Rem
Summary: Swaps the values of two given indices.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if either index is outside the range 0 <= index < Count()
#End
	Method SwapItems:Void(index1:Int, index2:Int)

#Rem
Summary: Reduces the number of items in the container to be no more than the passed size.
If there are less or equal elements in the container than the passed size, nothing happens.
If there are more elements in the container than the passed size, elements should be removed from the end
until Count() = the requested size.
Sets are unordered and should throw an UnsupportedOperationException.
If building with debug, an IllegalArgumentException should be thrown if the requested size < 0.
#End
	Method Truncate:Void(size:Int)

#Rem
Summary: Returns the number of items in the container.
#End
	Method Count:Int()

#Rem
Summary: Returns a new array containing all the items in the container.
The array length will be equal to Count().
Sets are unordered, therefore the order of the elements in the array will be undefined.
#End
	Method ToArray:T[]()

#Rem
Summary: Fills the passed array with the items in the container, if there is space.
If the array length is < Count(), an IllegalArgumentException should be thrown.
The first Count() items will contain the elements of the container, and any elements after that should be set to Null.
The method returns the number of elements actually filled.
Sets are unordered, therefore the order of the elements in the array will be undefined.
#End
	Method FillArray:Int(arr:T[]) ' undefined order in Set

#Rem
Summary: Compares two instances of the generic type, T.
Return 0 if the objects are equal, a negative value if rhs is "less than" lhs,
or a positive value if rhs is "greater than" lhs.
A Null value should always be considered "less than" a non-Null value.
Sets are unordered and cannot be sorted, so they should throw an UnsupportedOperationException.
#End
	Method Compare:Int(lhs:T, rhs:T)

#Rem
Summary: Performs an equality test of lhs and rhs.
Returns True if they are logically equal, otherwise False.
#End
	Method Equals:Bool(lhs:T, rhs:T)

#Rem
Summary: Returns True if there are no elements in the container, otherwise False.
#End
	Method IsEmpty:Bool()

#Rem
Summary: Returns a read-only wrapper on this container.
#End
	Method ReadOnly:ReadOnlyContainer<T>()
End

#Rem
Summary: IComparableWrapper is essentially a hack to get around Monkey's inability to cast primitives to objects.
#End
Class IComparableWrapper
#Rem
Summary: Returns True if the passed object implements the IComparable interface.
#End
	Function IsComparable:Bool(src:Object)
		Return IComparable(src) <> Null
	End

#Rem
Summary: Returns False, because an Int can never implement an interface.
#End
	Function IsComparable:Bool(src:Int)
		Return False
	End

#Rem
Summary: Returns False, because a Float can never implement an interface.
#End
	Function IsComparable:Bool(src:Float)
		Return False
	End

#Rem
Summary: Returns False, because a String can never implement an interface.
#End
	Function IsComparable:Bool(src:String)
		Return False
	End

#Rem
Summary: Performs a comparison on the passed objects.
If both objects are Null, returns 0.
If either object does not implement IComparable, returns 0.
Otherwise, IComparable.CompareTo() is called.
#End
	Function Compare:Int(lhs:Object, rhs:Object)
		' we check both lhs and rhs because we always want to compare if at least one value is non-null
		If IComparable(lhs) Then
			' normal comparison if lhs is not null
			Return IComparable(lhs).CompareTo(rhs)
		ElseIf IComparable(rhs) Then
			' reverse comparison if lhs is null but rhs is not
			Return -IComparable(rhs).CompareTo(lhs)
		End
		Return 0
	End

#Rem
Summary: Returns 0, because an Int can never implement an interface.
#End
	Function Compare:Int(lhs:Int, rhs:Int)
		Return 0
	End

#Rem
Summary: Returns 0, because a Float can never implement an interface.
#End
	Function Compare:Int(lhs:Float, rhs:Float)
		Return 0
	End

#Rem
Summary: Returns 0, because a String can never implement an interface.
#End
	Function Compare:Int(lhs:String, rhs:String)
		Return 0
	End
End

#Rem
Summary: Abstract class to provide predicate support to subclassed enumerators.
#End
Class PredicateEnumerator<T> Implements IEnumerator<T> Abstract
Private
	Global NIL:T

	Field pred:IPredicate<T>
	Field atEnd:Bool = False
	Field peeked:Bool = False
	Field nextVal:T = NIL
	Field nextValSet:Bool = False

Public
#Rem
Summary: Constructor with an optional predicate.
#End
	Method New(pred:IPredicate<T>=Null)
		Self.pred = pred
	End

#Rem
Summary: Implements IEnumerator<T>.HasNext().
If no predicate is assigned, it immediately delegates to CallHasNext()
If there is a predicate, it delegates to CallHasNext() and CallNextObject() as required.
#End
	Method HasNext:Bool()
		If Not pred Then Return CallHasNext()
		If atEnd Then Return False
		If nextValSet Then Return True
		peeked = True
		While CallHasNext()
			Local n:T = CallNextObject()
			If pred.Evaluate(n) Then
				nextVal = n
				nextValSet = True
				Return True
			End
		End
		atEnd = True
		Return False
	End

#Rem
Summary: Implements IEnumerator<T>.NextObject().
If no predicate is assigned, it immediately delegates to CallNextObject()
If there is a predicate, it delegates to HasNext() and CallNextObject() as required.
#End
	Method NextObject:T()
		If Not pred Then Return CallNextObject()
		If Not nextValSet And Not HasNext() Then Return NIL
		Local result:T = nextVal
		nextVal = NIL
		nextValSet = False
		peeked = False
		Return result
	End

#Rem
Summary: Abstract method that should delegate to the child enumerator's HasNext() method.
#End
	Method CallHasNext:Bool() Abstract

#Rem
Summary: Abstract method that should delegate to the child enumerator's NextObject() method.
#End
	Method CallNextObject:T() Abstract
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap MapValueEnumerator<String,T> as an IEnumerator<T>.
#End
Class WrappedMapValueEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:MapValueEnumerator<String,T>

Public
#Rem
Summary: Constructor to wrap a MapValueEnumerator<String,T> with an optional IPredicate<T>.
#End
	Method New(en:MapValueEnumerator<String,T>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to MapValueEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to MapValueEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps Map and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedMapValueEnumerable<K,V> Implements IEnumerable<V>
Private
	Field m:Map<K,V>
	Field pred:IPredicate<V>

Public
#Rem
Summary: Constructor to wrap a Map<K,V> enumerator as an IEnumerable<V> with an optional IPredicate<V>.
#End
	Method New(m:Map<K,V>, pred:IPredicate<V>=Null)
		Self.m = m
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedMapValueEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<V>()
		Return New WrappedMapValueEnumerator<V>(m.ObjectEnumerator(), pred)
	End
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap StackEnumerator<T> as an IEnumerator<T>.
#End
Class WrappedStackEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:StackEnumerator<T>

Public
#Rem
Summary: Constructor to wrap a StackEnumerator<T> with an optional IPredicate<T>.
#End
	Method New(en:StackEnumerator<T>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to StackEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to StackEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps Stack and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedStackEnumerable<T> Implements IEnumerable<T>
Private
	Field s:Stack<T>
	Field pred:IPredicate<T>

Public
#Rem
Summary: Constructor to wrap a Stack<T> enumerator as an IEnumerable<T> with an optional IPredicate<T>.
#End
	Method New(s:Stack<T>, pred:IPredicate<T>=Null)
		Self.s = s
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedStackEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<T>()
		Return New WrappedStackEnumerator<T>(s.ObjectEnumerator(), pred)
	End
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap ListEnumerator<T> as an IEnumerator<T>.
#End
Class WrappedListEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:ListEnumerator<T>

Public
#Rem
Summary: Constructor to wrap a ListEnumerator<T> with an optional IPredicate<T>.
#End
	Method New(en:ListEnumerator<T>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to ListEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to ListEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps List and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedListEnumerable<T> Implements IEnumerable<T>
Private
	Field l:List<T>
	Field pred:IPredicate<T>

Public
#Rem
Summary: Constructor to wrap a List<T> enumerator as an IEnumerable<T> with an optional IPredicate<T>.
#End
	Method New(l:List<T>, pred:IPredicate<T>=Null)
		Self.l = l
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedListEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<T>()
		Return New WrappedListEnumerator<T>(l.ObjectEnumerator(), pred)
	End
End

#Rem
Summary: Extends PredicateEnumerator<T> to wrap MapKeyEnumerator<T,Object> as an IEnumerator<T>.
#End
Class WrappedSetEnumerator<T> Extends PredicateEnumerator<T>
Private
	Field en:MapKeyEnumerator<T,Object>

Public
#Rem
Summary: Constructor to wrap a MapKeyEnumerator<T,Object> with an optional IPredicate<T>.
#End
	Method New(en:MapKeyEnumerator<T,Object>, pred:IPredicate<T>=Null)
		Super.New(pred)
		Self.en = en
	End

#Rem
Summary: Delegates to MapKeyEnumerator.NextObject().
#End
	Method CallNextObject:T()
		Return en.NextObject()
	End

#Rem
Summary: Delegates to MapKeyEnumerator.HasNext().
#End
	Method CallHasNext:Bool()
		Return en.HasNext()
	End
End

#Rem
Summary: Wraps Set and the ObjectEnumerator() method to provide access to a PredicateEnumerator<T>.
#End
Class WrappedSetEnumerable<T> Implements IEnumerable<T>
Private
	Field s:Set<T>
	Field pred:IPredicate<T>

Public
#Rem
Summary: Constructor to wrap a Set<T> enumerator as an IEnumerable<T> with an optional IPredicate<T>.
#End
	Method New(s:Set<T>, pred:IPredicate<T>)
		Self.s = s
		Self.pred = pred
	End

#Rem
Summary: Returns a WrappedSetEnumerator with the optional predicate.
#End
	Method ObjectEnumerator:IEnumerator<T>()
		Return New WrappedSetEnumerator<T>(s.ObjectEnumerator(), pred)
	End
End

Class ReadOnlyContainer<T> Implements IContainer<T> Final
Private
	Global NIL:T

	Field source:IContainer<T>

	Method ThrowReadOnly:Void()
		Throw New UnsupportedOperationException("The container is read only.")
	End

Public
	Method New(source:IContainer<T>)
		If Not source Then Throw New IllegalArgumentException("ReadOnlyContainer.New: Source container must not be null")
		Self.source = source
	End

	Method Comparator:IComparator<T>() Property
		Return source.Comparator
	end

	Method Comparator:Void(comparator:IComparator<T>) Property
		ThrowReadOnly()
	End

	Method AddAll:Void(src:Stack<T>)
		ThrowReadOnly()
	End

	Method AddAll:Void(src:List<T>)
		ThrowReadOnly()
	End

	Method AddAll:Void(src:Set<T>)
		ThrowReadOnly()
	End

	Method AddContainer:Void(src:IContainer<T>)
		ThrowReadOnly()
	End

	Method RemoveAll:Void(src:Stack<T>)
		ThrowReadOnly()
	End

	Method RemoveAll:Void(src:List<T>)
		ThrowReadOnly()
	End

	Method RemoveAll:Void(src:Set<T>)
		ThrowReadOnly()
	End

	Method RemoveContainer:Void(src:IContainer<T>)
		ThrowReadOnly()
	End

	Method RetainAll:Void(src:Stack<T>)
		ThrowReadOnly()
	End

	Method RetainAll:Void(src:List<T>)
		ThrowReadOnly()
	End

	Method RetainAll:Void(src:Set<T>)
		ThrowReadOnly()
	End

	Method RetainContainer:Void(src:IContainer<T>)
		ThrowReadOnly()
	End

	Method ContainsAll:Bool(src:Stack<T>)
		Return source.ContainsAll(src)
	End

	Method ContainsAll:Bool(src:List<T>)
		Return source.ContainsAll(src)
	End

	Method ContainsAll:Bool(src:Set<T>)
		Return source.ContainsAll(src)
	End

	Method ContainsContainer:Bool(src:IContainer<T>)
		Return source.ContainsContainer(src)
	End

	Method AddItem:Void(val:T)
		ThrowReadOnly()
	End

	Method RemoveItem:Void(val:T)
		ThrowReadOnly()
	End

	Method InsertItem:Void(index:Int, val:T)
		ThrowReadOnly()
	End

	Method DeleteItem:T(index:Int)
		ThrowReadOnly()
		Return NIL
	End

	Method ClearAll:Void()
		ThrowReadOnly()
	End

	Method ContainsItem:Bool(val:T)
		Return source.ContainsItem(val)
	End

	Method GetItem:T(index:Int)
		Return source.GetItem(index)
	End

	Method SetItem:Void(index:Int, value:T)
		ThrowReadOnly()
	End

	Method FindItem:Int(value:T)
		Return source.FindItem(value)
	End

	Method SortItems:Void(ascending:Bool = True)
		ThrowReadOnly()
	End

	Method Reverse:Void()
		ThrowReadOnly()
	End

	Method Shuffle:Void()
		ThrowReadOnly()
	End

	Method Items:IEnumerable<T>(pred:IPredicate<T>=Null)
		Return source.Items(pred)
	End

	Method SwapItems:Void(index1:Int, index2:Int)
		ThrowReadOnly()
	End

	Method Truncate:Void(size:Int)
		ThrowReadOnly()
	End

	Method Count:Int()
		Return source.Count()
	End

	Method ToArray:T[]()
		Return source.ToArray()
	End

	Method FillArray:Int(arr:T[])
		Return source.FillArray(arr)
	End

	Method Compare:Int(lhs:T, rhs:T)
		Return source.Compare(lhs, rhs)
	End

	Method Equals:Bool(lhs:T, rhs:T)
		Return source.Equals(lhs, rhs)
	End

	Method IsEmpty:Bool()
		Return source.IsEmpty()
	End

	Method ReadOnly:ReadOnlyContainer<T>()
		Return New ReadOnlyContainer<T>(Self)
	End
End