Added host/run details to TestReportSimple headline

Strict

Private

Import bono.src.exceptions
Import bono.src.utils
Import reflection
Import testlistener
Import testsuite

Public

Class TestReportSimple Implements TestListener
    Private

    Field lastClass:ClassInfo
    Field currentClass:ClassInfo
    Field currentMethod:MethodInfo
    Field result:Int = PASSED
    Field numTests:Int
    Field failureMessages:StringMap<String> = New StringMap<String>()
    Field skipMessages:StringMap<String> = New StringMap<String>()
    Field incompleteMessages:StringMap<String> = New StringMap<String>()
    Field printCache:String = "    "

    Const PASSED:Int = 0
    Const FAILED:Int = 1
    Const SKIPPED:Int = 2
    Const INCOMPLETE:Int = 3

    Public

    Field verbose:Bool

    Method StartTestSuite:Void(suite:TestSuite)
        Print "Running " + Target.CONFIG +
            " " + Target.LANG + "/" + Target.TARGET +
            " tests on " + Target.HOST + ":~n"
    End

    Method StartTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        currentClass = classInfo
        currentMethod = methodInfo
        result = PASSED
        numTests += 1
    End

    Method AddFailure:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = FAILED
        failureMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddSkippedTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = SKIPPED
        skipMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddIncompleteTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = INCOMPLETE
        incompleteMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method EndTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        PrintResult()
        currentClass = Null
        currentMethod = Null
    End

    Method EndTestSuite:Void(suite:TestSuite)
        FlushPrintCache()
        PrintStats()

        failureMessages.Clear()
        incompleteMessages.Clear()
        skipMessages.Clear()
    End

    Private

    Method PrintResult:Void()
        If Not lastClass Or Not (lastClass.Name() = currentClass.Name())
            If lastClass
                FlushPrintCache()
                Print ""
            End
            Print "  " + currentClass.Name()
        End
        lastClass = currentClass

        If verbose
            Print "    " + currentMethod.Name() + " " + GetResultText()
        Else
            printCache += GetResultDot()
            FlushPrintCacheIfRequired()
        End
    End

    Method FlushPrintCache:Void()
        If verbose Then Return
        Print printCache
        printCache = "    "
    End

    Method FlushPrintCacheIfRequired:Void(len:Int=80)
        If printCache.Length() >= len Then FlushPrintCache()
    End

    Method GetResultText:String()
        Select result
        Case PASSED
            Return ""
        Case FAILED
            Return "[FAILED]"
        Case SKIPPED
            Return "[SKIPPED]"
        Case INCOMPLETE
            Return "[INCOMPLETE]"
        Default
            Throw New RuntimeException("Invalid result id: " + result)
        End
    End

    Method GetResultDot:String()
        Select result
        Case PASSED
            Return "."
        Case FAILED
            Return "F"
        Case SKIPPED
            Return "S"
        Case INCOMPLETE
            Return "I"
        Default
            Throw New RuntimeException("Invalid result id: " + result)
        End
    End

    Method PrintStats:Void()
        Print ""

        PrintMessages(skipMessages, "skipped test")
        PrintMessages(incompleteMessages, "incomplete test")
        PrintMessages(failureMessages, "failure")

        PrintSummary()
        Print "Tests: "      + numTests                   +
            ", Failed: "     + failureMessages.Count()    +
            ", Skipped: "    + skipMessages.Count()       +
            ", Incomplete: " + incompleteMessages.Count() +
            "."
    End

    Method PrintMessages:Void(messages:StringMap<String>, desciption:String)
        If messages.Count() = 0 Then Return
        Print "There was " + messages.Count() + " " + desciption + ":~n"

        Local counter:Int = 0
        For Local key:String = EachIn messages.Keys()
            Local val:String = messages.Get(key).Replace("~n", "~n  ")
            counter += 1

            Print counter + ") " + key + "~n  " + val + "~n"
        End
    End

    Method PrintSummary:Void()
        If failureMessages.Count() > 0
            Print "FAILURES!"
        ElseIf skipMessages.Count() > 0 Or incompleteMessages.Count() > 0
            Print "OK, but incomplete or skipped tests!"
        Else
            Print "OK"
        End
    End

    Method GetFQN:String(classInfo:ClassInfo, methodInfo:MethodInfo)
        Local parts:String[] = classInfo.Name().Split(".")
        Local filename:String = "/".Join(parts[..-1]) + ".monkey"
        Local classname:String = parts[parts.Length() - 1]
        Return classname + "." + methodInfo.Name() + " in " + filename
    End
End

Strict

Private

Import bono.src.exceptions
Import reflection
Import testlistener
Import testsuite

Public

Class TestReportSimple Implements TestListener
    Private

    Field lastClass:ClassInfo
    Field currentClass:ClassInfo
    Field currentMethod:MethodInfo
    Field result:Int = PASSED
    Field numTests:Int
    Field failureMessages:StringMap<String> = New StringMap<String>()
    Field skipMessages:StringMap<String> = New StringMap<String>()
    Field incompleteMessages:StringMap<String> = New StringMap<String>()
    Field printCache:String = "    "

    Const PASSED:Int = 0
    Const FAILED:Int = 1
    Const SKIPPED:Int = 2
    Const INCOMPLETE:Int = 3

    Public

    Field verbose:Bool

    Method StartTestSuite:Void(suite:TestSuite)
        Print "Tests within this testsuite:~n"
    End

    Method StartTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        currentClass = classInfo
        currentMethod = methodInfo
        result = PASSED
        numTests += 1
    End

    Method AddFailure:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = FAILED
        failureMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddSkippedTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = SKIPPED
        skipMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddIncompleteTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = INCOMPLETE
        incompleteMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method EndTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        PrintResult()
        currentClass = Null
        currentMethod = Null
    End

    Method EndTestSuite:Void(suite:TestSuite)
        FlushPrintCache()
        PrintStats()

        failureMessages.Clear()
        incompleteMessages.Clear()
        skipMessages.Clear()
    End

    Private

    Method PrintResult:Void()
        If Not lastClass Or Not (lastClass.Name() = currentClass.Name())
            If lastClass
                FlushPrintCache()
                Print ""
            End
            Print "  " + currentClass.Name()
        End
        lastClass = currentClass

        If verbose
            Print "    " + currentMethod.Name() + " " + GetResultText()
        Else
            printCache += GetResultDot()
            FlushPrintCacheIfRequired()
        End
    End

    Method FlushPrintCache:Void()
        If verbose Then Return
        Print printCache
        printCache = "    "
    End

    Method FlushPrintCacheIfRequired:Void(len:Int=80)
        If printCache.Length() >= len Then FlushPrintCache()
    End

    Method GetResultText:String()
        Select result
        Case PASSED
            Return ""
        Case FAILED
            Return "[FAILED]"
        Case SKIPPED
            Return "[SKIPPED]"
        Case INCOMPLETE
            Return "[INCOMPLETE]"
        Default
            Throw New RuntimeException("Invalid result id: " + result)
        End
    End

    Method GetResultDot:String()
        Select result
        Case PASSED
            Return "."
        Case FAILED
            Return "F"
        Case SKIPPED
            Return "S"
        Case INCOMPLETE
            Return "I"
        Default
            Throw New RuntimeException("Invalid result id: " + result)
        End
    End

    Method PrintStats:Void()
        Print ""

        PrintMessages(skipMessages, "skipped test")
        PrintMessages(incompleteMessages, "incomplete test")
        PrintMessages(failureMessages, "failure")

        PrintSummary()
        Print "Tests: "      + numTests                   +
            ", Failed: "     + failureMessages.Count()    +
            ", Skipped: "    + skipMessages.Count()       +
            ", Incomplete: " + incompleteMessages.Count() +
            "."
    End

    Method PrintMessages:Void(messages:StringMap<String>, desciption:String)
        If messages.Count() = 0 Then Return
        Print "There was " + messages.Count() + " " + desciption + ":~n"

        Local counter:Int = 0
        For Local key:String = EachIn messages.Keys()
            Local val:String = messages.Get(key).Replace("~n", "~n  ")
            counter += 1

            Print counter + ") " + key + "~n  " + val + "~n"
        End
    End

    Method PrintSummary:Void()
        If failureMessages.Count() > 0
            Print "FAILURES!"
        ElseIf skipMessages.Count() > 0 Or incompleteMessages.Count() > 0
            Print "OK, but incomplete or skipped tests!"
        Else
            Print "OK"
        End
    End

    Method GetFQN:String(classInfo:ClassInfo, methodInfo:MethodInfo)
        Local parts:String[] = classInfo.Name().Split(".")
        Local filename:String = "/".Join(parts[..-1]) + ".monkey"
        Local classname:String = parts[parts.Length() - 1]
        Return classname + "." + methodInfo.Name() + " in " + filename
    End
End


StringWidth/StringHeight etc.

StringWidth/StringHeight etc.

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

Import mojo
Import diddy

Class Font
Public
' Public globals
	Global fonts:StringMap

Private
' Private consts
	Const CHAR_COUNT:Int = 128

' Private fields
	Field name:String
	Field atlasName:String
	Field blackAtlasName:String
	Field atlas:GameImage
	Field blackAtlas:GameImage

	Field srcX:Int[]
	Field srcY:Int[]
	Field srcWidth:Int[]
	Field srcHeight:Int[]
	Field baseline:Float[]
	Field maxHeight:Int
	Field maxBaseline:Float

Public
' Properties
	' Name is read-only
	Method Name:String() Property
		Return name
	End

	' AtlasName is read-only
	Method AtlasName:String() Property
		Return atlasName
	End

	' BlackAtlasName is read-only
	Method BlackAtlasName:String() Property
		Return blackAtlasName
	End

	' MaxHeight is read-only
	Method MaxHeight:Int() Property
		Return maxHeight
	End

	' MaxBaseline is read-only
	Method MaxBaseline:Float() Property
		Return maxBaseline
	End

' Constructors
	Method New()
		srcX = New Int[CHAR_COUNT]
		srcY = New Int[CHAR_COUNT]
		srcWidth = New Int[CHAR_COUNT]
		srcHeight = New Int[CHAR_COUNT]
		baseline = New Float[CHAR_COUNT]
	End

' Public methods
	Method StringWidth:Int(str:String,offset:Int=0,length:Int=-1)
		If length <= 0 Then Return 0
		If offset < 0 Or offset+length > str.Length Then Return 0
		Local rv:Int = 0
		For Local i:Int = offset Until offset+length
			rv += srcWidth[str[i]]
		Next
		Return rv
	End

	Method StringHeight:Int(str:String,offset:Int=0,length:Int=-1)
		If length <= 0 Then Return 0
		If offset < 0 Or offset+length > str.Length Then Return 0
		Local rv:Int = 0
		For Local i:Int = offset Until offset+length
			If rv < srcHeight[str[i]] Then rv = srcHeight[str[i]]
		Next
		Return rv
	End

	Method DrawString:Void(str:String, x:Int, y:Int, alignX:Float=0, alignY:Float=0, useBaseline:Bool=True,red:Int=255,green:Int=255,blue:Int=255)
		Local myAtlas:GameImage
		If red = 0 And green = 0 And blue = 0 Then
			SetColor(255,255,255)
			If blackAtlas = Null Then blackAtlas = game.images.Load(blackAtlasName,,False)
			myAtlas = blackAtlas
		Else
			SetColor(red,green,blue)
			If atlas = Null Then atlas = game.images.Load(atlasName,,False)
			myAtlas = atlas
		End
		Local strlen:Int = str.Length
		Local rx:Int = 0
		Local ry:Int = 0 ' TODO: newlines
		Local bl:Float = 0
		Local totalWidth:Int = 0
		Local totalHeight:Int = srcHeight[ASC_UPPER_A]
		For Local i:Int = 0 Until strlen
			If useBaseline Then bl = baseline[str[i]]
			Select str[i]
				Case ASC_SPACE
				Case ASC_LF
				Case ASC_CR
				Default

			End
			rx += srcWidth[str[i]]
			If rx > totalWidth Then totalWidth = rx
		Next
		rx = x-alignX*totalWidth
		ry = y-alignY*totalHeight
		For Local i:Int = 0 Until strlen
			If useBaseline Then bl = baseline[str[i]]
			Select str[i]
				Case ASC_SPACE
				Case ASC_LF
				Case ASC_CR
				Default
					myAtlas.DrawSubImage(rx, ry-bl, srcX[str[i]], srcY[str[i]], srcWidth[str[i]], srcHeight[str[i]])
			End
			rx += srcWidth[str[i]]
		Next
	End

' Public functions
	Function LoadFonts:Void(doc:XMLDocument)
		fonts = New StringMap
		Local fontNodes:ArrayList<XMLElement> = doc.Root.GetChildrenByName("font")
		For Local node:XMLElement = EachIn fontNodes
			Local f:Font = New Font
			f.name = node.GetAttribute("name")
			f.atlasName = node.GetAttribute("atlas")
			f.blackAtlasName = node.GetAttribute("blackAtlas")
			Local glyphNodes:ArrayList<XMLElement> = node.GetChildrenByName("glyph")
			For Local glyph:XMLElement = EachIn glyphNodes
				Local code:Int = Int(glyph.GetAttribute("code","0"))
				f.baseline[code] = Float(glyph.GetAttribute("baseline","0"))
				f.srcX[code] = Int(glyph.GetAttribute("srcX","0"))
				f.srcY[code] = Int(glyph.GetAttribute("srcY","0"))
				f.srcWidth[code] = Int(glyph.GetAttribute("srcWidth","0"))
				f.srcHeight[code] = Int(glyph.GetAttribute("srcHeight","0"))
				If f.maxHeight < f.srcHeight[code] Then f.maxHeight = f.srcHeight[code]
				If f.maxBaseline < f.baseline[code] Then f.maxBaseline = f.baseline[code]
			Next
			fonts.Set(f.name, f)
		Next
	End
End ' Class Font

Import mojo
Import diddy

Class Font
Public
' Public globals

	Global fonts:StringMap

Private
' Private consts

	Const CHAR_COUNT:Int = 128

' Private fields

	Field name:String
	Field atlasName:String
	Field blackAtlasName:String
	Field atlas:GameImage
	Field blackAtlas:GameImage

	Field srcX:Int[]
	Field srcY:Int[]
	Field srcWidth:Int[]
	Field srcHeight:Int[]
	Field baseline:Float[]

Public
' Constructors

	Method New()
		srcX = New Int[CHAR_COUNT]
		srcY = New Int[CHAR_COUNT]
		srcWidth = New Int[CHAR_COUNT]
		srcHeight = New Int[CHAR_COUNT]
		baseline = New Float[CHAR_COUNT]
	End

' Public methods

	Method DrawString:Void(str:String, x:Int, y:Int, alignX:Float=0, alignY:Float=0, useBaseline:Bool=True,red:Int=255,green:Int=255,blue:Int=255)
		Local myAtlas:GameImage
		If red = 0 And green = 0 And blue = 0 Then
			SetColor(255,255,255)
			If blackAtlas = Null Then blackAtlas = game.images.Load(blackAtlasName,,False)
			myAtlas = blackAtlas
		Else
			SetColor(red,green,blue)
			If atlas = Null Then atlas = game.images.Load(atlasName,,False)
			myAtlas = atlas
		End
		Local strlen:Int = str.Length
		Local rx:Int = 0
		Local ry:Int = 0 ' TODO: newlines
		Local bl:Float = 0
		Local totalWidth:Int = 0
		Local totalHeight:Int = srcHeight[ASC_UPPER_A]
		For Local i:Int = 0 Until strlen
			If useBaseline Then bl = baseline[str[i]]
			Select str[i]
				Case ASC_SPACE
				Case ASC_LF
				Case ASC_CR
				Default

			End
			rx += srcWidth[str[i]]
			If rx > totalWidth Then totalWidth = rx
		Next
		rx = x-alignX*totalWidth
		ry = y-alignY*totalHeight
		For Local i:Int = 0 Until strlen
			If useBaseline Then bl = baseline[str[i]]
			Select str[i]
				Case ASC_SPACE
				Case ASC_LF
				Case ASC_CR
				Default
					myAtlas.DrawSubImage(rx, ry-bl, srcX[str[i]], srcY[str[i]], srcWidth[str[i]], srcHeight[str[i]])
			End
			rx += srcWidth[str[i]]
		Next
	End

' Public functions

	Function LoadFonts:Void(doc:XMLDocument)
		fonts = New StringMap
		Local fontNodes:ArrayList<XMLElement> = doc.Root.GetChildrenByName("font")
		For Local node:XMLElement = EachIn fontNodes
			Local f:Font = New Font
			f.name = node.GetAttribute("name")
			f.atlasName = node.GetAttribute("atlas")
			f.blackAtlasName = node.GetAttribute("blackAtlas")
			Local glyphNodes:ArrayList<XMLElement> = node.GetChildrenByName("glyph")
			For Local glyph:XMLElement = EachIn glyphNodes
				Local code:Int = Int(glyph.GetAttribute("code","0"))
				f.baseline[code] = Float(glyph.GetAttribute("baseline","0"))
				f.srcX[code] = Int(glyph.GetAttribute("srcX","0"))
				f.srcY[code] = Int(glyph.GetAttribute("srcY","0"))
				f.srcWidth[code] = Int(glyph.GetAttribute("srcWidth","0"))
				f.srcHeight[code] = Int(glyph.GetAttribute("srcHeight","0"))
			Next
			fonts.Set(f.name, f)
		Next
	End
End ' Class Font


Added Comparable interface.

Added Comparable interface.

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

Strict

Import assert

#Rem
Monkey Collections Framework
Based loosely on the Java Collections Framework
#End

'''' Top level Collection stuff ''''

#Rem
	AbstractCollection
	Due to limitations with generics in Monkey, ToArray cannot work with an array of type E.  We must return Object[] instead.
#End
Class AbstractCollection<E> Abstract
Private
	' A custom comparator to use for sorting and comparing items.
	Field comparator:AbstractComparator = Null

Public
	' Property to read comparator
	Method Comparator:AbstractComparator() Property
		Return comparator
	End
	' Property to write comparator
	Method Comparator:Void(comparator:AbstractComparator) Property
		Self.comparator = comparator
	End

	Method Add:Bool(o:E) Abstract
	Method AddAll:Bool(c:AbstractCollection<E>) Abstract
	Method Clear:Void() Abstract
	Method Contains:Bool(o:E) Abstract
	Method ContainsAll:Bool(c:AbstractCollection<E>) Abstract
	Method Enumerator:AbstractEnumerator<E>() Abstract
	Method FillArray:Int(arr:Object[]) Abstract ' populates the passed array and returns the number of items filled. best used for android
	Method IsEmpty:Bool() Abstract
	Method Remove:Bool(o:E) Abstract
	Method RemoveAll:Bool(c:AbstractCollection<E>) Abstract
	Method RetainAll:Bool(c:AbstractCollection<E>) Abstract
	Method Size:Int() Property Abstract
	Method Sort:Void(reverse:Bool = False, comp:AbstractComparator = Null) Abstract
	Method ToArray:Object[]() Abstract ' creates a new array of the correct size and returns it

	Method ObjectEnumerator:AbstractEnumerator<E>()
		Return Enumerator()
	End
End

#Rem
	AbstractEnumerator
	Used in the ObjectEnumerator method for calls to EachIn.
	If retrieved and used manually, the HasPrevious/PreviousObject/Remove/First/Last methods can be called.
#End
Class AbstractEnumerator<E>
Public
	Method HasNext:Bool() Abstract
	Method HasPrevious:Bool() Abstract
	Method NextObject:E() Abstract
	Method PreviousObject:E() Abstract
	Method Remove:Void() Abstract
	Method First:Void() Abstract
	Method Last:Void() Abstract
	Method Reset:Void() Abstract
End

#Rem
	Comparable
	Allows developers to sort a collection without using a Comparator.  Each class is responsible
	for providing its own logic to determine how it should be sorted.
#End
Interface Comparable
	Method Compare:Int(o1:Object, o2:Object)
	Method CompareBool:Bool(o1:Object, o2:Object)
End

#Rem
	AbstractComparator
	This is a way for developers to provide a custom comparison method for sorting lists.
	It's sort of like a function pointer.
#End
Class AbstractComparator Abstract
	Method Compare:Int(o1:Object, o2:Object) Abstract

	Method CompareBool:Bool(o1:Object, o2:Object)
		Return o1 = o2 Or Compare(o1, o2) = 0
	End

	Method HashCode:Int(o:Object)
		Return 0
	End
End

#Rem
	DefaultComparator
	Implements an AbstractComparator to handle primitive wrappers and strings.
#End
Global DEFAULT_COMPARATOR:DefaultComparator = New DefaultComparator
Class DefaultComparator Extends AbstractComparator
	Method Compare:Int(o1:Object, o2:Object)
		If IntObject(o1) <> Null And IntObject(o2) <> Null Then
			If IntObject(o1).value < IntObject(o2).value Then Return -1
			If IntObject(o1).value > IntObject(o2).value Then Return 1
			Return 0
		ElseIf FloatObject(o1) <> Null And FloatObject(o2) <> Null Then
			If FloatObject(o1).value < FloatObject(o2).value Then Return -1
			If FloatObject(o1).value > FloatObject(o2).value Then Return 1
			Return 0
		ElseIf StringObject(o1) <> Null And StringObject(o2) <> Null Then
			If StringObject(o1).value < StringObject(o2).value Then Return -1
			If StringObject(o1).value > StringObject(o2).value Then Return 1
			Return 0
		End
		If o1 = o2 Then Return 0
		If o1 = Null Then Return -1
		If o2 = Null Then Return 1
		Return 0 ' don't know what to do!
	End

	Method CompareBool:Bool(o1:Object, o2:Object)
		If IntObject(o1) <> Null And IntObject(o2) <> Null Then
			Return IntObject(o1).value = IntObject(o2).value
		ElseIf FloatObject(o1) <> Null And FloatObject(o2) <> Null Then
			Return FloatObject(o1).value = FloatObject(o2).value
		ElseIf StringObject(o1) <> Null And StringObject(o2) <> Null Then
			Return StringObject(o1).value = StringObject(o2).value
		End
		Return o1 = o2
	End
End

'''' List stuff ''''

#Rem
	AbstractList
	Extends AbstractCollection to implement some of the abstract methods that apply to any kind of list.
#End
Class AbstractList<E> Extends AbstractCollection<E> Abstract
Private
	' If true, bounds checking should be performed.
#if CONFIG="debug"
	Field rangeChecking:Bool = True
#else
	Field rangeChecking:Bool = False
#end

	' A counter for modifications to the list.  Used for concurrency checks.
	Field modCount:Int = 0

	' Performs a range check.
	Method RangeCheck:Void(index:Int)
		Local size:Int = Self.Size()
		' range check doesn't use assert, for speed
		If index < 0 Or index >= size Then Error("AbstractList.RangeCheck: Index out of bounds: " + index + " is not 0<=index<" + size)
	End

Public
	' Property to read rangeChecking
	Method RangeChecking:Bool() Property
		Return rangeChecking
	End
	' Property to write rangeChecking
	Method RangeChecking:Void(rangeChecking:Bool) Property
		Self.rangeChecking = rangeChecking
	End

	Method Enumerator:AbstractEnumerator<E>()
		Return New ListEnumerator<E>(Self)
	End

	Method Get:E(index:Int) Abstract
	Method Insert:Void(index:Int, o:E) Abstract
	Method InsertAll:Bool(index:Int, c:AbstractCollection<E>) Abstract
	Method IndexOf:Int(o:E) Abstract
	Method LastIndexOf:Int(o:E) Abstract
	Method RemoveAt:E(index:Int) Abstract ' Can't overload Remove since it's an inherited method, and Monkey doesn't support that.
	Method RemoveRange:Void(fromIndex:Int, toIndex:Int) Abstract
	Method Set:E(index:Int, o:E) Abstract
End

#Rem
	ListEnumerator
	Extends AbstractEnumerator to provide support for EachIn.  Blocks concurrent modification, but allows elements to be removed on the fly.
#End
Class ListEnumerator<E> Extends AbstractEnumerator<E>
Private
	Field lst:AbstractList<E>
	Field lastIndex:Int = 0
	Field index:Int = 0
	Field expectedModCount:Int = 0
Public
	Method New(lst:AbstractList<E>)
		Self.lst = lst
		expectedModCount = lst.modCount
	End

	Method CheckConcurrency:Void()
		' for speed we don't use assert
		If lst.modCount <> expectedModCount Then Error("ListEnumerator.CheckConcurrency: Concurrent list modification")
	End

	Method HasNext:Bool()
		CheckConcurrency()
		Return index < lst.Size
	End

	Method HasPrevious:Bool()
		CheckConcurrency()
		Return index > 0
	End

	Method NextObject:E()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex)
	End

	Method PreviousObject:E()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex)
	End

	Method Remove:Void()
		CheckConcurrency()
		lst.RemoveAt(lastIndex)
		If lastIndex < index Then index -= 1
		lastIndex = -1
		expectedModCount = lst.modCount
	End

	Method First:Void()
		CheckConcurrency()
		index = 0
	End

	Method Last:Void()
		CheckConcurrency()
		index = lst.Size
	End

	Method Reset:Void()
		index = 0
		expectedModCount = lst.modCount
	End
End

#Rem
	ArrayListEnumerator
	Extends ListEnumerator to avoid some method calls.
#End
Class ArrayListEnumerator<E> Extends ListEnumerator<E>
	Field alst:ArrayList<E>

	Method New(lst:ArrayList<E>)
		Super.New(lst)
		Self.alst = lst
		expectedModCount = alst.modCount
	End

	Method HasNext:Bool()
		CheckConcurrency()
		Return index < alst.size
	End

	Method NextObject:E()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return alst.elements[lastIndex]
	End

	Method PreviousObject:E()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return alst.elements[lastIndex]
	End

	Method Last:Void()
		CheckConcurrency()
		index = alst.size
	End
End

Class IntListEnumerator Extends ListEnumerator<IntObject>
Public
	Method New(lst:AbstractList<IntObject>)
		Super.New(lst)
	End

	Method NextInt:Int()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex).value
	End

	Method PreviousInt:Int()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex).value
	End
End

Class FloatListEnumerator Extends ListEnumerator<FloatObject>
Public
	Method New(lst:AbstractList<FloatObject>)
		Super.New(lst)
	End

	Method NextFloat:Float()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex).value
	End

	Method PreviousFloat:Float()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex).value
	End
End

Class StringListEnumerator Extends ListEnumerator<StringObject>
Public
	Method New(lst:AbstractList<IntObject>)
		Super.New(lst)
	End

	Method NextString:String()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex).value
	End

	Method PreviousString:String()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex).value
	End
End

#Rem
	ArrayList
	Concrete implementation of AbstractList that uses a dynamically sized array to store elements.
	Has best performance when it is initialised with a capacity large enough to hold the expected number of elements.
#End
Class ArrayList<E> Extends AbstractList<E>
Private
	' fields
	Field elements:Object[]
	Field size:Int = 0

	' resizes the elements array if necessary to ensure it can fit minCapacity elements
	Method EnsureCapacity:Void(minCapacity:Int)
		Local oldCapacity:Int = elements.Length
		If minCapacity > oldCapacity Then
			Local newCapacity:Int = (oldCapacity * 3) / 2 + 1
			If newCapacity < minCapacity Then newCapacity = minCapacity
			elements = elements.Resize(newCapacity)
			modCount += 1
		End
	End

	Method RangeCheck:Void(index:Int)
		' range check doesn't use assert, for speed
		If index < 0 Or index >= size Then Error("ArrayList.RangeCheck: Index out of bounds: " + index + " is not 0<=index<" + size)
	End

	Field tempArr:Object[] = New Object[128] ' temp array used for internal call to ToArray (so we don't create an object)

Public
	' constructors
	Method New()
		Self.elements = New Object[10]
	End

	Method New(initialCapacity:Int)
		AssertGreaterThanOrEqual(initialCapacity, 0, "ArrayList.New: Illegal Capacity:")
		Self.elements = New Object[initialCapacity]
	End

	Method New(c:AbstractCollection<E>)
		elements = c.ToArray()
		size = elements.Length
	End

	'implements AbstractCollection

	Method Add:Bool(o:E)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		elements[size] = o
		size+=1
		modCount += 1
		Return True
	End

	Method AddAll:Bool(c:AbstractCollection<E>)
		If c.IsEmpty() Then Return False
		Local newItemCount:Int = c.Size
		If size + newItemCount > elements.Length Then EnsureCapacity(size+newItemCount)
		If tempArr.Length < newItemCount Then tempArr = tempArr.Resize(newItemCount)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			elements[size] = tempArr[i]
			size+=1
		End
		modCount += 1
		Return newItemCount <> 0
	End

	Method Clear:Void()
		For Local i:Int = 0 Until size
			elements[i] = Null
		Next
		modCount += 1
		size = 0
	End

	Method Contains:Bool(o:E)
		For Local i:Int = 0 Until size
			If elements[i] = o Then Return True
		Next
		Return False
	End

	Method ContainsAll:Bool(c:AbstractCollection<E>)
		If c.IsEmpty() Then Return True
		If tempArr.Length < c.Size Then tempArr = tempArr.Resize(c.Size)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			If Not Self.Contains(E(tempArr[i])) Then Return False
		Next
		Return True
	End

	Method FillArray:Int(arr:Object[])
		AssertGreaterThanOrEqual(arr.Length, size, "ArrayList.FillArray: Array too small:")
		For Local i:Int = 0 Until size
			arr[i] = elements[i]
		Next
		Return size
	End

	Method IsEmpty:Bool()
		Return size = 0
	End

	Method Remove:Bool(o:E)
		For Local i:Int = 0 Until size
			If elements[i] = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method RemoveAll:Bool(c:AbstractCollection<E>)
		If c.IsEmpty() Then Return False
		Local modified:Bool = False
		If tempArr.Length < c.Size Then tempArr = tempArr.Resize(c.Size)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			If Self.Contains(E(tempArr[i])) Then
				Self.Remove(E(tempArr[i]))
			End
		Next
		If modified Then modCount += 1
		Return modified
	End

	Method RetainAll:Bool(c:AbstractCollection<E>)
		Local modified:Bool = False
		If tempArr.Length < c.Size Then tempArr = tempArr.Resize(c.Size)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			If Not Self.Contains(E(tempArr[i])) Then
				Self.Remove(E(tempArr[i]))
				modified = True
			End
		Next
		If modified Then modCount += 1
		Return modified
	End

	Method Size:Int() Property
		Return size
	End

	Method Sort:Void(reverse:Bool = False, comp:AbstractComparator = Null)
		If size <= 1 Then Return ' can't sort 0 or 1 elements
		If comp = Null Then comp = Self.Comparator
		If comp = Null Then comp = DEFAULT_COMPARATOR
		QuickSort(elements, 0, size-1, comp, reverse)
		modCount += 1
	End

	Method ToArray:Object[]()
		Local arr:Object[] = New Object[size]
		For Local i:Int = 0 Until size
			arr[i] = elements[i]
		Next
		Return arr
	End

	' implements AbstractList

	Method Get:E(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return E(elements[index])
	End

	Method Insert:Void(index:Int, o:E)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = o
		size+=1
		modCount += 1
	End

	Method InsertAll:Bool(index:Int, c:AbstractCollection<E>)
		Local newItemCount:Int = c.Size
		If newItemCount = 0 Then Return False
		If size + newItemCount > elements.Length Then EnsureCapacity(size+newItemCount)
		If tempArr.Length < newItemCount Then tempArr = tempArr.Resize(newItemCount)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = size - 1 To index Step -1
			elements[i+newItemCount] = elements[i]
		Next
		For Local i:Int = 0 Until newItemCount
			elements[index+i] = tempArr[i]
		End
		size += newItemCount
		modCount += 1
		Return newItemCount <> 0
	End

	Method IndexOf:Int(o:E)
		For Local i:Int = 0 Until size
			If elements[i] = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOf:Int(o:E)
		For Local i:Int = size-1 To 0 Step -1
			If elements[i] = o Then Return i
		Next
		Return -1
	End

	Method RemoveAt:E(index:Int)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:E = E(elements[index])
		For Local i:Int = index Until size-1
			elements[i] = elements[i+1]
		Next
		elements[size-1] = Null
		size-=1
		modCount += 1
		Return oldValue
	End

	Method RemoveRange:Void(fromIndex:Int, toIndex:Int)
		AssertLessThanOrEqual(fromIndex, toIndex, "ArrayList.RemoveRange: fromIndex > toIndex:")
		If rangeChecking Then
			RangeCheck(fromIndex)
			RangeCheck(toIndex)
		End
		For Local i:Int = 0 Until toIndex - fromIndex
			RemoveAt(fromIndex)
		Next
	End

	Method Set:E(index:Int, o:E)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:E = E(elements[index])
		elements[index] = o
		modCount += 1
		Return oldValue
	End

	Method Enumerator:AbstractEnumerator<E>()
		Return New ArrayListEnumerator<E>(Self)
	End
End

' ArrayList wrapper classes

Class IntArrayList Extends ArrayList<IntObject>
Public
	Method AddInt:Bool(o:Int)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		elements[size] = New IntObject(o)
		size+=1
		modCount += 1
		Return True
	End

	Method ContainsInt:Bool(o:Int)
		For Local i:Int = 0 Until size
			If IntObject(elements[i]).value = o Then Return True
		Next
		Return False
	End

	Method Enumerator:AbstractEnumerator<IntObject>()
		Return New IntArrayListEnumerator(Self)
	End

	Method RemoveInt:Bool(o:Int)
		For Local i:Int = 0 Until size
			If IntObject(elements[i]).value = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method ToIntArray:Int[]()
		Local arr:Int[] = New Int[size]
		For Local i:Int = 0 Until size
			arr[i] = IntObject(elements[i]).value
		Next
		Return arr
	End

	Method GetInt:Int(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return IntObject(elements[index]).value
	End

	Method InsertInt:Void(index:Int, o:Int)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = New IntObject(o)
		size+=1
		modCount += 1
	End

	Method IndexOfInt:Int(o:Int)
		For Local i:Int = 0 Until size
			If IntObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOfInt:Int(o:E)
		For Local i:Int = size-1 To 0 Step -1
			If IntObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method SetInt:Int(index:Int, o:Int)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:Int = IntObject(elements[index]).value
		If elements[index] <> Null Then
			IntObject(elements[index]).value = o ' XXX: not sure whether we can change value on the fly
		End
		modCount += 1
		Return oldValue
	End

	Method FillIntArray:Int(arr:Int[])
		AssertLessThan(arr.Length, size, "IntArrayList.FillIntArray: Array too small:")
		For Local i:Int = 0 Until size
			arr[i] = IntObject(elements[i]).value
		Next
		Return size
	End
End

Class FloatArrayList Extends ArrayList<FloatObject>
Public
	Method AddFloat:Bool(o:Float)
		If size + 1 > elements.Length Then EnsureCapacity(size + 1)
		elements[size] = New FloatObject(o)
		size += 1
		modCount += 1
		Return True
	End

	Method ContainsFloat:Bool(o:Float)
		For Local i:Int = 0 Until size
			If FloatObject(elements[i]).value = o Then Return True
		Next
		Return False
	End

	Method Enumerator:AbstractEnumerator<FloatObject>()
		Return New FloatArrayListEnumerator(Self)
	End

	Method RemoveFloat:Bool(o:Float)
		For Local i:Int = 0 Until size
			If FloatObject(elements[i]).value = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method ToFloatArray:Float[]()
		Local arr:Float[] = New Float[size]
		For Local i:Int = 0 Until size
			arr[i] = FloatObject(elements[i]).value
		Next
		Return arr
	End

	Method GetFloat:Float(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return FloatObject(elements[index]).value
	End

	Method InsertFloat:Void(index:Int, o:Float)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = New FloatObject(o)
		size+=1
		modCount += 1
	End

	Method IndexOfFloat:Int(o:Float)
		For Local i:Int = 0 Until size
			If FloatObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOfFloat:Int(o:Float)
		For Local i:Int = size-1 To 0 Step -1
			If FloatObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method SetFloat:Float(index:Int, o:Float)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:Float = FloatObject(elements[index]).value
		If elements[index] <> Null Then
			FloatObject(elements[index]).value = o ' XXX: not sure whether we can change value on the fly
		End
		modCount += 1
		Return oldValue
	End

	Method FillFloatArray:Int(arr:Float[])
		AssertLessThan(arr.Length, size, "FloatArrayList.FillFloatArray: Array too small:")
		For Local i:Int = 0 Until size
			arr[i] = FloatObject(elements[i]).value
		Next
		Return size
	End
End

Class StringArrayList Extends ArrayList<StringObject>
Public
	Method AddString:Bool(o:String)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		elements[size] = New StringObject(o)
		size+=1
				modCount += 1
				Return True
			End

	Method ContainsString:Bool(o:String)
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = o Then Return True
		Next
		Return False
	End

	Method Enumerator:AbstractEnumerator<StringObject>()
		Return New StringArrayListEnumerator(Self)
	End

	Method RemoveString:Bool(o:String)
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method ToStringArray:String[]()
		Local arr:String[] = New String[size]
		For Local i:Int = 0 Until size
			arr[i] = StringObject(elements[i]).value
		Next
		Return arr
	End

	Method GetString:String(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return StringObject(elements[index]).value
	End

	Method InsertString:Void(index:Int, o:String)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = New StringObject(o)
		size+=1
		modCount += 1
	End

	Method IndexOfString:Int(o:String)
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOfString:Int(o:String)
		For Local i:Int = size-1 To 0 Step -1
			If StringObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method SetString:String(index:Int, o:String)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:String = StringObject(elements[index]).value
		If elements[index] <> Null Then
			StringObject(elements[index]).value = o ' XXX: not sure whether we can change value on the fly
		End
		modCount += 1
		Return oldValue
	End

	Method FillStringArray:Int(arr:String[])
		AssertLessThan(arr.Length, size, "StringArrayList.FillStringArray: Array too small:")
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = value Then
				Remove(elements[i])
				modCount += 1
				Return True
			End
		Next
		Return False
	End
End

Function QuickSort:Void(arr:Object[], left:Int, right:Int, comp:AbstractComparator, reverse:Bool = False)
	If right > left Then
		Local pivotIndex:Int = left + (right-left)/2
		Local pivotNewIndex:Int = QuickSortPartition(arr, left, right, pivotIndex, comp, reverse)
		QuickSort(arr, left, pivotNewIndex - 1, comp, reverse)
		QuickSort(arr, pivotNewIndex + 1, right, comp, reverse)
	End
End

Function QuickSortPartition:Int(arr:Object[], left:Int, right:Int, pivotIndex:Int, comp:AbstractComparator, reverse:Bool = False)
	Local pivotValue:Object = arr[pivotIndex]
	arr[pivotIndex] = arr[right]
	arr[right] = pivotValue
	Local storeIndex:Int = left, val:Object
	For Local i:Int = left Until right
		If Comparable(arr[i]) <> Null Then
			If Not reverse And Comparable(arr[i]).Compare(arr[i], pivotValue) <= 0 Or reverse And Comparable(arr[i]).Compare(arr[i], pivotValue) >= 0 Then
				val = arr[i]
				arr[i] = arr[storeIndex]
				arr[storeIndex] = val
				storeIndex += 1
			End
		Else
			If Not reverse And comp.Compare(arr[i], pivotValue) <= 0 Or reverse And comp.Compare(arr[i], pivotValue) >= 0 Then
				val = arr[i]
				arr[i] = arr[storeIndex]
				arr[storeIndex] = val
				storeIndex += 1
			End
		End
	Next
	val = arr[storeIndex]
	arr[storeIndex] = arr[right]
	arr[right] = val
	Return storeIndex
End

Strict

Import assert

#Rem
Monkey Collections Framework
Based loosely on the Java Collections Framework
#End

'''' Top level Collection stuff ''''

#Rem
	AbstractCollection
	Due to limitations with generics in Monkey, ToArray cannot work with an array of type E.  We must return Object[] instead.
#End
Class AbstractCollection<E> Abstract
Private
	' A custom comparator to use for sorting and comparing items.
	Field comparator:AbstractComparator = Null

Public
	' Property to read comparator
	Method Comparator:AbstractComparator() Property
		Return comparator
	End
	' Property to write comparator
	Method Comparator:Void(comparator:AbstractComparator) Property
		Self.comparator = comparator
	End

	Method Add:Bool(o:E) Abstract
	Method AddAll:Bool(c:AbstractCollection<E>) Abstract
	Method Clear:Void() Abstract
	Method Contains:Bool(o:E) Abstract
	Method ContainsAll:Bool(c:AbstractCollection<E>) Abstract
	Method Enumerator:AbstractEnumerator<E>() Abstract
	Method FillArray:Int(arr:Object[]) Abstract ' populates the passed array and returns the number of items filled. best used for android
	Method IsEmpty:Bool() Abstract
	Method Remove:Bool(o:E) Abstract
	Method RemoveAll:Bool(c:AbstractCollection<E>) Abstract
	Method RetainAll:Bool(c:AbstractCollection<E>) Abstract
	Method Size:Int() Property Abstract
	Method Sort:Void(reverse:Bool = False, comp:AbstractComparator = Null) Abstract
	Method ToArray:Object[]() Abstract ' creates a new array of the correct size and returns it

	Method ObjectEnumerator:AbstractEnumerator<E>()
		Return Enumerator()
	End
End

#Rem
	AbstractEnumerator
	Used in the ObjectEnumerator method for calls to EachIn.
	If retrieved and used manually, the HasPrevious/PreviousObject/Remove/First/Last methods can be called.
#End
Class AbstractEnumerator<E>
Public
	Method HasNext:Bool() Abstract
	Method HasPrevious:Bool() Abstract
	Method NextObject:E() Abstract
	Method PreviousObject:E() Abstract
	Method Remove:Void() Abstract
	Method First:Void() Abstract
	Method Last:Void() Abstract
	Method Reset:Void() Abstract
End

#Rem
	AbstractComparator
	This is a way For developers to provide a custom comparison method for sorting lists.
	It's sort of like a function pointer.
#End
Class AbstractComparator Abstract
	Method Compare:Int(o1:Object, o2:Object) Abstract

	Method CompareBool:Bool(o1:Object, o2:Object)
		Return o1 = o2 Or Compare(o1, o2) = 0
	End

	Method HashCode:Int(o:Object)
		Return 0
	End
End

#Rem
	DefaultComparator
	Implements an AbstractComparator to handle primitive wrappers and strings.
#End
Global DEFAULT_COMPARATOR:DefaultComparator = New DefaultComparator
Class DefaultComparator Extends AbstractComparator
	Method Compare:Int(o1:Object, o2:Object)
		If IntObject(o1) <> Null And IntObject(o2) <> Null Then
			If IntObject(o1).value < IntObject(o2).value Then Return -1
			If IntObject(o1).value > IntObject(o2).value Then Return 1
			Return 0
		ElseIf FloatObject(o1) <> Null And FloatObject(o2) <> Null Then
			If FloatObject(o1).value < FloatObject(o2).value Then Return -1
			If FloatObject(o1).value > FloatObject(o2).value Then Return 1
			Return 0
		ElseIf StringObject(o1) <> Null And StringObject(o2) <> Null Then
			If StringObject(o1).value < StringObject(o2).value Then Return -1
			If StringObject(o1).value > StringObject(o2).value Then Return 1
			Return 0
		End
		If o1 = o2 Then Return 0
		If o1 = Null Then Return -1
		If o2 = Null Then Return 1
		Return 0 ' don't know what to do!
	End

	Method CompareBool:Bool(o1:Object, o2:Object)
		If IntObject(o1) <> Null And IntObject(o2) <> Null Then
			Return IntObject(o1).value = IntObject(o2).value
		ElseIf FloatObject(o1) <> Null And FloatObject(o2) <> Null Then
			Return FloatObject(o1).value = FloatObject(o2).value
		ElseIf StringObject(o1) <> Null And StringObject(o2) <> Null Then
			Return StringObject(o1).value = StringObject(o2).value
		End
		Return o1 = o2
	End
End

'''' List stuff ''''

#Rem
	AbstractList
	Extends AbstractCollection to implement some of the abstract methods that apply to any kind of list.
#End
Class AbstractList<E> Extends AbstractCollection<E> Abstract
Private
	' If true, bounds checking should be performed.
#if CONFIG="debug"
	Field rangeChecking:Bool = True
#else
	Field rangeChecking:Bool = False
#end

	' A counter for modifications to the list.  Used for concurrency checks.
	Field modCount:Int = 0

	' Performs a range check.
	Method RangeCheck:Void(index:Int)
		Local size:Int = Self.Size()
		' range check doesn't use assert, for speed
		If index < 0 Or index >= size Then Error("AbstractList.RangeCheck: Index out of bounds: " + index + " is not 0<=index<" + size)
	End

Public
	' Property to read rangeChecking
	Method RangeChecking:Bool() Property
		Return rangeChecking
	End
	' Property to write rangeChecking
	Method RangeChecking:Void(rangeChecking:Bool) Property
		Self.rangeChecking = rangeChecking
	End

	Method Enumerator:AbstractEnumerator<E>()
		Return New ListEnumerator<E>(Self)
	End

	Method Get:E(index:Int) Abstract
	Method Insert:Void(index:Int, o:E) Abstract
	Method InsertAll:Bool(index:Int, c:AbstractCollection<E>) Abstract
	Method IndexOf:Int(o:E) Abstract
	Method LastIndexOf:Int(o:E) Abstract
	Method RemoveAt:E(index:Int) Abstract ' Can't overload Remove since it's an inherited method, and Monkey doesn't support that.
	Method RemoveRange:Void(fromIndex:Int, toIndex:Int) Abstract
	Method Set:E(index:Int, o:E) Abstract
End

#Rem
	ListEnumerator
	Extends AbstractEnumerator to provide support for EachIn.  Blocks concurrent modification, but allows elements to be removed on the fly.
#End
Class ListEnumerator<E> Extends AbstractEnumerator<E>
Private
	Field lst:AbstractList<E>
	Field lastIndex:Int = 0
	Field index:Int = 0
	Field expectedModCount:Int = 0
Public
	Method New(lst:AbstractList<E>)
		Self.lst = lst
		expectedModCount = lst.modCount
	End

	Method CheckConcurrency:Void()
		' for speed we don't use assert
		If lst.modCount <> expectedModCount Then Error("ListEnumerator.CheckConcurrency: Concurrent list modification")
	End

	Method HasNext:Bool()
		CheckConcurrency()
		Return index < lst.Size
	End

	Method HasPrevious:Bool()
		CheckConcurrency()
		Return index > 0
	End

	Method NextObject:E()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex)
	End

	Method PreviousObject:E()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex)
	End

	Method Remove:Void()
		CheckConcurrency()
		lst.RemoveAt(lastIndex)
		If lastIndex < index Then index -= 1
		lastIndex = -1
		expectedModCount = lst.modCount
	End

	Method First:Void()
		CheckConcurrency()
		index = 0
	End

	Method Last:Void()
		CheckConcurrency()
		index = lst.Size
	End

	Method Reset:Void()
		index = 0
		expectedModCount = lst.modCount
	End
End

#Rem
	ArrayListEnumerator
	Extends ListEnumerator to avoid some method calls.
#End
Class ArrayListEnumerator<E> Extends ListEnumerator<E>
	Field alst:ArrayList<E>

	Method New(lst:ArrayList<E>)
		Super.New(lst)
		Self.alst = lst
		expectedModCount = alst.modCount
	End

	Method HasNext:Bool()
		CheckConcurrency()
		Return index < alst.size
	End

	Method NextObject:E()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return alst.elements[lastIndex]
	End

	Method PreviousObject:E()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return alst.elements[lastIndex]
	End

	Method Last:Void()
		CheckConcurrency()
		index = alst.size
	End
End

Class IntListEnumerator Extends ListEnumerator<IntObject>
Public
	Method New(lst:AbstractList<IntObject>)
		Super.New(lst)
	End

	Method NextInt:Int()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex).value
	End

	Method PreviousInt:Int()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex).value
	End
End

Class FloatListEnumerator Extends ListEnumerator<FloatObject>
Public
	Method New(lst:AbstractList<FloatObject>)
		Super.New(lst)
	End

	Method NextFloat:Float()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex).value
	End

	Method PreviousFloat:Float()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex).value
	End
End

Class StringListEnumerator Extends ListEnumerator<StringObject>
Public
	Method New(lst:AbstractList<IntObject>)
		Super.New(lst)
	End

	Method NextString:String()
		CheckConcurrency()
		lastIndex = index
		index += 1
		Return lst.Get(lastIndex).value
	End

	Method PreviousString:String()
		CheckConcurrency()
		index -= 1
		lastIndex = index
		Return lst.Get(lastIndex).value
	End
End

#Rem
	ArrayList
	Concrete implementation of AbstractList that uses a dynamically sized array to store elements.
	Has best performance when it is initialised with a capacity large enough to hold the expected number of elements.
#End
Class ArrayList<E> Extends AbstractList<E>
Private
	' fields
	Field elements:Object[]
	Field size:Int = 0

	' resizes the elements array if necessary to ensure it can fit minCapacity elements
	Method EnsureCapacity:Void(minCapacity:Int)
		Local oldCapacity:Int = elements.Length
		If minCapacity > oldCapacity Then
			Local newCapacity:Int = (oldCapacity * 3) / 2 + 1
			If newCapacity < minCapacity Then newCapacity = minCapacity
			elements = elements.Resize(newCapacity)
			modCount += 1
		End
	End

	Method RangeCheck:Void(index:Int)
		' range check doesn't use assert, for speed
		If index < 0 Or index >= size Then Error("ArrayList.RangeCheck: Index out of bounds: " + index + " is not 0<=index<" + size)
	End

	Field tempArr:Object[] = New Object[128] ' temp array used for internal call to ToArray (so we don't create an object)

Public
	' constructors
	Method New()
		Self.elements = New Object[10]
	End

	Method New(initialCapacity:Int)
		AssertGreaterThanOrEqual(initialCapacity, 0, "ArrayList.New: Illegal Capacity:")
		Self.elements = New Object[initialCapacity]
	End

	Method New(c:AbstractCollection<E>)
		elements = c.ToArray()
		size = elements.Length
	End

	'implements AbstractCollection

	Method Add:Bool(o:E)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		elements[size] = o
		size+=1
		modCount += 1
		Return True
	End

	Method AddAll:Bool(c:AbstractCollection<E>)
		If c.IsEmpty() Then Return False
		Local newItemCount:Int = c.Size
		If size + newItemCount > elements.Length Then EnsureCapacity(size+newItemCount)
		If tempArr.Length < newItemCount Then tempArr = tempArr.Resize(newItemCount)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			elements[size] = tempArr[i]
			size+=1
		End
		modCount += 1
		Return newItemCount <> 0
	End

	Method Clear:Void()
		For Local i:Int = 0 Until size
			elements[i] = Null
		Next
		modCount += 1
		size = 0
	End

	Method Contains:Bool(o:E)
		For Local i:Int = 0 Until size
			If elements[i] = o Then Return True
		Next
		Return False
	End

	Method ContainsAll:Bool(c:AbstractCollection<E>)
		If c.IsEmpty() Then Return True
		If tempArr.Length < c.Size Then tempArr = tempArr.Resize(c.Size)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			If Not Self.Contains(E(tempArr[i])) Then Return False
		Next
		Return True
	End

	Method FillArray:Int(arr:Object[])
		AssertGreaterThanOrEqual(arr.Length, size, "ArrayList.FillArray: Array too small:")
		For Local i:Int = 0 Until size
			arr[i] = elements[i]
		Next
		Return size
	End

	Method IsEmpty:Bool()
		Return size = 0
	End

	Method Remove:Bool(o:E)
		For Local i:Int = 0 Until size
			If elements[i] = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method RemoveAll:Bool(c:AbstractCollection<E>)
		If c.IsEmpty() Then Return False
		Local modified:Bool = False
		If tempArr.Length < c.Size Then tempArr = tempArr.Resize(c.Size)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			If Self.Contains(E(tempArr[i])) Then
				Self.Remove(E(tempArr[i]))
			End
		Next
		If modified Then modCount += 1
		Return modified
	End

	Method RetainAll:Bool(c:AbstractCollection<E>)
		Local modified:Bool = False
		If tempArr.Length < c.Size Then tempArr = tempArr.Resize(c.Size)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = 0 Until len
			If Not Self.Contains(E(tempArr[i])) Then
				Self.Remove(E(tempArr[i]))
				modified = True
			End
		Next
		If modified Then modCount += 1
		Return modified
	End

	Method Size:Int() Property
		Return size
	End

	Method Sort:Void(reverse:Bool = False, comp:AbstractComparator = Null)
		If size <= 1 Then Return ' can't sort 0 or 1 elements
		If comp = Null Then comp = Self.Comparator
		If comp = Null Then comp = DEFAULT_COMPARATOR
		QuickSort(elements, 0, size-1, comp, reverse)
		modCount += 1
	End

	Method ToArray:Object[]()
		Local arr:Object[] = New Object[size]
		For Local i:Int = 0 Until size
			arr[i] = elements[i]
		Next
		Return arr
	End

	' implements AbstractList

	Method Get:E(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return E(elements[index])
	End

	Method Insert:Void(index:Int, o:E)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = o
		size+=1
		modCount += 1
	End

	Method InsertAll:Bool(index:Int, c:AbstractCollection<E>)
		Local newItemCount:Int = c.Size
		If newItemCount = 0 Then Return False
		If size + newItemCount > elements.Length Then EnsureCapacity(size+newItemCount)
		If tempArr.Length < newItemCount Then tempArr = tempArr.Resize(newItemCount)
		Local len:Int = c.FillArray(tempArr)
		For Local i:Int = size - 1 To index Step -1
			elements[i+newItemCount] = elements[i]
		Next
		For Local i:Int = 0 Until newItemCount
			elements[index+i] = tempArr[i]
		End
		size += newItemCount
		modCount += 1
		Return newItemCount <> 0
	End

	Method IndexOf:Int(o:E)
		For Local i:Int = 0 Until size
			If elements[i] = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOf:Int(o:E)
		For Local i:Int = size-1 To 0 Step -1
			If elements[i] = o Then Return i
		Next
		Return -1
	End

	Method RemoveAt:E(index:Int)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:E = E(elements[index])
		For Local i:Int = index Until size-1
			elements[i] = elements[i+1]
		Next
		elements[size-1] = Null
		size-=1
		modCount += 1
		Return oldValue
	End

	Method RemoveRange:Void(fromIndex:Int, toIndex:Int)
		AssertLessThanOrEqual(fromIndex, toIndex, "ArrayList.RemoveRange: fromIndex > toIndex:")
		If rangeChecking Then
			RangeCheck(fromIndex)
			RangeCheck(toIndex)
		End
		For Local i:Int = 0 Until toIndex - fromIndex
			RemoveAt(fromIndex)
		Next
	End

	Method Set:E(index:Int, o:E)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:E = E(elements[index])
		elements[index] = o
		modCount += 1
		Return oldValue
	End

	Method Enumerator:AbstractEnumerator<E>()
		Return New ArrayListEnumerator<E>(Self)
	End
End

' ArrayList wrapper classes

Class IntArrayList Extends ArrayList<IntObject>
Public
	Method AddInt:Bool(o:Int)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		elements[size] = New IntObject(o)
		size+=1
		modCount += 1
		Return True
	End

	Method ContainsInt:Bool(o:Int)
		For Local i:Int = 0 Until size
			If IntObject(elements[i]).value = o Then Return True
		Next
		Return False
	End

	Method Enumerator:AbstractEnumerator<IntObject>()
		Return New IntArrayListEnumerator(Self)
	End

	Method RemoveInt:Bool(o:Int)
		For Local i:Int = 0 Until size
			If IntObject(elements[i]).value = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method ToIntArray:Int[]()
		Local arr:Int[] = New Int[size]
		For Local i:Int = 0 Until size
			arr[i] = IntObject(elements[i]).value
		Next
		Return arr
	End

	Method GetInt:Int(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return IntObject(elements[index]).value
	End

	Method InsertInt:Void(index:Int, o:Int)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = New IntObject(o)
		size+=1
		modCount += 1
	End

	Method IndexOfInt:Int(o:Int)
		For Local i:Int = 0 Until size
			If IntObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOfInt:Int(o:E)
		For Local i:Int = size-1 To 0 Step -1
			If IntObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method SetInt:Int(index:Int, o:Int)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:Int = IntObject(elements[index]).value
		If elements[index] <> Null Then
			IntObject(elements[index]).value = o ' XXX: not sure whether we can change value on the fly
		End
		modCount += 1
		Return oldValue
	End

	Method FillIntArray:Int(arr:Int[])
		AssertLessThan(arr.Length, size, "IntArrayList.FillIntArray: Array too small:")
		For Local i:Int = 0 Until size
			arr[i] = IntObject(elements[i]).value
		Next
		Return size
	End
End

Class FloatArrayList Extends ArrayList<FloatObject>
Public
	Method AddFloat:Bool(o:Float)
		If size + 1 > elements.Length Then EnsureCapacity(size + 1)
		elements[size] = New FloatObject(o)
		size += 1
		modCount += 1
		Return True
	End

	Method ContainsFloat:Bool(o:Float)
		For Local i:Int = 0 Until size
			If FloatObject(elements[i]).value = o Then Return True
		Next
		Return False
	End

	Method Enumerator:AbstractEnumerator<FloatObject>()
		Return New FloatArrayListEnumerator(Self)
	End

	Method RemoveFloat:Bool(o:Float)
		For Local i:Int = 0 Until size
			If FloatObject(elements[i]).value = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method ToFloatArray:Float[]()
		Local arr:Float[] = New Float[size]
		For Local i:Int = 0 Until size
			arr[i] = FloatObject(elements[i]).value
		Next
		Return arr
	End

	Method GetFloat:Float(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return FloatObject(elements[index]).value
	End

	Method InsertFloat:Void(index:Int, o:Float)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = New FloatObject(o)
		size+=1
		modCount += 1
	End

	Method IndexOfFloat:Int(o:Float)
		For Local i:Int = 0 Until size
			If FloatObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOfFloat:Int(o:Float)
		For Local i:Int = size-1 To 0 Step -1
			If FloatObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method SetFloat:Float(index:Int, o:Float)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:Float = FloatObject(elements[index]).value
		If elements[index] <> Null Then
			FloatObject(elements[index]).value = o ' XXX: not sure whether we can change value on the fly
		End
		modCount += 1
		Return oldValue
	End

	Method FillFloatArray:Int(arr:Float[])
		AssertLessThan(arr.Length, size, "FloatArrayList.FillFloatArray: Array too small:")
		For Local i:Int = 0 Until size
			arr[i] = FloatObject(elements[i]).value
		Next
		Return size
	End
End

Class StringArrayList Extends ArrayList<StringObject>
Public
	Method AddString:Bool(o:String)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		elements[size] = New StringObject(o)
		size+=1
				modCount += 1
				Return True
			End

	Method ContainsString:Bool(o:String)
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = o Then Return True
		Next
		Return False
	End

	Method Enumerator:AbstractEnumerator<StringObject>()
		Return New StringArrayListEnumerator(Self)
	End

	Method RemoveString:Bool(o:String)
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = o Then
				RemoveAt(i)
				modCount += 1
				Return True
			End
		Next
		Return False
	End

	Method ToStringArray:String[]()
		Local arr:String[] = New String[size]
		For Local i:Int = 0 Until size
			arr[i] = StringObject(elements[i]).value
		Next
		Return arr
	End

	Method GetString:String(index:Int)
		If rangeChecking Then RangeCheck(index)
		Return StringObject(elements[index]).value
	End

	Method InsertString:Void(index:Int, o:String)
		If rangeChecking Then RangeCheck(index)
		If size+1 > elements.Length Then EnsureCapacity(size+1)
		For Local i:Int = size Until index Step -1
			elements[i] = elements[i-1]
		Next
		elements[index] = New StringObject(o)
		size+=1
		modCount += 1
	End

	Method IndexOfString:Int(o:String)
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method LastIndexOfString:Int(o:String)
		For Local i:Int = size-1 To 0 Step -1
			If StringObject(elements[i]).value = o Then Return i
		Next
		Return -1
	End

	Method SetString:String(index:Int, o:String)
		If rangeChecking Then RangeCheck(index)
		Local oldValue:String = StringObject(elements[index]).value
		If elements[index] <> Null Then
			StringObject(elements[index]).value = o ' XXX: not sure whether we can change value on the fly
		End
		modCount += 1
		Return oldValue
	End

	Method FillStringArray:Int(arr:String[])
		AssertLessThan(arr.Length, size, "StringArrayList.FillStringArray: Array too small:")
		For Local i:Int = 0 Until size
			If StringObject(elements[i]).value = value Then
				Remove(elements[i])
				modCount += 1
				Return True
			End
		Next
		Return False
	End
End

Function QuickSort:Void(arr:Object[], left:Int, right:Int, comp:AbstractComparator, reverse:Bool = False)
	If right > left Then
		Local pivotIndex:Int = left + (right-left)/2
		Local pivotNewIndex:Int = QuickSortPartition(arr, left, right, pivotIndex, comp, reverse)
		QuickSort(arr, left, pivotNewIndex - 1, comp, reverse)
		QuickSort(arr, pivotNewIndex + 1, right, comp, reverse)
	End
End

Function QuickSortPartition:Int(arr:Object[], left:Int, right:Int, pivotIndex:Int, comp:AbstractComparator, reverse:Bool = False)
	Local pivotValue:Object = arr[pivotIndex]
	arr[pivotIndex] = arr[right]
	arr[right] = pivotValue
	Local storeIndex:Int = left, val:Object
	For Local i:Int = left Until right
		If Not reverse And comp.Compare(arr[i], pivotValue) <= 0 Or reverse And comp.Compare(arr[i], pivotValue) >= 0 Then
			val = arr[i]
			arr[i] = arr[storeIndex]
			arr[storeIndex] = val
			storeIndex += 1
		End
	Next
	val = arr[storeIndex]
	arr[storeIndex] = arr[right]
	arr[right] = val
	Return storeIndex
End


Fixed FlxG.BgColor null pointer error

Fixed FlxG.BgColor null pointer error

Strict

Import flxgame
Import flxcamera
Import flxu

Import plugin.timermanager

Class FlxG

	Const LIBRARY_NAME:String = "Monkey flixel"

	Const LIBRARY_MAJOR_VERSION:Int = 1

	Const LIBRARY_MINOR_VERSION:Int = 0

	Const DATA_PREFIX:String = "flx_"

	Const RED:Int = $FFFF0012

	Const GREEN:Int = $FF00F225

	Const BLUE:Int = $FF0090E9

	Const PINK:Int = $FFF01EFF

	Const WHITE:Int = $FFFFFFFF

	Const BLACK:Int = $FF000000

	Global deviceWidth:Int

	Global deviceHeight:Int

	Global width:Int

	Global height:Int

	Global cameras:Stack<FlxCamera>

	Global camera:FlxCamera

	Global plugins:Stack<FlxBasic>

	Global elapsed:Float

	Global timeScale:Float

	Global visualDebug:Bool

	Global globalSeed:Int

	Global framerate:Int

	Global _deviceScaleFactorX:Float = 1

	Global _deviceScaleFactorY:Float = 1

	Global _bgColor:FlxColor = FlxColor.ARGB(FlxG.BLACK)

	Global _game:FlxGame

	Global _lastDrawingColor:Int

	Global _lastDrawingBlend:Int

Public
	Function Random:Float()
		FlxG.globalSeed = (FlxG.globalSeed * 1664525 + 1013904223)|0
		Return FlxU.Srand(FlxG.globalSeed)
	End Function

	Function GetRandom:FlxBasic(objects:FlxBasic[], startIndex:Int = 0, length:Int = 0)
		If (objects <> Null) Then
			Local l:Int = length

			If (l = 0 Or l > objects.Length() - startIndex) Then
				l = objects.Length() - startIndex
			End if

			If (l > 0) Return objects[startIndex + int(FlxG.Random()*l)]
		End If

		Return Null
	End Function

	#Rem
	Function GetRandom:Object(objects:Object[], startIndex:Int = 0, length:Int = 0)
		GetRandom is not currently supported in Monkey. Use FlxArray.GetSafeRandom method
	End Function
	#End

	Function AddCamera:FlxCamera(newCamera:FlxCamera)
		FlxG.cameras.Push(newCamera)
		Return newCamera
	End Function

	Function RemoveCamera:Void(camera:FlxCamera, destroy:Bool = True)
		FlxG.cameras.RemoveEach(camera)
		If (destroy) camera.Destroy()
	End Function

	Function ResetCameras:Void(newCamera:FlxCamera = Null)
		Local cam:FlxCamera
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			cam = FlxG.cameras.Get(i)
			cam.Destroy()
			i+=1
		Wend

		FlxG.cameras.Clear()
		If (newCamera = Null) newCamera = New FlxCamera(0, 0, FlxG.width, FlxG.height)

		FlxG.camera = FlxG.AddCamera(newCamera)
	End Function

	Function Flash:Void(color:Int = FlxG.WHITE, duration:Float = 1, onComplete:FlxFunction = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).Flash(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Fade:Void(color:Int = FlxG.BLACK, duration:Float = 1, onComplete:FlxFunction = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).Fade(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Shake:Void(intensity:Float = 0.05, duration:Float = 0.5, onComplete:FlxFunction = Null, force:Bool = True, direction:Int = FlxCamera.SHAKE_BOTH_AXES)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).Shake(intensity, duration, onComplete, force, direction)
			i+=1
		Wend
	End Function

	Function BgColor:Int()
		If (FlxG.camera = Null Or Not FlxG.camera.alive) Return FlxG._bgColor.argb
		Return FlxG.camera.BgColor
	End Function

	Function BgColor:Void(color:Int)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).BgColor = color
			i+=1
		Wend

		FlxG._bgColor.SetARGB(color)
	End Function

	Function AddPlugin:FlxBasic(plugin:FlxBasic)
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			If (pluginList.Get(i).ToString() = plugin.ToString()) Then
				Return plugin
			End If

			i+=1
		Wend

		pluginList.Push(plugin)
		Return plugin
	End Function

	Function GetPlugin:FlxBasic(creator:FlxClass)
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local plugin:FlxBasic
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (creator.InstanceOf(plugin)) Return plugin

			i+=1
		Wend

		Return Null
	End Function

	Function RemovePlugin:FlxBasic(plugin:FlxBasic)
		plugins.RemoveEach(plugin)
		Return plugin
	End Function

	Function RemovePluginType:Bool(creator:FlxClass)
		Local results:Bool = False
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = pluginList.Length() - 1

		While(i >= 0)
			If (creator.InstanceOf(pluginList.Get(i))) Then
				pluginList.Remove(i)
				results = True
			End If

			i-=1
		Wend

		Return results
	End Function

	Function Init:Void(game:FlxGame, width:Int, height:Int, zoom:Float)
		FlxG._game = game
		FlxG.width = width
		FlxG.height = height

		FlxCamera.defaultZoom = zoom
		FlxG.cameras = New Stack<FlxCamera>()

		plugins = New Stack<FlxBasic>
		AddPlugin(New TimerManager())
	End Function

	Function Reset:Void()
		FlxG.timeScale = 1
		FlxG.elapsed = 0
		FlxG.globalSeed = Rnd(1, 10000000)
	End Function

	Function UpdateCameras:Void()
		Local cam:FlxCamera
		Local cams:Stack<FlxCamera> = FlxG.cameras
		Local i:Int = 0
		Local l:Int = cams.Length()

		While(i < l)
			cam = cams.Get(i)
			If (cam <> Null And cam.exists And cam.active) cam.Update()
			i+=1
		Wend
	End Function

	Function UpdatePlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.active) plugin.Update()
			i+=1
		Wend
	End Function

	Function DrawPlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.visible) plugin.Draw()
			i+=1
		Wend
	End Function

End Class

Strict

Import flxgame
Import flxcamera
Import flxu

Import plugin.timermanager

Class FlxG

	Const LIBRARY_NAME:String = "Monkey flixel"

	Const LIBRARY_MAJOR_VERSION:Int = 1

	Const LIBRARY_MINOR_VERSION:Int = 0

	Const DATA_PREFIX:String = "flx_"

	Const RED:Int = $FFFF0012

	Const GREEN:Int = $FF00F225

	Const BLUE:Int = $FF0090E9

	Const PINK:Int = $FFF01EFF

	Const WHITE:Int = $FFFFFFFF

	Const BLACK:Int = $FF000000

	Global deviceWidth:Int

	Global deviceHeight:Int

	Global width:Int

	Global height:Int

	Global cameras:Stack<FlxCamera>

	Global camera:FlxCamera

	Global plugins:Stack<FlxBasic>

	Global elapsed:Float

	Global timeScale:Float

	Global visualDebug:Bool

	Global globalSeed:Int

	Global framerate:Int

	Global _deviceScaleFactorX:Float = 1

	Global _deviceScaleFactorY:Float = 1

	Global _bgColor:FlxColor = FlxColor.ARGB(FlxG.BLACK)

	Global _game:FlxGame

	Global _lastDrawingColor:Int

	Global _lastDrawingBlend:Int

Public
	Function Random:Float()
		FlxG.globalSeed = (FlxG.globalSeed * 1664525 + 1013904223)|0
		Return FlxU.Srand(FlxG.globalSeed)
	End Function

	Function GetRandom:FlxBasic(objects:FlxBasic[], startIndex:Int = 0, length:Int = 0)
		If (objects <> Null) Then
			Local l:Int = length

			If (l = 0 Or l > objects.Length() - startIndex) Then
				l = objects.Length() - startIndex
			End if

			If (l > 0) Return objects[startIndex + int(FlxG.Random()*l)]
		End If

		Return Null
	End Function

	#Rem
	Function GetRandom:Object(objects:Object[], startIndex:Int = 0, length:Int = 0)
		GetRandom is not currently supported in Monkey. Use FlxArray.GetSafeRandom method
	End Function
	#End

	Function AddCamera:FlxCamera(newCamera:FlxCamera)
		FlxG.cameras.Push(newCamera)
		Return newCamera
	End Function

	Function RemoveCamera:Void(camera:FlxCamera, destroy:Bool = True)
		FlxG.cameras.RemoveEach(camera)
		If (destroy) camera.Destroy()
	End Function

	Function ResetCameras:Void(newCamera:FlxCamera = Null)
		Local cam:FlxCamera
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			cam = FlxG.cameras.Get(i)
			cam.Destroy()
			i+=1
		Wend

		FlxG.cameras.Clear()
		If (newCamera = Null) newCamera = New FlxCamera(0, 0, FlxG.width, FlxG.height)

		FlxG.camera = FlxG.AddCamera(newCamera)
	End Function

	Function Flash:Void(color:Int = FlxG.WHITE, duration:Float = 1, onComplete:FlxFunction = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).Flash(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Fade:Void(color:Int = FlxG.BLACK, duration:Float = 1, onComplete:FlxFunction = Null, force:Bool = False)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).Fade(color, duration, onComplete, force)
			i+=1
		Wend
	End Function

	Function Shake:Void(intensity:Float = 0.05, duration:Float = 0.5, onComplete:FlxFunction = Null, force:Bool = True, direction:Int = FlxCamera.SHAKE_BOTH_AXES)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).Shake(intensity, duration, onComplete, force, direction)
			i+=1
		Wend
	End Function

	Function BgColor:Int()
		If (FlxG.camera = Null) Return FlxG._bgColor.argb
		Return FlxG.camera.BgColor
	End Function

	Function BgColor:Void(color:Int)
		Local i:Int = 0
		Local l:Int = FlxG.cameras.Length()

		While(i < l)
			FlxG.cameras.Get(i).BgColor = color
			i+=1
		Wend

		FlxG._bgColor.SetARGB(color)
	End Function

	Function AddPlugin:FlxBasic(plugin:FlxBasic)
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			If (pluginList.Get(i).ToString() = plugin.ToString()) Then
				Return plugin
			End If

			i+=1
		Wend

		pluginList.Push(plugin)
		Return plugin
	End Function

	Function GetPlugin:FlxBasic(creator:FlxClass)
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local plugin:FlxBasic
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (creator.InstanceOf(plugin)) Return plugin

			i+=1
		Wend

		Return Null
	End Function

	Function RemovePlugin:FlxBasic(plugin:FlxBasic)
		plugins.RemoveEach(plugin)
		Return plugin
	End Function

	Function RemovePluginType:Bool(creator:FlxClass)
		Local results:Bool = False
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = pluginList.Length() - 1

		While(i >= 0)
			If (creator.InstanceOf(pluginList.Get(i))) Then
				pluginList.Remove(i)
				results = True
			End If

			i-=1
		Wend

		Return results
	End Function

	Function Init:Void(game:FlxGame, width:Int, height:Int, zoom:Float)
		FlxG._game = game
		FlxG.width = width
		FlxG.height = height

		FlxCamera.defaultZoom = zoom
		FlxG.cameras = New Stack<FlxCamera>()

		plugins = New Stack<FlxBasic>
		AddPlugin(New TimerManager())
	End Function

	Function Reset:Void()
		FlxG.timeScale = 1
		FlxG.elapsed = 0
		FlxG.globalSeed = Rnd(1, 10000000)
	End Function

	Function UpdateCameras:Void()
		Local cam:FlxCamera
		Local cams:Stack<FlxCamera> = FlxG.cameras
		Local i:Int = 0
		Local l:Int = cams.Length()

		While(i < l)
			cam = cams.Get(i)
			If (cam <> Null And cam.exists And cam.active) cam.Update()
			i+=1
		Wend
	End Function

	Function UpdatePlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.active) plugin.Update()
			i+=1
		Wend
	End Function

	Function DrawPlugins:Void()
		Local plugin:FlxBasic
		Local pluginList:Stack<FlxBasic> = FlxG.plugins
		Local i:Int = 0
		Local l:Int = pluginList.Length()

		While(i < l)
			plugin = pluginList.Get(i)
			If (plugin.exists And plugin.visible) plugin.Draw()
			i+=1
		Wend
	End Function

End Class


FlxGame ignore FlxCamera.exists fixed #60

Strict

Import mojo
Import reflection

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

#If TARGET = "xna" Or TARGET = "psm"
	Import "data/beep_flx.wav"
#ElseIf TARGET = "html5"
	Import "data/beep_flx.ogg"
	Import "data/beep_flx.mp3"
#ElseIf TARGET = "glfw" Or TARGET = "bmax"
	Import "data/beep_flx.ogg"
#Else
	Import "data/beep_flx.mp3"
#End

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Float

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:ClassInfo

	Field _created:Bool

	Field _total:Int

	Field _accumulator:Float

	Field _step:Float

	Field _maxAccumulation:Float

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updaterate:Int

	Field _framerate:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:ClassInfo, zoom:Float = 1, updaterate:Int = 60, framerate:Int = 30, useSystemCursor:Bool = False)
		Local classObject:GlobalInfo

		For Local classInfo:ClassInfo = EachIn GetClasses()
			classObject = classInfo.GetGlobal("ClassObject", False)
			If (classObject = Null) classObject = classInfo.GetGlobal("ClassInfo", False)

			If (classObject <> Null) Then
				classObject.SetValue(classInfo)
			End If
		Next

		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 45
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate
		FlxG.Updaterate = updaterate
		_total = 0

		_state = Null

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor

		If (Not useSystemCursor) HideMouse()

		forceDebugger = False
		_debuggerUp = False

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False
	End Method

	Method OnCreate:Int()
		_InitData()
		_Step()

		#Rem
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		#End
		Return 0
	End Method

	Method OnUpdate:Int()
		FlxG.UpdateDevice()

	#If TARGET <> "ios" And TARGET <> "android" And TARGET <> "psm"
		If (useSoundHotKeys) Then
			If (KeyHit(KEY_0)) Then
				FlxG.Mute = Not FlxG.Mute

				If (FlxG.VolumeHandler <> Null) Then
					If (FlxG.Mute) Then
						FlxG.VolumeHandler.OnVolumeChange(0)
					Else
						FlxG.VolumeHandler.OnVolumeChange(FlxG.Volume())
					End If
				End If

				_ShowSoundTray()
			End If

			If (KeyHit(KEY_MINUS)) Then
				FlxG.Mute = False
				FlxG.Volume(FlxG.Volume() - .1)
				_ShowSoundTray()
			End If

			If (KeyHit(KEY_EQUALS)) Then
				FlxG.Mute = False
				FlxG.Volume(FlxG.Volume() + .1)
				_ShowSoundTray()
			End If
		End If

		_UpdateSoundTray(_step)
	#End

		Return 0
	End Method

	Method OnRender:Int()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Local mark:Int = Millisecs()
		Local elapsedMS:Int = mark - _total
		_total = mark

		If (_debugger <> Null) Then
			'TODO!
		Else
			_accumulator += elapsedMS

			if (_accumulator > _maxAccumulation) Then
				_accumulator = _maxAccumulation
			End If

			While (_accumulator >= _step)
				_Step()
				_accumulator -= _step
			Wend
		End If

		FlxBasic._VisibleCount = 0

		_Draw()

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And Not useSystemCursor) Then
			HideMouse()
		End If

		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play("beep" + FlxG.DATA_SUFFIX)
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()
		FlxG.ResetInput()
		FlxG.DestroySounds()

		If (_debugger <> Null) Then
			'TODO!
		End If

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()

		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.NewInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

			If (_debugger <> Null) Then
				'TODO
			End If

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

			If (_debugger <> Null) Then
				'TODO
			End If
		End If

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _Update:Void()
		FlxG.Elapsed = FlxG.TimeScale * (_step / 1000.0)

		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()

		If (FlxG.Tweener.active And FlxG.Tweener.HasTween) Then
			FlxG.Tweener.UpdateTweens()
		End If

		FlxG.UpdateCameras()

		If (_debuggerUp) Then
			'TODO!
		End If
	End Method

	Method _UpdateSoundTray:Void(ms:Int)
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= ms / 1000.0

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (ms / 1000.0 * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Draw:Void()
		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)

	#If TARGET <> "ios" And TARGET <> "android" And TARGET <> "psm"
		If( Not FlxG.Mobile) Then
			PushMatrix()
		End If
	#End

		Translate(FlxG._DeviceOffsetX, FlxG._DeviceOffsetY)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG._CurrentCamera = FlxG.Cameras.Get(i)

			If (FlxG._CurrentCamera <> Null And FlxG._CurrentCamera.active And FlxG._CurrentCamera.exists And FlxG._CurrentCamera.visible) Then
				FlxG._CurrentCamera.DrawFX() 'not really draw. Only calculation
				FlxG._CurrentCamera.Lock()
				_state.Draw()
				FlxG.DrawPlugins()
				FlxG._CurrentCamera.Unlock()
			End If

			i+=1
		Wend

	#If TARGET <> "ios" And TARGET <> "android" And TARGET <> "psm"
		If( Not FlxG.Mobile) Then
			PopMatrix()
			_DrawSoundTray()
			FlxG.Mouse.Draw()
		End If
	#End
	End Method

	Method _DrawSoundTray:Void()
		If (_soundTrayVisible) Then
			Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
			If (FlxG.Mute) globalVolume = 0

			PushMatrix()

			Transform(FlxG._DeviceScaleFactorX, 0, 0, FlxG._DeviceScaleFactorY, ( (FlxG.DeviceWidth - _soundTrayWidth * FlxG._DeviceScaleFactorX) * 0.5), _soundTrayY)

			If (FlxG._LastDrawingAlpha <> .5) Then
				SetAlpha(.5)
				FlxG._LastDrawingAlpha = .5
			End If

			If (FlxG._LastDrawingColor <> FlxG.BLACK) Then
				SetColor(0, 0, 0)
			End If

			DrawRect(0, 0, _soundTrayWidth, _soundTrayHeight)

			SetColor(255, 255, 255)
			FlxG._LastDrawingColor = FlxG.WHITE

			Local bx:Int = 20
			Local by:Int = 28

			Local i:Int = 0
			While (i < 10)
				If (i < globalVolume) Then
					If (FlxG._LastDrawingAlpha <> 1) Then
						SetAlpha(1)
						FlxG._LastDrawingAlpha = 1
					End If

				Else
					If (FlxG._LastDrawingAlpha <> .5) Then
						SetAlpha(.5)
						FlxG._LastDrawingAlpha = .5
					End If
				End If

				DrawRect(bx, by, 8, i * 2)

				bx += 12
				by -= 2
				i += 1
			Wend

			'_soundTrayLabel.Draw()

			PopMatrix()
		End If
	End Method

	Method _Reset:Void()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Seed = SystemMillisecs()
		FlxG.UpdateDevice()
	End Method

	Method _ResetFramerate:Void()
		SetUpdateRate(FlxG.Framerate)

		_step = 1000.0 / FlxG.Updaterate
		_maxAccumulation = 2000.0 / FlxG.Framerate - 1

		If (_maxAccumulation < _step) Then
			_maxAccumulation = _step
		End If

		_updaterate = FlxG.Updaterate
		_framerate = FlxG.Framerate
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) +  FlxG.DATA_SUFFIX + ".png")
		Next

		FlxAssetsManager.AddImage("default" + FlxG.DATA_SUFFIX, "default" +  FlxG.DATA_SUFFIX + ".png")
		FlxAssetsManager.AddImage("button" + FlxG.DATA_SUFFIX, "button" + FlxG.DATA_SUFFIX + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor("cursor" + FlxG.DATA_SUFFIX, "cursor" + FlxG.DATA_SUFFIX + ".png")

		FlxAssetsManager.AddSound("beep" + FlxG.DATA_SUFFIX, "beep" + FlxG.DATA_SUFFIX + "." + FlxSound.GetValidExt())

		Local contentInitTrigger:FunctionInfo

		For Local classInfo:ClassInfo = EachIn GetClasses()
			contentInitTrigger = classInfo.GetFunction("OnContentInit",[], False)
			If (contentInitTrigger <> Null) contentInitTrigger.Invoke([])
		Next

		Self.OnContentInit()
	End Method
End Class

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (_lastUpdateTime = 0) Then
			_lastUpdateTime = Millisecs()
		ElseIf (Millisecs() - _lastUpdateTime >= 1000) Then
			FPS = _FPSCounter
			_lastUpdateTime = Millisecs()
			_FPSCounter = 0
		End If

		_FPSCounter += 1
	End Method

	Method Reset:Void()
		FPS = FlxG.Framerate
		_FPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class

Strict

Import mojo
Import reflection

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

#If TARGET = "xna" Or TARGET = "psm"
	Import "data/beep_flx.wav"
#ElseIf TARGET = "html5"
	Import "data/beep_flx.ogg"
	Import "data/beep_flx.mp3"
#ElseIf TARGET = "glfw" Or TARGET = "bmax"
	Import "data/beep_flx.ogg"
#Else
	Import "data/beep_flx.mp3"
#End

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Float

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:ClassInfo

	Field _created:Bool

	Field _total:Int

	Field _accumulator:Float

	Field _step:Float

	Field _maxAccumulation:Float

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updaterate:Int

	Field _framerate:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:ClassInfo, zoom:Float = 1, updaterate:Int = 60, framerate:Int = 30, useSystemCursor:Bool = False)
		Local classObject:GlobalInfo

		For Local classInfo:ClassInfo = EachIn GetClasses()
			classObject = classInfo.GetGlobal("ClassObject", False)
			If (classObject = Null) classObject = classInfo.GetGlobal("ClassInfo", False)

			If (classObject <> Null) Then
				classObject.SetValue(classInfo)
			End If
		Next

		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 45
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate
		FlxG.Updaterate = updaterate
		_total = 0

		_state = Null

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor

		If (Not useSystemCursor) HideMouse()

		forceDebugger = False
		_debuggerUp = False

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False
	End Method

	Method OnCreate:Int()
		_InitData()
		_Step()

		#Rem
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		#End
		Return 0
	End Method

	Method OnUpdate:Int()
		FlxG.UpdateDevice()

	#If TARGET <> "ios" And TARGET <> "android" And TARGET <> "psm"
		If (useSoundHotKeys) Then
			If (KeyHit(KEY_0)) Then
				FlxG.Mute = Not FlxG.Mute

				If (FlxG.VolumeHandler <> Null) Then
					If (FlxG.Mute) Then
						FlxG.VolumeHandler.OnVolumeChange(0)
					Else
						FlxG.VolumeHandler.OnVolumeChange(FlxG.Volume())
					End If
				End If

				_ShowSoundTray()
			End If

			If (KeyHit(KEY_MINUS)) Then
				FlxG.Mute = False
				FlxG.Volume(FlxG.Volume() - .1)
				_ShowSoundTray()
			End If

			If (KeyHit(KEY_EQUALS)) Then
				FlxG.Mute = False
				FlxG.Volume(FlxG.Volume() + .1)
				_ShowSoundTray()
			End If
		End If

		_UpdateSoundTray(_step)
	#End

		Return 0
	End Method

	Method OnRender:Int()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Local mark:Int = Millisecs()
		Local elapsedMS:Int = mark - _total
		_total = mark

		If (_debugger <> Null) Then
			'TODO!
		Else
			_accumulator += elapsedMS

			if (_accumulator > _maxAccumulation) Then
				_accumulator = _maxAccumulation
			End If

			While (_accumulator >= _step)
				_Step()
				_accumulator -= _step
			Wend
		End If

		FlxBasic._VisibleCount = 0

		_Draw()

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And Not useSystemCursor) Then
			HideMouse()
		End If

		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play("beep" + FlxG.DATA_SUFFIX)
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()
		FlxG.ResetInput()
		FlxG.DestroySounds()

		If (_debugger <> Null) Then
			'TODO!
		End If

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()

		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.NewInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

			If (_debugger <> Null) Then
				'TODO
			End If

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

			If (_debugger <> Null) Then
				'TODO
			End If
		End If

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _Update:Void()
		FlxG.Elapsed = FlxG.TimeScale * (_step / 1000.0)

		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()

		If (FlxG.Tweener.active And FlxG.Tweener.HasTween) Then
			FlxG.Tweener.UpdateTweens()
		End If

		FlxG.UpdateCameras()

		If (_debuggerUp) Then
			'TODO!
		End If
	End Method

	Method _UpdateSoundTray:Void(ms:Int)
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= ms / 1000.0

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (ms / 1000.0 * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Draw:Void()
		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)

	#If TARGET <> "ios" And TARGET <> "android" And TARGET <> "psm"
		If( Not FlxG.Mobile) Then
			PushMatrix()
		End If
	#End

		Translate(FlxG._DeviceOffsetX, FlxG._DeviceOffsetY)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG._CurrentCamera = FlxG.Cameras.Get(i)

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

	#If TARGET <> "ios" And TARGET <> "android" And TARGET <> "psm"
		If( Not FlxG.Mobile) Then
			PopMatrix()
			_DrawSoundTray()
			FlxG.Mouse.Draw()
		End If
	#End
	End Method

	Method _DrawSoundTray:Void()
		If (_soundTrayVisible) Then
			Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
			If (FlxG.Mute) globalVolume = 0

			PushMatrix()

			Transform(FlxG._DeviceScaleFactorX, 0, 0, FlxG._DeviceScaleFactorY, ( (FlxG.DeviceWidth - _soundTrayWidth * FlxG._DeviceScaleFactorX) * 0.5), _soundTrayY)

			If (FlxG._LastDrawingAlpha <> .5) Then
				SetAlpha(.5)
				FlxG._LastDrawingAlpha = .5
			End If

			If (FlxG._LastDrawingColor <> FlxG.BLACK) Then
				SetColor(0, 0, 0)
			End If

			DrawRect(0, 0, _soundTrayWidth, _soundTrayHeight)

			SetColor(255, 255, 255)
			FlxG._LastDrawingColor = FlxG.WHITE

			Local bx:Int = 20
			Local by:Int = 28

			Local i:Int = 0
			While (i < 10)
				If (i < globalVolume) Then
					If (FlxG._LastDrawingAlpha <> 1) Then
						SetAlpha(1)
						FlxG._LastDrawingAlpha = 1
					End If

				Else
					If (FlxG._LastDrawingAlpha <> .5) Then
						SetAlpha(.5)
						FlxG._LastDrawingAlpha = .5
					End If
				End If

				DrawRect(bx, by, 8, i * 2)

				bx += 12
				by -= 2
				i += 1
			Wend

			'_soundTrayLabel.Draw()

			PopMatrix()
		End If
	End Method

	Method _Reset:Void()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Seed = SystemMillisecs()
		FlxG.UpdateDevice()
	End Method

	Method _ResetFramerate:Void()
		SetUpdateRate(FlxG.Framerate)

		_step = 1000.0 / FlxG.Updaterate
		_maxAccumulation = 2000.0 / FlxG.Framerate - 1

		If (_maxAccumulation < _step) Then
			_maxAccumulation = _step
		End If

		_updaterate = FlxG.Updaterate
		_framerate = FlxG.Framerate
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) +  FlxG.DATA_SUFFIX + ".png")
		Next

		FlxAssetsManager.AddImage("default" + FlxG.DATA_SUFFIX, "default" +  FlxG.DATA_SUFFIX + ".png")
		FlxAssetsManager.AddImage("button" + FlxG.DATA_SUFFIX, "button" + FlxG.DATA_SUFFIX + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor("cursor" + FlxG.DATA_SUFFIX, "cursor" + FlxG.DATA_SUFFIX + ".png")

		FlxAssetsManager.AddSound("beep" + FlxG.DATA_SUFFIX, "beep" + FlxG.DATA_SUFFIX + "." + FlxSound.GetValidExt())

		Local contentInitTrigger:FunctionInfo

		For Local classInfo:ClassInfo = EachIn GetClasses()
			contentInitTrigger = classInfo.GetFunction("OnContentInit",[], False)
			If (contentInitTrigger <> Null) contentInitTrigger.Invoke([])
		Next

		Self.OnContentInit()
	End Method
End Class

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (_lastUpdateTime = 0) Then
			_lastUpdateTime = Millisecs()
		ElseIf (Millisecs() - _lastUpdateTime >= 1000) Then
			FPS = _FPSCounter
			_lastUpdateTime = Millisecs()
			_FPSCounter = 0
		End If

		_FPSCounter += 1
	End Method

	Method Reset:Void()
		FPS = FlxG.Framerate
		_FPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class