(**
  * This is a modified version of the Elysion Frameworks (ElysionKronos) library 
  * and will probably be merged into the Elysion SVN in the near future
  *
  * SDL + OpenGL support
  *
  *
  * Not suitable for public use
  *
  * (C) Johannes Stein, 2009, www.freeze-dev.de
  * Licensed under MIT license
  *  For more information see 
  *				     http://www.opensource.org/licenses/mit-license.php
  *)

unit ElysionKronos;

{$IFDEF FPC}
{$mode delphi} // TODO: ObjFPC - Compability!
{$H+}
{$ENDIF}

// Include compiler switches
{$I ElysionKronos.inc}

interface

uses
	SDL,
	{$IFDEF USE_SDL_IMAGE} SDL_image, {$ENDIF}
	{$IFDEF USE_VAMPYRE}   ImagingSDL,{$ENDIF}
    {$IFDEF USE_SDL_MIXER} SDL_mixer, {$ENDIF}
    {$IFDEF USE_SFONT}     SFont,     {$ENDIF}
    {$IFDEF USE_SDL_TTF}   SDL_ttf,   {$ENDIF}
    {$IFDEF USE_SDL_GFX}   SDL_gfx,   {$ENDIF}
    {$IFDEF USE_SDL_NET}   SDL_net,   {$ENDIF}
    SDLTextures,
	  SDLUtils,
    {$IFDEF USE_DGL_HEADER}
    dglOpenGL,
    {$ELSE}
    gl, glu, glext,
    {$ENDIF}
    SysUtils,
    Classes;

const

// Include keymap
{$I keymap.inc}

type

// Float 2D coordinate
TelfPoint = record
  X, Y: Single;
end;

// Integer 2D coordinate
TelPoint = record
  X, Y: Integer;
end;

// Vertex: Integer 3D coordinate
TelVertex = record
  X, Y, Z: Integer;
end;

// Vertex: Float 3D Coordinate
TelfVertex = record
  X, Y, Z: Single;
end;

// Color type (R, G, B)
TelColor = Record
  R, G, B: Byte;
end;

// Color type with alpha channel
TRGBAColor = Record
  R, G, B, Alpha: Byte;
end;

TGradientStyle = SDLUtils.TGradientStyle;
TColorVertices = array[0..3] of TRGBAColor;

TelGradient = Record
  StartColor: TRGBAColor;
  EndColor: TRGBAColor;
  GradientStyle: TGradientStyle;
end;

// Specifies a rect
TelRect = Record
  X, Y, W, H: Integer;
end;

{
  Class: TelObject
  Group: Essential
  Description: Basic object
}
TelObject = class
  private
    FName, FNote: String;
	  FID: Integer;

    function GetUniqueID(): String;
  public
    {
      constructor TelObject.Create;

    }
    constructor Create; virtual;
    destructor Destroy; override;

  published
    // Name of an object
    property Name: String read FName write FName;
    // ID of an object
	  property ID: Integer read FID write FID;
    // Unique ID of an object; can't be changed
    property UniqueID: String read GetUniqueID;
    // Note of an object
    // For example to be used for description, tooltip, etc.
    property Note: String read FNote write FNote;
end;


{$IFDEF USE_LOGGER}
TLogType = (ltError, ltWarning, ltNote); // Logger type

{
	Class: TelLogger
	Group: Optional
	Description: Very simple logger class
	
	}
TelLogger = class
  private
    fFilename: String;
    fMessageList: TStringList;
  protected
    {$WARNINGS OFF}
    // Creates this class
    constructor Create;
    {$WARNINGS ON}
    
  public
    {	TelLogger.GetInstance
    	
    	}
    class function GetInstance: TelLogger;
	destructor Destroy; Override;
	
	{	TelLogger.WriteLog
		Parameters:
		
		}
    procedure WriteLog(LogString: String; LogType: TLogType = ltError);
  published
    property Filename: String read fFilename;
end;
{$ENDIF}

// This class provides common colors
{
	Class: TelColorHelper
	Description: 
	
	}
TelColorHelper = class
  private
    {
      Stores last used color in this variable as buffer
      -> See: function GetLastUsedColor: TelColor;
    }
    fLastUsedColor: TelColor;
    FBufferEmpty: Boolean;
    
    //function Get
  public
    constructor Create;
    destructor Destroy; Override;

	function clBlack: TelColor; 		     //< R: 0   G: 0   B: 0
	function clMaroon: TelColor;		     //< R: 128 G: 0   B: 0
	function clGreen: TelColor;			     //< R: 0   G: 128 B: 0
	function clOlive: TelColor;			     //< R: 128 G: 128 B: 0
	function clNavy: TelColor;			     //< R: 0   G: 0   B: 128
	function clPurple: TelColor;		     //< R: 128 G: 0   B: 128
	function clTeal: TelColor;			     //< R: 0   G: 128 B: 128
	function clGray: TelColor;			     //< R: 128 G: 128 B: 128
	function clSilver: TelColor;		     //< R: 192 G: 192 B: 192
	function clRed: TelColor;			       //< R: 255 G: 0   B: 0
	function clLime: TelColor;			     //< R: 0   G: 255 B: 0
	function clBlue: TelColor;			     //< R: 0   G: 0   B: 255
	function clFuchsia: TelColor;		     //< R: 255 G: 0   B: 255
	function clAqua: TelColor;			     //< R: 0   G: 255 B: 255
	function clCornflowerBlue: TelColor; //< R: 100 G: 149 B: 237 Best color ever, right? Right? Am I right or what?
	function clWhite: TelColor;			     //< R: 255 G: 255 B: 255

	function GetLastUsedColor: TelColor; //< Returns last used color
	procedure ClearBuffer;
  published
    //property Buffer:
end;



TelKeyboardHelper = class
  private
    fLastUsedKey: LongWord;
  public
    constructor Create;
    destructor Destroy; Override;

	  function isKeyDown(Key: Cardinal): Boolean;
	  function isKeyHit(Key: Cardinal): Boolean;
	  function isKeyUp(Key: Cardinal): Boolean;

	  function GetLastUsedKey: LongWord;
  published
    property LastUsedKey: LongWord read GetLastUsedKey;
end;

TelJoystickHelper = class
  private

  public
    constructor Create;
    destructor Destroy; Override;


  published

end;

TelMouseHelper = class
    private
        fLastCursorPos: TelPoint;
    public
        constructor Create;
        destructor Destroy;
	
        function isButtonDown(Button: LongWord): Boolean;
	function isButtonHit(Button: LongWord): Boolean;
        function isButtonUp(Button: LongWord): Boolean;

        function LeftClick(): Boolean;
        function RightClick(): Boolean;

        function WheelDown(): Boolean;
        function WheelUp(): Boolean;
	
        function Motion: Boolean; Overload;
        function Motion(Point: TelPoint): Boolean; Overload;
        function Motion(Rect: TelRect): Boolean; Overload;

        //function GetLastPosition: TelPoint;
end;


TelInputHelper = class
  private

  public
    Keyboard: TelKeyboardHelper;
    Joystick: TelJoystickHelper;
    Mouse: TelMouseHelper;

    constructor Create;
    destructor Destroy; Override;
end;

{
  #############################################################################
  # TelTimer                                                                  #
  #############################################################################
  # Function prototypes                                                       #

  Description:
    Provides basic timing functions

  Additional Notes: Inspired by Lazy Foo Productions
                    -> http://lazyfoo.net/SDL_tutorials/lesson13/index.php

}
TelTimer = class(TelObject)
  private
    FActive, FPaused: Boolean;
    FInterval: Integer;

    FStartTicks, FStoredTicks, FOldTicks: Integer;
  public
    // Constructor
    constructor Create;overload;override;

    // Constructor with interval
    constructor Create(Interval: Integer);overload;

    // Destructor
    destructor Destroy; Override;

    // Start timer -> you need to call that manually after creation
    procedure Start();

    // Stop timer
    procedure Stop();

    // Pause & unpause
    procedure Pause();
    procedure UnPause();

    function GetTicks(): Integer;

    // If Interval is set, use this to provoke an action

    // Works great if you're checking action every second or higher
    // If you're using it for < 100 ms intervals, it might not be precise enough
    // In that case use the GetTicks() - function or SDL_AddTimer
    function Event: Boolean;
  published
    property Active: Boolean read FActive;
    
    property Interval: Integer read FInterval write FInterval;

    property Paused: Boolean read FPaused;
end;

// Don't use vfNull
TelVideoFlags = (vfNull, vfAuto, vfHardware, vfSoftware);

{	@classname @br
	Description: @br
	Provides an application container
	}
TAppContainer = class(TelObject)
  private
    FInitialized: Boolean;

    FRun: Boolean;

    FUseLogger: Boolean;
  
    {$IFDEF USE_SDL_MIXER}
	FAudio_rate, FAudio_rate_Mac: Integer;
    FAudio_format: UInt16; 
    FAudio_channels: Integer;
    FAudio_buffers: Integer;
	{$ENDIF}
  
    {	Application.GetUnicodeSupport @br
    	@return
    	
    	}
    function GetUnicodeSupport: Boolean;
    
	{	Application.SetUnicodeSupport
    	
    	}
    procedure SetUnicodeSupport(Value: Boolean);
  public
    {
      Application.Create


      Returns: Instance of class Application
    }
    constructor Create; Override;

    {
      Application.Destroy


    }
    destructor Destroy; Override;

    {
      Application.Initialize

      Returns:
    }
    function Initialize: Boolean; Overload;

    // For backward compability purposes
    function Initialize(Name: String; Width, Height, Bits: Integer; Fullscreen: Boolean; VideoFlag: TelVideoFlags): Boolean; Overload;
    function Initialize(Width, Height, Bits: Integer; Fullscreen: Boolean = false): Boolean; Overload;

    {
      Application.CreateWindow
      Parameters: Name: String
                  Width, Height, Bits: Integer
                  Fullscreen: Boolean
                  VideoFlag: TelVideoFlags

      Note: Only supported in ElysionLegacy and ElysionKronos @br
            ElysionMobile creates a window automatically

    }
    function CreateWindow(Name: String; Width, Height, Bits: Integer; Fullscreen: Boolean; VideoFlag: TelVideoFlags): Boolean; Overload;

    {
      Application.CreateWindow

      @seealso
    }
    function CreateWindow(Name: String; Width, Height, Bits: Integer; Fullscreen: Boolean = false): Boolean; Overload;

    {
      Application.Quit
      Description:
    }
    procedure Quit;

  published
    // Use Application.Run for the main loop
    property Run: Boolean read FRun;

    property UseLogger: Boolean read FUseLogger write FUseLogger;
    {
      If UnicodeSupport is active, all keydown events are switched to unicode
      and true type fonts will be automatically set for unicode mode
    }
    property UnicodeSupport: Boolean read GetUnicodeSupport write SetUnicodeSupport;
end;

TProjectionMode = (pmPerspective, pmOrtho);
// 
TelWindow = class(TelObject)
  private
    // Resolution is stored in a vertex
    FResolution: TelVertex;

    // VideoFlags
    FVideoFlag: TelVideoFlags;

	  FFullscreen: Boolean;
	  FHideCursor: Boolean;

    fFlags, fGFlags: Cardinal;
    fFullscreenFlag: Cardinal;
    
    FFoVY, FZNear, FZFar: Double;

        UseBackgroundImage: Boolean;
        FBackgroundColor: TelColor;
        FBackgroundChange: Boolean;
        FBackgroundImage: PSDL_Surface;

        fEvent: TSDL_Event;
    
        fCaption, fIcon: PChar;

        FStartTick, FCurTick, FTickNow: UInt32;
        fFrames: Integer;
    
	fLimitFPS: Boolean;
	fFrameCap: Single;

        fFPSTimer, fDeltaTimer: TelTimer;
      
      	fDeltaTime: Double;

        // Handling key and mouse inputs
        procedure HandleEvents;
    
        // Get the window title
        function GetCaption: String;

	// Sets the window title
        procedure SetCaption(Caption: String);
    
        procedure SetFPS(Value: Single);
        function GetFPS: Single;
        
        function GetDeltaTime: Double;

        function GetWidth: Integer;
        function GetHeight: Integer;
        function GetBitsPerPixel: Integer;

    public
        // You can access the SDL surface directly if you wish
	SDL_Surface: PSDL_Surface;

  // Deprecated, use Input class
	// True, if key is pressed down
	KeyDown: array[0..K_EURO] of Boolean;

  // Deprecated, use Input class
	// True, if key is pressed up again
	KeyUp: array[0..K_EURO] of Boolean;

  // Deprecated, use Input class
	// True, if key is pressed down
	ModKeyDown: array[KMOD_NONE..KMOD_META] of Boolean;

  // Deprecated, use Input class
	// True, if key is pressed up again
	ModKeyUp: array[KMOD_NONE..KMOD_META] of Boolean;

	// Gets current mouse coordinates (read-only)
	Cursor: TelPoint;

  // Deprecated, use Input class
	// True, if mouse button is pressed down
	MouseButtonDown: array[BUTTON_LEFT..BUTTON_WHEELDOWN] of Boolean;

  // Deprecated, use Input class
	// True, if mouse button is pressed up again
	MouseButtonUp: array[BUTTON_LEFT..BUTTON_WHEELDOWN] of Boolean;

  constructor Create; Override;
  destructor Destroy; Override;

  function SetVideoMode(_Width, _Height, _ColorBits: Integer; _Fullscreen: Boolean; _VideoFlag: TelVideoFlags): Boolean; Overload;

	// Sets video mode for the surface. Use this if you want to change the resolution in-game.
	function SetVideoMode(_Width, _Height, _ColorBits: Integer; _Fullscreen: Boolean = False): Boolean; Overload;



	// Switches between windowed mode and fullscreen mode
	procedure ToggleFullscreen();

	// Use BeginScene first thing in the game loop
	procedure BeginScene();
	
	// Use EndScene last thing before the end of the game loop
        procedure EndScene;
        
    // Set projection matrix
    // Expert function: Automatically Ortho mode is being called, this function needs to be used
    // if you want to display 3d objects on the screen, you need to call perspective mode
    procedure SetProjection(ProjectionMode: TProjectionMode);


        // Expert function: Use background image instead of a color
        procedure SetBackgroundImage(Surface: PSDL_Surface); Overload;
	
	// Sets background image with specifying a filename
	procedure SetBackgroundImage(const Filename: String); Overload;
	
	// Sets background color
        procedure SetBackgroundColor(Color: TelColor);
	
	// Sets a window icon
        procedure SetIcon(Filename: String); Overload;

	// Sets a window icon with color key
	procedure SetIcon(Filename: String; Mask: TelColor); Overload;

	// Hide the standard black/white cursor. Normally turned on
        procedure HideCursor;
	
	// Show the cursor
        procedure ShowCursor;

	// Saves the surface into an image
        Procedure TakeScreenshot(Filename: String);
    
        // Checks if a point is within a rect
        Function OnPoint(Coord: TelPoint; Rect: TelRect): Boolean;

  // Checks if surface is active
  function isActive: Boolean;

  published

    // Read-only, set values through SetVideoMode
    property Width: Integer read GetWidth;
	  property Height: Integer read GetHeight;
    property Bits: Integer read GetBitsPerPixel;
    property Fullscreen: Boolean read FFullscreen;

	  // Setting the caption
	  property Caption: String read GetCaption write SetCaption;
	  
	  property DeltaTime: Double read GetDeltaTime;


	    property FPS: Single read GetFPS write SetFPS;
	    property LimitFPS: Boolean read fLimitFPS write fLimitFPS;
	    
	    
	    // Values for experts
	    property FoVY: Double read FFoVY write FFoVY;
	    property ZNear: Double read FZNear write FZNear;
	    property ZFar: Double read FZFar write FZFar;
end;

TelCamera = class
    private
      fWidth, fHeight: Integer;
    public
        Position: TelPoint;
  
        constructor Create;
        procedure Move(Point: TelPoint);
	published
      property Width: Integer read fWidth write fWidth;
      property Height: Integer read fHeight write fHeight;
	  
end;

// See Book: iPhone Game Development
TelGameState = class(TelObject)
  public
    constructor Create; Override;
	destructor Destroy; Override;
    
	procedure Render; virtual; abstract;
	procedure Update; virtual; abstract;
	procedure HandleEvents; virtual; abstract;
end;

TelGameStateManager = class(TelObject)
  private
    fGameStateList: TList;
  public
    constructor Create; Override;
	destructor Destroy; Override;
	
	procedure Render;
	procedure Update;
	procedure HandleEvents;
	
	procedure SetState(GameState: TelGameState);
end;

TAlignVertical = (avNone, avTop, avButtom, avCenter);
TAlignHorizontal = (ahNone, ahLeft, ahRight, ahCenter);

// See: TelGraphicObject.Generate;
TGenerateMode = (gmAuto, gmRGB, gmRGBA);

// Basic graphical object
TelGraphicObject = class(TelObject)
  private
    FAlpha: Byte;
    FAngle: Single;


    FChanged: Boolean;
    FCloned: Boolean;
    FGenerated: Boolean;
    FImageType: String;

    FParent: PSDL_Surface;
    FRect: TelRect;

    // Virtual abstract to keep it as wide as possible for fonts
		function GetWidth: Integer; virtual; abstract;
		function GetHeight: Integer; virtual; abstract;

		function GetAlignVertical(): TAlignVertical;
		function GetAlignHorizontal(): TAlignHorizontal;
		procedure SetAlignVertical(VerticalAlignment: TAlignVertical);
		procedure SetAlignHorizontal(HorizontalAlignment: TAlignHorizontal);
  public
	    // Access the SDL_Surface directly if you need to
      SDL_Surface: PSDL_Surface;

		// Position of the graphic object
		Position: TelVertex;

    // Hot Spot
    Offset: TelPoint;

    Color: TelColor;

		// Create Object
        constructor Create;
        // Destructor
        destructor Destroy; Override;

    procedure LoadFromFile(Filename: String); virtual; abstract;

    {

    }
    procedure SaveToBitmap(Filename: String);

    procedure VirtualClone(Source: TelGraphicObject); virtual; abstract;

		procedure Draw; virtual; abstract;

		// Move object
		procedure Move(Point: TelPoint);
  published
      // Ignores Position if used
		  property AlignH: TAlignHorizontal read GetAlignHorizontal write SetAlignHorizontal;
		  property AlignV: TAlignVertical read GetAlignVertical write SetAlignVertical;
	
    // Alpha value
    property Alpha: Byte read FAlpha write FAlpha;

    // Angle
    property Angle: Single read FAngle write FAngle;
    property Cloned: Boolean read FCloned;

    property Generated: Boolean read FGenerated;
end;


{
  #############################################################################
  # TelScene                                                                  #
  #############################################################################
  # Function prototypes                                                       #

  Description:
    Allows the developer to put different graphical objects in one scene.
    Use of TelScene is encouraged, more resource-friendly and clearly arranged
    than using 100 graphical objects for example

    Can be combined with TelSpriteEngine

  Additional Notes: -

}
TelScene = class(TelGraphicObject)
  private

  public
    {

    }
    constructor Create(Width, Height: Integer); Overload;

    {

    }
    constructor Create(Width, Height: Integer; Mask: TRGBAColor); Overload;

    {

    }
    destructor Destroy; Override;


end;


{$IFDEF USE_SFONT}
TelBitmapFont = class
    private
        fAlpha: Byte;
    
        fFontInfo: TSFont_FontInfo;
    public
        // Constructor
        constructor Create;

	// Destructor
        destructor Destroy;

	// Load BitmapFont from filename
        procedure LoadFromFile(Filename: String);
    
	// Set transparency from Color
        procedure SetTransparency(Color: TelColor); Overload;
	
	// Set transparency from Coordinate
        procedure SetTransparency(Coord: TelPoint); Overload;
    
	
        procedure SetAlpha(Alpha: Byte);
        function GetAlpha: Byte;
    
        function GetWidth(Text: String): Integer;
        function GetHeight: Integer;
    
	// Draw text at designed position
        procedure TextOut(Point: TelPoint; Text: String; AlignV: TAlignVertical = avNone; AlignH: TAlignHorizontal = ahNone);
    published	
        property Alpha: Byte read GetAlpha write SetAlpha;
        
        property Height: Integer read GetHeight;
	
end;
{$ENDIF}

{$IFDEF USE_SDL_TTF}
TFontRender = (rtSolid, rtBlended); // Font Render: rtSolid = Simple with black background under Mac OS X and Linux; rtBlended = pretty, but slow
TFontStyle = (fsBold, fsItalic, fsUnderline); // Font Style: different font styles
TFontStyles = set of TFontStyle; // Font Style Set
TTextStyle = (tsUnicode, tsUTF8, tsNormal); // Text Style: which encoding to use
TelTrueTypeFont = class
  private
    fSurface: PSDL_Surface;
	fFont: PTTF_Font;
	fFontRender: TFontRender;
	fFontStyles: TFontStyles;
	fTextStyle: TTextStyle;
	fColor: TelColor;

        fChange: Boolean;
        fTrim: Boolean;
        fMargin: Integer;
	
	fText: String;
  function GetWidth_Text(Text: String): Integer;
	function RenderFont(Text: String; FontRender: TFontRender; TextStyle: TTextStyle): PSDL_Surface;
  public
    Texture: array[0..1024] of GLuInt;
    
    constructor Create();
	destructor Destroy;
	
	function GetFontPointer: PTTF_Font;
	procedure VirtualClone(FromFont: TelTrueTypeFont);
	
	procedure LoadFromFile(Filename: String; PtSize: Integer = 8);
	
	procedure SetRenderType(FontRender: TFontRender);
	function GetRenderType: TFontRender;
	
	procedure SetFontStyle(FontStyles: TFontStyles);
	function GetFontStyle: TFontStyles;
	
	procedure SetTextStyle(TextStyle: TTextStyle);
	function GetTextStyle: TTextStyle;
	
	procedure SetText(Text: String);
	function GetText: String;

	procedure SetColor(Color: TelColor);
        function GetColor: TelColor;
	
        function GetWidth: Integer;
        function GetHeight: Integer;
        
	procedure TextOut(Point: TelVertex; Text: String;  AlignV: TAlignVertical = avNone; AlignH: TAlignHorizontal = ahNone);
	procedure Draw;

  published

    property Margin: Integer read fMargin write fMargin;
      property Trim: Boolean read fTrim write fTrim;

    property Text: String read GetText write SetText;
	
	property Height: Integer read GetHeight;
	property Width: Integer read GetWidth;
        
    property Style: TFontStyles read GetFontStyle write SetFontStyle;
end;
{$ENDIF}

TelFontContainer = class
  private
  
  public
    //procedure TextOut(Point: TelPoint; Text: String);
  published
  
end;


TOffset = record
	Rotation: TelPoint;
	Position: TelPoint;
end;

TRotation = record
	Angle: Single;
	Vector: TelfVertex;
end;

TelSprite = class(TelObject)
  private
    fFilename: String; // Stores filename private
	fGenerated, fCloned, fError: Boolean;
   
    fLoaded, fChange: Boolean;
    fNull: Boolean;

    FTransparent: Boolean;

    fImageType: String;
    
    FOriginalSize: TelPoint;

    FFrame, FMaxFrames: Integer;
    FAnim: TelRect;
    FZ: Integer;
    FVisible: Boolean;
    
    FColor: TelColor;
    FColoring: Boolean;

    FRect: TelRect;
    FClipRect: TelRect;
    FAlpha: Byte;
	
	procedure SetFrame(Frame: Integer);
    function GetFrame: Integer;
	
	function OnPoint(Coord: TelPoint): Boolean;
	
	procedure SetWidth(Width: Integer);
    function GetWidth: Integer;
    procedure SetHeight(Height: Integer);
    function GetHeight: Integer;
    function GetSurfaceHeight: Integer; // read-only
    function GetSurfaceWidth: Integer; // read-only
    
    procedure SetAutoTransparency(Value: Boolean);
  public
    Offset: TOffset;
	Rotation: TRotation;
	
	Scale: TelfPoint;
	Position: TelVertex;
	Color: TelColor;
	
	SDL_Surface: PSDL_Surface;
	Texture: GLuInt;
  
    constructor Create; Override;
    destructor Destroy; Override;

    function LoadFromFile(Filename: String): Boolean; Overload;
    function LoadFromFile(Filename: String; ClipRect: TelRect): Boolean; Overload;
	
	procedure LoadFromStream(Stream: TStream);
	procedure SaveToStream(Stream: TStream);
	
	procedure VirtualClone(FromSprite: TelSprite);
	
	procedure Generate(GenerateMode: TGenerateMode = gmAuto);
	
	{

  }

    function GetImageType: String;

    procedure ClipImage(Rect: TelRect);
    function GetClipRect: TelRect;
	function GetRect: TelRect;

    procedure Move(Coord: TelPoint); Overload;
    procedure Move(Coord: TelVertex); Overload;

    function GetMaxFrames: Integer;

    procedure SetTransparency(Color: TelColor); Overload;
    procedure SetTransparency(Coord: TelPoint); Overload;

    procedure Draw; 
    //procedure Draw(TargetSprite: TelSprite); Overload;
	//procedure Draw(Target: PSDL_Surface); Overload;

    procedure Animate(Timer: TelTimer; Loop: Boolean = true); Overload;
	procedure Animate(var Counter: Integer; Loop: Boolean = true); Overload;

    function OnMouseOver: Boolean;
    function OnClick: Boolean;

  published
    property Alpha: Byte read fAlpha write fAlpha;
  
    property SurfaceWidth: Integer read GetSurfaceWidth;
	property SurfaceHeight: Integer read GetSurfaceHeight;
	property Width: Integer read GetWidth write SetWidth;
	property Height: Integer read GetHeight write SetHeight;
	
	property Filename: String read fFilename;
	
	property Frame: Integer read GetFrame write SetFrame;
	
	property Transparent: Boolean read FTransparent write SetAutoTransparency;
	
	property Visible: Boolean read fVisible write fVisible;

    property Null: Boolean read fNull write fNull;
	
	
	property Generated: Boolean read fGenerated;
	property Cloned: Boolean read fCloned;
	property Error: Boolean read fError;
end;

TelSpriteList = class(TelObject)
  private
    FSpriteList: TList;
	Head: String[13];
  
    function Get(Index: Integer): TelSprite;
	function GetPos(Index: String): Integer;
    procedure Put(Index: Integer; const Item: TelSprite);
	procedure PutS(Index: String; const Item: TelSprite);
    function GetS(Index: String): TelSprite;
    function GetCount: integer;
  public
    constructor Create; Override;
	destructor Destroy; Override;
	
	procedure Insert(Index: Integer; Sprite: TelSprite);
    function  Add(Sprite: TelSprite): Integer;
    procedure Delete(Index: Integer);
    procedure LoadFromStream(Stream : TFileStream);
    procedure SaveToStream(Stream : TFileStream);
	
    procedure LoadFromFile(Filename: String);
	procedure SaveToFile(Filename: String);
	
	property Items[Index: Integer]: TelSprite read Get write Put; default;
	property Find[Index: String]: TelSprite read GetS write PutS;
  published
    property Count: Integer read GetCount;
end;

TelSpriteEngine = class
  private

  public
    UpdateRects: array of TelRect;
end;


TelButtonStyle = (bsNormal, bsFlat, bsRounded, bsImage);
// GUI elements
TelButton = class(TelObject)
  private	
	fSprite: TelSprite;
	fFont: TelTrueTypeFont;
	
	fCaption: String;
	FEnabled: Boolean;
	
	function GetWidth: Integer;
	function GetHeight: Integer;
  public
    Position: TelVertex;
  
    constructor Create; Override;
    destructor Destroy; Override;
	
	procedure VirtualClone(FromButton: TelButton);
	
	function GetImage: TelSprite;
	function GetFont: TelTrueTypeFont;
	
	procedure LoadFromFile(Filename: String); Overload;
	procedure LoadFromFile(Filename: String; Rect: TelRect); Overload;
	
	procedure LoadFromSprite(Sprite: TelSprite); Overload;
	procedure LoadFromSprite(Sprite: TelSprite; Rect: TelRect); Overload;
	
	procedure LoadFont(Filename: String; ptSize: Integer = 10);
	
	procedure Move(Point: TelPoint);
	
    procedure ClipImage(Rect: TelRect);
        
    function OnMouseOver: Boolean;
    function OnClick: Boolean;
	function OnRightClick: Boolean;
        
	procedure Draw;
  published	
    property Caption: String read fCaption write fCaption;
	
	property Enabled: Boolean read FEnabled write FEnabled;

    property Font: TelTrueTypeFont read fFont write fFont;
	
	property Sprite: TelSprite read FSprite write FSprite;
	
	property Width: Integer read GetWidth;
	property Height: Integer read GetHeight;
end;

{TelCheckButton = class
  private

  public

  published
    property Checked: Boolean read FChecked
end;}

// Intermediate GUI (best GUI conecept ever!)
TelGUI = class
  private

  public
    constructor Create;
    destructor Destroy;

    procedure Box(Rect: TelRect; Color: TelColor); Overload;
    procedure Box(Rect: TelRect; Color: TRGBAColor); Overload;
    procedure Box(Rect: TelRect; Color: TelGradient); Overload;

    function Button(Rect: TelRect; Caption: String): Boolean;
end;
  
{$IFDEF USE_SDL_MIXER}
TelMusic = class
  private
    fMusic: PMix_Music;
	fPaused, fisPlaying: Boolean;
	
	FVolume: ShortInt;
	procedure SetVolume(Value: ShortInt);
  public
    constructor Create(); overload;
    constructor Create(Filename: String); overload;
    destructor Destroy();

	procedure SetPosition(Position: Double);
	
    procedure LoadFromFile(Filename: String);
    procedure Play(Loop: Integer = 0);
	procedure Pause();
    procedure Stop();
  published
    property isPlaying: Boolean read fisPlaying;
    property Paused: Boolean read fPaused;
	property Volume: ShortInt read FVolume write SetVolume;
end;

TelSound = class
  private
    fSound: PMix_Chunk;
  public
    constructor Create; overload;
    constructor Create(Filename: String); overload;
    destructor Destroy;

    procedure LoadFromFile(Filename: String);
    procedure Play;
    procedure Stop;
end;
{$ENDIF}

function makeCol(R, G, B: Byte): TelColor; Overload;
function makeCol(R, G, B, A: Byte): TRGBAColor; Overload;
function makeGradient(StartColor: TRGBAColor; EndColor: TRGBAColor; GradientStyle: TGradientStyle = gsVertical): TelGradient;

function convCol(S: TelWindow; Color: TelColor): Cardinal; Overload;
function convCol(S: TelWindow; R, G, B: Byte): Cardinal; Overload;
function convCol(Color: TelColor): TSDL_Color; Overload;
//function convCol(Color: TelColor): Cardinal; Overload;
function convCol(R, G, B: Byte): Cardinal; Overload;
function makeP2D(X, Y: Single): TelfPoint; Overload;
function makeP2D(X, Y: Integer): TelPoint; Overload;
function makeP3D(X, Y: Integer; Z: Integer = 0): TelVertex; Overload;
function makeP3D(X, Y, Z: Single): TelfVertex; Overload;
function makeRect(X, Y, W, H: Integer): TelRect;

function OnPoint(Coord: TelPoint; Rect: TelRect): Boolean;
function GetFilenameExtension(Filename: String): String;
function SubPoints(Point1, Point2: TelPoint): TelPoint;

procedure DrawQuad(pX, pY, pW, pH, pZ: Single); Overload;
procedure DrawQuad(OriginalSize: TelPoint; ClipRect, DrawRect: TelRect; Z: Single); Overload;

function ActiveWindow: TelWindow;

function CollisionTest(SpriteOne, SpriteTwo: TelSprite): Boolean;
function PixelTest(SpriteOne, SpriteTwo: TelSprite): Boolean; Overload;
function PixelTest(Sprite: TelSprite; Rect: TelRect): Boolean; Overload;

function IsRectEmpty(Rect: TelRect): Boolean;

procedure DrawLine(Src, Dst: TelPoint; Color: TelColor); Overload;
procedure DrawLine(Points: array of TelPoint; Color: TelColor); Overload;

// Use operator overloading if supported


var
  // Application
  Application: TAppContainer;

  // ColorHelper: To use
  Color: TelColorHelper;

  GUI: TelGUI;

  Input: TelInputHelper;

  // Use Surface directly for backward compability if necessary
  // Better way: Use ActiveWindow function
  Window: TelWindow;

implementation

{$IFDEF USE_LOGGER}
var
  Logger: TelLogger;
{$ENDIF}

// Uh, those are some pretty functions

//
function makeCol(R, G, B: Byte): TelColor;
begin
  Result.R := R;
  Result.G := G;
  Result.B := B;
end;

function makeCol(R, G, B, A: Byte): TRGBAColor;
begin
  Result.R := R;
  Result.G := G;
  Result.B := B;
  Result.Alpha := A;
end;

function makeGradient(StartColor: TRGBAColor; EndColor: TRGBAColor; GradientStyle: TGradientStyle = gsVertical): TelGradient;
var
  tmpGradient: TelGradient;
begin
  tmpGradient.StartColor := StartColor;
  tmpGradient.EndColor := EndColor;
  tmpGradient.GradientStyle := GradientStyle;

  Result := tmpGradient;
end;

// See: http://www.delphi-library.de/topic_wie+kann+ich+einen+String+zerteilen_26639,0.html
function Split(fText: String;fSep: Char;fTrim: Boolean=false;fQuotes: Boolean=false):TStringList;
var vI: Integer;
    vBuffer: String;
    vOn: Boolean;
begin
  Result:=TStringList.Create;
  vBuffer:='';
  vOn:=true;
  for vI:=1 to Length(fText) do
  begin
    if (fQuotes and(fText[vI]=fSep)and vOn)or(Not(fQuotes) and (fText[vI]=fSep)) then
    begin
      if fTrim then vBuffer:=Trim(vBuffer);
      if vBuffer[1]=fSep then
        vBuffer:=Copy(vBuffer,2,Length(vBuffer));
      Result.Add(vBuffer);
      vBuffer:='';
    end;
    if fQuotes then
    begin
      if fText[vI]='"' then
      begin
        vOn:=Not(vOn);
        Continue;
      end;
      if (fText[vI]<>fSep)or((fText[vI]=fSep)and(vOn=false)) then
        vBuffer:=vBuffer+fText[vI];
    end else
      if fText[vI]<>fSep then
        vBuffer:=vBuffer+fText[vI];
  end;
  if vBuffer<>'' then
  begin
    if fTrim then vBuffer:=Trim(vBuffer);
    Result.Add(vBuffer);
  end;
end;

// deprecated, please use function convCol(Color: TelColor): Cardinal; instead
function convCol(S: TelWindow; Color: TelColor): Cardinal; Overload;
begin
  Result := SDL_MapRGB(S.SDL_Surface.Format, Color.R, Color.G, Color.B);
end;

// deprecated, please use function convCol(R, G, B: Byte): Cardinal; instead
function convCol(S: TelWindow; R, G, B: Byte): Cardinal; Overload;
begin
  Result := SDL_MapRGB(S.SDL_Surface.Format, R, G, B);
end;

//function convCol(Color: TelColor): Cardinal; Overload;
//begin
//  Result := SDL_MapRGB(Surface.SDL_Surface.Format, Color.R, Color.G, Color.B);
//end;

function convCol(R, G, B: Byte): Cardinal; Overload;
begin
  Result := SDL_MapRGB(Window.SDL_Surface.Format, R, G, B);
end;

function convCol(Color: TelColor): TSDL_Color; Overload;
begin
  Result.r := Color.R;
  Result.g := Color.G;
  Result.b := Color.B;
  Result.unused := 0;
end;

function makeP2D(X, Y: Single): TelfPoint; Overload;
begin
  Result.X := X;
  Result.Y := Y;
end;

function makeP2D(X, Y: Integer): TelPoint; Overload;
begin
  Result.X := X;
  Result.Y := Y;
end;

function makeP3D(X, Y: Integer; Z: Integer = 0): TelVertex; Overload;
begin
  Result.X := X;
  Result.Y := Y;
  Result.Z := Z;
end;

function makeP3D(X, Y, Z: Single): TelfVertex; Overload;
begin
  Result.X := X;
  Result.Y := Y;
  Result.Z := Z;
end;

function makeRect(X, Y, W, H: Integer): TelRect;
begin
  Result.X := X;
  Result.Y := Y;
  Result.W := W;
  Result.H := H;
end;

function OnPoint(Coord: TelPoint; Rect: TelRect): Boolean;
begin
  if (Coord.X >= Rect.X) and
     (Coord.Y >= Rect.Y) and
     (Coord.X < (Rect.X + Rect.W)) and
     (Coord.Y < (Rect.Y + Rect.H)) then Result := true else Result := false;
end;

function GetFilenameExtension(Filename: String): String;
begin
  Result := UpperCase(Copy(Filename, Length(Filename) - 2, Length(Filename)));
end;




function SubPoints(Point1, Point2: TelPoint): TelPoint;
var tmp: TelPoint;
begin
  tmp.X := Point1.X - Point2.X;
  tmp.Y := Point1.Y - Point2.Y;

  Result := tmp;
end;



function ActiveWindow: TelWindow;
begin
  Result := Window;
end;


// TODO: Use VBOs instead of glBegin/glEnd calls
procedure DrawQuad(pX, pY, pW, pH, pZ: Single); Overload;
begin
  glBegin(GL_QUADS);
    glTexCoord2f(1, 0); glVertex3f(pX + pW, pY, -pZ);
    glTexCoord2f(0, 0); glVertex3f(pX	  , pY, -pZ);
    glTexCoord2f(0, 1); glVertex3f(pX	  , pY + pH, -pZ);
    glTexCoord2f(1, 1); glVertex3f(pX + pW, pY + pH, -pZ);
  glEnd(); 
end;

procedure DrawQuad(pX, pY, pW, pH, pZ: Single; Vertices: TColorVertices); Overload;
begin
  glBegin(GL_QUADS);
    glColor4f(Vertices[0].R / 255, Vertices[0].G / 255, Vertices[0].B / 255, Vertices[0].Alpha / 255); glTexCoord2f(1, 0); glVertex3f(pX + pW, pY, -pZ);
    glColor4f(Vertices[1].R / 255, Vertices[1].G / 255, Vertices[1].B / 255, Vertices[1].Alpha / 255); glTexCoord2f(0, 0); glVertex3f(pX	  , pY, -pZ);
    glColor4f(Vertices[2].R / 255, Vertices[2].G / 255, Vertices[2].B / 255, Vertices[2].Alpha / 255); glTexCoord2f(0, 1); glVertex3f(pX	  , pY + pH, -pZ);
    glColor4f(Vertices[3].R / 255, Vertices[3].G / 255, Vertices[3].B / 255, Vertices[3].Alpha / 255); glTexCoord2f(1, 1); glVertex3f(pX + pW, pY + pH, -pZ);
  glEnd();
end;

procedure DrawQuad(OriginalSize: TelPoint; ClipRect, DrawRect: TelRect; Z: Single); Overload;
//var tposx, tposy, row, col: single;
var tposx, tposy, tposw, tposh: Single;
begin
  if (OriginalSize.X > 0) and (OriginalSize.Y > 0) then
  begin
    if ClipRect.X > 0 then tposx := ClipRect.X / OriginalSize.X else tposx := 0;
    if ClipRect.Y > 0 then tposy := ClipRect.Y / OriginalSize.Y else tposy := 0;
  
    tposw := ClipRect.W / OriginalSize.X;
    tposh := ClipRect.H / OriginalSize.Y;
  end;
  
  {row := OriginalSize.X / ClipRect.W;
  col := OriginalSize.Y / ClipRect.H;

  tposx := OriginalSize.X / ClipRect.X;
  tposy := OriginalSize.Y / ClipRect.Y;}

  // Use VBOs if OpenGL >= 1.5, else glBegin/glEnd
  
  {$IFDEF USE_DGL_HEADER}
  if (GL_VERSION_1_5) then
  {$ELSE}
  if (Load_GL_version_2_0) then
  {$ENDIF}
  begin
  
  end else
  begin
  
  end;
  
  glBegin(GL_QUADS);
    glTexCoord2f(0 + tposx		  , 0 + tposy); 		glVertex3f(DrawRect.X, DrawRect.Y, -Z);
    glTexCoord2f(0 + tposx		  , 0 + tposy + tposh); glVertex3f(DrawRect.X, DrawRect.Y + DrawRect.H, -Z);
    glTexCoord2f(0 + tposx + tposw, 0 + tposy + tposh); glVertex3f(DrawRect.X + DrawRect.W, DrawRect.Y + DrawRect.H, -Z);
    glTexCoord2f(0 + tposx + tposw, 0 + tposy); 		glVertex3f(DrawRect.X + DrawRect.W, DrawRect.Y, -Z);
  glEnd;

  {glBegin(GL_QUADS);
    glTexCoord2f(0+1/tposx+1/row, 1-1/tposy);       glTexCoord2f(1, 0); glVertex3f(DrawRect.X + DrawRect.W, DrawRect.Y, -Z);
    glTexCoord2f(0+1/tposx      , 1-1/tposy);       glTexCoord2f(0 + tposx, 0 + tposy); glVertex3f(DrawRect.X, DrawRect.Y, -Z);
    glTexCoord2f(0+1/tposx      , 1-1/tposy-1/col); glTexCoord2f(0, 1); glVertex3f(DrawRect.X, DrawRect.Y + DrawRect.H, -Z);
    glTexCoord2f(0+1/tposx+1/row, 1-1/tposy-1/col); glTexCoord2f(1, 1); glVertex3f(DrawRect.X + DrawRect.W, DrawRect.Y + DrawRect.H, -Z);
  glEnd;}
end;

function CollisionTest(SpriteOne, SpriteTwo: TelSprite): Boolean;
begin
  Result := False;
  if ((SpriteOne.Visible) and (SpriteTwo.Visible)) then
  begin
    If SpriteOne.OnPoint(makeP2D(SpriteTwo.Position.X, SpriteTwo.Position.Y)) Or
       SpriteTwo.OnPoint(makeP2D(SpriteOne.Position.X, SpriteOne.Position.Y)) Or
       SpriteOne.OnPoint(makeP2D(SpriteTwo.Position.X + SpriteTwo.Width, SpriteTwo.Position.Y)) Or
       SpriteTwo.OnPoint(makeP2D(SpriteOne.Position.X + SpriteOne.Width, SpriteOne.Position.Y)) Or
       SpriteOne.OnPoint(makeP2D(SpriteTwo.Position.X,                   SpriteTwo.Position.Y + SpriteTwo.Height)) Or
       SpriteTwo.OnPoint(makeP2D(SpriteOne.Position.X,                   SpriteOne.Position.Y + SpriteOne.Height)) Or
       SpriteOne.OnPoint(makeP2D(SpriteTwo.Position.X + SpriteTwo.Width, SpriteTwo.Position.Y + SpriteTwo.Height)) Or
       SpriteTwo.OnPoint(makeP2D(SpriteOne.Position.X + SpriteOne.Width, SpriteOne.Position.Y + SpriteOne.Height)) Then Result := True;
  end;
end;

function PixelTest(SpriteOne, SpriteTwo: TelSprite): Boolean;
var 
  SpriteOneRect, SpriteTwoRect: TSDL_Rect;
begin
  Result := false;
  if ((SpriteOne.Visible) and (SpriteTwo.Visible)) then
  begin
    if (not SpriteOne.Transparent) then SpriteOne.Transparent := true;
    if (not SpriteTwo.Transparent) then SpriteTwo.Transparent := true;

    SpriteOneRect.x := SpriteOne.GetClipRect.X;
	SpriteOneRect.y := SpriteOne.GetClipRect.Y;
	SpriteOneRect.w := SpriteOne.GetClipRect.W;
	SpriteOneRect.h := SpriteOne.GetClipRect.H;
	
	SpriteTwoRect.x := SpriteTwo.GetClipRect.X;
	SpriteTwoRect.y := SpriteTwo.GetClipRect.Y;
	SpriteTwoRect.w := SpriteTwo.GetClipRect.W;
	SpriteTwoRect.h := SpriteTwo.GetClipRect.H;
  
    if SDL_PixelTest(SpriteOne.SDL_Surface, @SpriteOneRect,
					 SpriteTwo.SDL_Surface, @SpriteTwoRect,
					 SpriteOne.Position.X, SpriteOne.Position.Y, SpriteTwo.Position.X, SpriteTwo.Position.Y) then Result := true;
  end;
end;

function PixelTest(Sprite: TelSprite; Rect: TelRect): Boolean;
var
  SpriteRect, CRect: TSDL_Rect;
begin
  Result := false;

  if (Sprite.Visible) then
  begin
    SpriteRect.x := Sprite.GetClipRect.X;
    SpriteRect.y := Sprite.GetClipRect.Y;
    SpriteRect.w := Sprite.GetClipRect.W;
    SpriteRect.h := Sprite.GetClipRect.H;
    
    CRect.x := Rect.X;
    CRect.y := Rect.Y;
    CRect.w := Rect.W;
    CRect.h := Rect.H;
    
    if SDL_PixelTestSurfaceVsRect(Sprite.SDL_Surface, @SpriteRect, @CRect, Sprite.Position.X, Sprite.Position.Y, Rect.X, Rect.Y) then Result := true;
  end;
end;

function IsRectEmpty(Rect: TelRect): Boolean;
begin
  if ((Rect.X <> 0) and
      (Rect.Y <> 0) and
	  (Rect.W > 0) and
	  (Rect.H > 0)) then Result := false else Result := true;
end;

procedure DrawLine(Src, Dst: TelPoint; Color: TelColor);
begin
  glColor3f(Color.R / 255, Color.G / 255, Color.B / 255);

  // GL_LINE_STRIP instead of GL_LINES -> http://wiki.delphigl.com/index.php/glBegin
  glBegin(GL_LINE_STRIP);
    glVertex3f(Src.X, Src.Y, 0);
    glVertex3f(Dst.X, Dst.Y, 0);
  glEnd;
end;

procedure DrawLine(Points: array of TelPoint; Color: TelColor);
var
  i: Integer;
begin
  glColor3f(Color.R / 255, Color.G / 255, Color.B / 255);

  // GL_LINE_STRIP instead of GL_LINES -> http://wiki.delphigl.com/index.php/glBegin
  glBegin(GL_LINE_STRIP);
    for i := 0 to High(Points) - 1 do
    begin
      glVertex3f(Points[i].X, Points[i].Y, 0);
      glVertex3f(Points[i+1].Y, Points[i+1].Y, 0);
    end;
  glEnd;
end;

//
// TelObject
//

constructor TelObject.Create;
begin
  FName := '';
  FNote := '';
  FID := 0;
end;

destructor TelObject.Destroy;
begin
  inherited Destroy;
end;

function TelObject.GetUniqueID: String;
begin
  Result := Self.ClassName + IntToStr(Integer(Self));
end;

//
// TelGraphicObject
//

constructor TelGraphicObject.Create;
begin
  inherited Create;
end;

destructor TelGraphicObject.Destroy;
begin
  if not Cloned then SDL_FreeSurface(SDL_Surface);

  inherited Destroy;
end;

procedure TelGraphicObject.SaveToBitmap(Filename: string);
begin
  if LowerCase(GetFilenameExtension(Filename)) <> 'bmp' then Filename := Filename + '.bmp';
  SDL_SaveBMP(SDL_Surface, PChar(Filename));
end;

function TelGraphicObject.GetAlignVertical(): TAlignVertical;
begin

end;

function TelGraphicObject.GetAlignHorizontal(): TAlignHorizontal;
begin

end;

procedure TelGraphicObject.SetAlignVertical(VerticalAlignment: TAlignVertical);
begin

end;

procedure TelGraphicObject.SetAlignHorizontal(HorizontalAlignment: TAlignHorizontal);
begin

end;

procedure TelGraphicObject.Move(Point: TelPoint);
begin

end;


{$IFDEF USE_LOGGER}

//
// TelLogger
//

constructor TelLogger.Create;
begin
  inherited;

  try
    fMessageList := TStringList.Create;
  finally
    WriteLog('Start log', ltNote);
  end;
end;

class function TelLogger.GetInstance: TelLogger;
begin
  if (Logger = nil) then Logger := TelLogger.Create;
  Result := Logger;
end;

destructor TelLogger.Destroy;
begin
  try
    WriteLog('End log', ltNote);
  finally
    fMessageList.SaveToFile(ExtractFilename(ParamStr(0)) + '.log');
  end;

  inherited;
end;

procedure TelLogger.WriteLog(LogString: String; LogType: TLogType = ltError);
var PreString: String;
begin
  try
    case LogType of
      ltError:   PreString := '- Error   - ';
      ltWarning: PreString := '- Warning - ';
	  ltNote:    PreString := '- Note    - ';
    end;

    PreString := PreString + DateTimeToStr(Now) + ' - ';
  finally
    fMessageList.Add(PreString + LogString);
  end;
end;
	
{$ENDIF}
	

constructor TelColorHelper.Create;
begin
  inherited Create;

  FBufferEmpty := true;
  fLastUsedColor := makeCol(0, 0, 0);
end;

destructor TelColorHelper.Destroy;
begin
  ClearBuffer;

  inherited Destroy;
end;

function TelColorHelper.clBlack: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 0, 0);
  Result := fLastUsedColor;
end;

function TelColorHelper.clMaroon: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(128, 0, 0);
  Result := fLastUsedColor;
end;

function TelColorHelper.clGreen: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 128, 0);
  Result := fLastUsedColor;
end;

function TelColorHelper.clOlive: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(128, 128, 0);
  Result := fLastUsedColor;
end;

function TelColorHelper.clNavy: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 0, 128);
  Result := fLastUsedColor;
end;

function TelColorHelper.clPurple: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(128, 0, 128);
  Result := fLastUsedColor;
end;

function TelColorHelper.clTeal: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 128, 128);
  Result := fLastUsedColor;
end;

function TelColorHelper.clGray: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(128, 128, 128);
  Result := fLastUsedColor;
end;

function TelColorHelper.clSilver: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(192, 192, 192);
  Result := fLastUsedColor;
end;

function TelColorHelper.clRed: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(255, 0, 0);
  Result := fLastUsedColor;
end;

function TelColorHelper.clLime: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 255, 0);
  Result := fLastUsedColor;
end;

function TelColorHelper.clBlue: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 0, 255);
  Result := fLastUsedColor;
end;

function TelColorHelper.clFuchsia: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(255, 0, 255);
  Result := fLastUsedColor;
end;

function TelColorHelper.clAqua: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(0, 255, 255);
  Result := fLastUsedColor;
end;

function TelColorHelper.clCornflowerBlue: TelColor; 
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(100, 149, 237);
  Result := fLastUsedColor;
end;

function TelColorHelper.clWhite: TelColor;
begin
  FBufferEmpty := false;
  fLastUsedColor := makeCol(255, 255, 255);
  Result := fLastUsedColor;
end;

function TelColorHelper.GetLastUsedColor: TelColor;
begin
  if (not FBufferEmpty) then Result := fLastUsedColor;
end;

procedure TelColorHelper.ClearBuffer;
begin
  FBufferEmpty := true;
end;



constructor TelKeyboardHelper.Create;
begin
  inherited Create;
end;

destructor TelKeyboardHelper.Destroy;
begin
  FLastUsedKey := 0;

  inherited Destroy;
end;

function TelKeyboardHelper.isKeyDown(Key: Cardinal): Boolean;
begin
  fLastUsedKey := Key;

  if SDL_GetModState = Key then Result := Window.ModKeyDown[Key]
                           else Result := Window.KeyDown[Key];

end;

function TelKeyboardHelper.isKeyHit(Key: Cardinal): Boolean;
begin
  fLastUsedKey := Key;
  
  if isKeyDown(Key) then
  begin
    Result := true;
    if SDL_GetModState = Key then Window.ModKeyDown[Key] := false
    else Window.KeyDown[Key] := false;
  end else Result := false;
end;

function TelKeyboardHelper.isKeyUp(Key: Cardinal): Boolean;
begin
  fLastUsedKey := Key;

  if SDL_GetModState = Key then Result := Window.ModKeyUp[Key]
                           else Result := Window.KeyUp[Key];

end;

function TelKeyboardHelper.GetLastUsedKey(): LongWord;
begin
  Result := fLastUsedKey;
end;

constructor TelJoystickHelper.Create;
begin
  inherited Create;
end;

destructor TelJoystickHelper.Destroy;
begin
  inherited Destroy;
end;

constructor TelMouseHelper.Create;
begin
  inherited Create;
end;

destructor TelMouseHelper.Destroy;
begin
  inherited Destroy;
end;

function TelMouseHelper.isButtonDown(Button: LongWord): Boolean;
begin
  Result := Window.MouseButtonDown[Button];
end;

function TelMouseHelper.isButtonHit(Button: LongWord): Boolean;
begin
  if Window.MouseButtonDown[Button] then
  begin
	Window.MouseButtonDown[Button] := false;
	Result := true;
  end else Result := false;
end;

function TelMouseHelper.isButtonUp(Button: LongWord): Boolean;
begin
  Result := Window.MouseButtonUp[Button];
end;

function TelMouseHelper.LeftClick: Boolean;
begin
  Result := isButtonHit(BUTTON_LEFT);
end;

function TelMouseHelper.RightClick: Boolean;
begin
  Result := isButtonHit(BUTTON_RIGHT);
end;

function TelMouseHelper.WheelDown(): Boolean;
begin
  Result := Window.MouseButtonUp[BUTTON_WHEELDOWN];
end;

function TelMouseHelper.WheelUp(): Boolean;
begin
  Result := Window.MouseButtonUp[BUTTON_WHEELUP];
end;

function TelMouseHelper.Motion(): Boolean;
begin
  fLastCursorPos := Window.Cursor;
end;

function TelMouseHelper.Motion(Point: TelPoint): Boolean;
begin

end;

function TelMouseHelper.Motion(Rect: TelRect): Boolean;
begin

end;

{
  #############################################################################
  # TelInputHelper                                                            #
  #############################################################################

  Description:
    Handles keyboard, mouse and joystick inputs

  Additional Notes: -

}

constructor TelInputHelper.Create;
begin
  Keyboard := TelKeyboardHelper.Create;
  Joystick := TelJoystickHelper.Create;
  Mouse := TelMouseHelper.Create;
end;

destructor TelInputHelper.Destroy;
begin
  Keyboard.Destroy;
  Joystick.Destroy;
  Mouse.Destroy;
end;

{
  #############################################################################
  # TAppContainer                                                             #
  #############################################################################

  Description:


  Additional Notes: Essential class

}

constructor TAppContainer.Create;
begin
  inherited Create;

  FRun := true;
  UnicodeSupport := true;
  
  {$IFDEF USE_SDL_MIXER}
  FAudio_rate := 22050;
  FAudio_rate_Mac := 44100;
  FAudio_format := AUDIO_S16; 
  FAudio_channels := 2;
  FAudio_buffers := 4096;
  {$ENDIF}
end;

destructor TAppContainer.Destroy;
begin
  if Window <> nil then Window.Destroy;

  if FInitialized then
  begin
    {$IFDEF USE_SDL_MIXER} Mix_CloseAudio; {$ENDIF}
    SDL_Quit;
  end;

  inherited Destroy;

  Halt(0);
end;

function TAppContainer.Initialize: Boolean;
begin
  Result := true;

  {$IFDEF USE_SDL_MIXER}
  if SDL_Init(SDL_INIT_EVERYTHING) <> 0 then
  {$ELSE}
  if SDL_Init(SDL_INIT_VIDEO or SDL_INIT_TIMER or SDL_INIT_CDROM or SDL_INIT_JOYSTICK) <> 0 then
  {$ENDIF}
  begin
    Result := false;
	  {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('Error initializing SDL'); {$ENDIF}
    Exit;
  end else FInitialized := true;

  // Laden und Initalisieren von OpenGL
  
    {$IFDEF USE_DGL_HEADER}
    InitOpenGL;
    ReadExtensions;
    {$ENDIF}
    //ReadImplementationProperties;
    

  {$IFDEF USE_SDL_MIXER}
  
    {$IFDEF DARWIN}
    Mix_OpenAudio(FAudio_Rate_Mac, FAudio_format, FAudio_channels, FAudio_buffers);
    {$ELSE}
    Mix_OpenAudio(FAudio_Rate, FAudio_format, FAudio_channels, FAudio_buffers);
    {$ENDIF}

  {$ENDIF}

  {$IFDEF USE_SDL_TTF}
  if TTF_Init <> 0 then
  begin
    Result := false;
    {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('Could not initialize SDL_TTF'); {$ENDIF}
	  Exit;
  end;
  {$ENDIF}

end;

function TAppContainer.Initialize(Name: String; Width, Height, Bits: Integer; Fullscreen: Boolean; VideoFlag: TelVideoFlags): Boolean;
begin
  Initialize();
  CreateWindow(Name, Width, Height, Bits, Fullscreen, VideoFlag);
end;

function TAppContainer.Initialize(Width, Height, Bits: Integer; Fullscreen: Boolean = false): Boolean;
begin
  Initialize();
  CreateWindow('', Width, Height, Bits, Fullscreen);
end;

function TAppContainer.CreateWindow(Name: String; Width, Height, Bits: Integer; Fullscreen: Boolean; VideoFlag: TelVideoFlags): Boolean;
begin
  if Window = nil then
  begin
    if Name = '' then Name := 'Elysion Application';
    Window := TelWindow.Create;
    Window.ID := 0;
    Window.Name := Name;
    Result := Window.SetVideoMode(Width, Height, Bits, Fullscreen, VideoFlag);
  end else
  begin
    {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('ElysionKronos supports only one window at a time at the moment.'); {$ENDIF}
    Result := false;
  end;

end;

function TAppContainer.CreateWindow(Name: String; Width, Height, Bits: Integer; Fullscreen: Boolean = false): Boolean;
begin
  Result := CreateWindow(Name, Width, Height, Bits, Fullscreen, vfAuto);
end;

procedure TAppContainer.Quit;
begin
  FRun := false;
end;

function TAppContainer.GetUnicodeSupport: Boolean;
begin
  if SDL_EnableUNICODE(-1) = 0 then Result := false
                               else Result := true;
end;

procedure TAppContainer.SetUnicodeSupport(Value: Boolean);
begin
  if Value then SDL_EnableUNICODE(1)
           else SDL_EnableUNICODE(0);
end;

{
  #############################################################################
  # TelWindow                                                                #
  #############################################################################

  Description:
    TelWindow provides a window to draw the graphical objects on.

  Additional Notes: -

}

constructor TelWindow.Create;
var i: Integer;
begin
  inherited Create;

  Cursor := makeP2D(0, 0);
  fFPSTimer := TelTimer.Create;
  fDeltaTimer := TelTimer.Create;

  FHideCursor := true;
  
  fLimitFPS := true;
  fFrameCap := 60.0;
  
  FFullscreenFlag := SDL_FULLSCREEN;

  FResolution.X := 0;
  FResolution.Y := 0;
  FResolution.Z := 0;

  FVideoFlag := vfNull;

  fCaption := nil;
  fIcon := nil;
  
  FBackgroundChange := False;
  FFoVY := 60.0;
  FZNear := 0.1;
  FZFar := 128.0;

  for i := 0 to High(KeyUp) do
  begin
    KeyDown[i] := false;
    KeyUp[i] := false;
  end;

  for i := 0 to High(MouseButtonUp) do
  begin
    MouseButtonDown[i] := false;
    MouseButtonUp[i] := false;
  end;

  for i := KMOD_NONE to KMOD_META do
  begin
    ModKeyDown[i] := false;
    ModKeyUp[i] := false;
  end;

  if FHideCursor then SDL_ShowCursor(0)
                 else SDL_ShowCursor(1);
                 

end;

destructor TelWindow.Destroy;
begin
  if Self.SDL_Surface <> nil then
  begin
    if FFullscreen then ToggleFullscreen();
    SDL_FreeSurface(Self.SDL_Surface);
  End;

  inherited;
end;

function TelWindow.SetVideoMode(_Width, _Height, _ColorBits: Integer; _Fullscreen: Boolean; _VideoFlag: TelVideoFlags): Boolean;
var Changed: Boolean;
    VideoInfo: PSDL_VideoInfo;
begin
  Result := false;

  if _VideoFlag = vfNull then FVideoFlag := vfAuto;

  if _Width <> FResolution.X then
  begin
    FResolution.X := _Width;
    Changed := true;
  end;
  if _Height <> FResolution.Y then
  begin
    FResolution.Y := _Height;
    Changed := true;
  end;
  if _ColorBits <> FResolution.Z then
  begin
    FResolution.Z := _ColorBits;
    Changed := true;
  end;
  if FVideoFlag <> _VideoFlag then
  begin
    FVideoFlag := _VideoFlag;
    Changed := true;

    if FVideoFlag = vfAuto then
    begin
      VideoInfo := SDL_GetVideoInfo;

      if VideoInfo = nil then
      begin
        Destroy;
        Exit;
      end;

	    if VideoInfo.hw_available <> 0  then
	    begin
        FVideoFlag := vfHardware;
		    {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('Using hardware surface.', ltNote); {$ENDIF}
	    end else
	    begin
		    FVideoFlag := vfSoftware;
		    {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('Using software surface.', ltNote); {$ENDIF}
	    end;

      VideoInfo := nil;
    end;
  end;
  if FFullscreen <> _Fullscreen then
  begin
    FFullscreen := _Fullscreen;
    Changed := true;
  end;

  if Changed then
  begin
    FFlags := SDL_DOUBLEBUF or SDL_HWPALETTE;

    //if FVideoFlag = vfHardware then FFlags := FFlags or SDL_HWSURFACE;
    //if FVideoFlag = vfSoftware then FFlags := FFlags or SDL_SWSURFACE;
    
    FFlags := FFlags or SDL_OPENGL;
    
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
  
    SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE,  8 );

    if FFullscreen then
    begin
      FGFlags := FFlags or FFullscreenFlag;
      Self.SDL_Surface := SDL_SetVideoMode(FResolution.X, FResolution.Y, FResolution.Z, FGFlags);
    end  else Self.SDL_Surface := SDL_SetVideoMode(FResolution.X, FResolution.Y, FResolution.Z, FFlags);
    
    glClearDepth(1.0);                                    // Depth Buffer Setup
    glEnable(GL_DEPTH_TEST);                        // Aktiviert Depth Testing
    glDepthFunc(GL_ALWAYS);                        // Bestimmt den Typ des Depth Testing
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Qualitativ bessere Koordinaten
                                                       // Interpolation

    
    SetBackgroundColor(Color.clBlack);
    
    glViewport(0, 0, FResolution.X, FResolution.Y);
    SetProjection(pmOrtho);
    glTranslatef(0, 0, 0);

    if not Assigned(Self.SDL_Surface) then
    begin
      Result := false;
	    {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('Surface not assigned', ltError); {$ENDIF}
            Exit;
            Destroy;
    end else 
	begin
	  Self.Caption := Self.Name;
	  Result := true;
	end;
    Changed := false;
  end;
end;

function TelWindow.SetVideoMode(_Width, _Height, _ColorBits: Integer; _Fullscreen: Boolean = False): Boolean;
begin
  Result := SetVideoMode(_Width, _Height, _ColorBits, _Fullscreen, FVideoFlag);
end;

procedure TelWindow.ToggleFullscreen();
begin
  FFullscreen := not FFullscreen;

  {$IFDEF LINUX}
  SDL_WM_ToggleFullScreen(Self.SDL_Surface);
  {$ELSE}
  SetVideoMode(FResolution.X, FResolution.Y, FResolution.Z, FFullscreen);
  {$ENDIF}
end;

procedure TelWindow.BeginScene();
begin
	fFPSTimer.start();
	
	//if (fDeltaTimer.getTicks > 0) then fDeltaTime := (fDeltaTimer.getTicks / 1000);

	if FBackgroundChange then glClearColor(FBackgroundColor.R / 255, FBackgroundColor.G / 255, FBackgroundColor.B / 255, 0.5);
	 
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
	  
	glLoadIdentity;
end;

procedure TelWindow.SetProjection(ProjectionMode: TProjectionMode);
begin
  glMatrixMode( GL_PROJECTION );
  
  glLoadIdentity;
  
  case ProjectionMode of
    pmOrtho: glOrtho(0, FResolution.X, FResolution.Y, 0,  0, FZFar);
    pmPerspective: gluPerspective(FFoVY, FResolution.X / FResolution.Y, FZNear, FZFar);
  end;
  
  glMatrixMode( GL_MODELVIEW );
  
end;

procedure TelWindow.EndScene;
begin
  
  HandleEvents;
  
  glFlush(); 
  SDL_GL_SwapBuffers();
 
  //fDeltaTimer.start(); // restart delta timer
  
  if fLimitFPS then
  begin
  	if ( fFPSTimer.getTicks < (1000 / fFrameCap) ) then
  	begin
      SDL_Delay( Abs(Trunc(1000 / fFrameCap) - fFPSTimer.getTicks) ); // Abs - Dirty Hack!!! TODO: Find an even dirtier hack ;)
    end;
  end;

end;

procedure TelWindow.SetFPS(Value: Single);
begin
  if Value > 0 then fFrameCap := Value;
end;

function TelWindow.GetFPS: Single;
begin
	(*if ((fFPSTimer.getTicks > 0) and (fFrames > 0)) then
	begin
		Result := fFrames / ( fFPSTimer.getTicks() / 1000 );
	end else Result := 0;*)
	
	if (not fLimitFPS) then Result := 1000 / fFPSTimer.getTicks();
end;

function TelWindow.GetDeltaTime: Double;
begin
	//if fDeltaTime > 0 then Result := fDeltaTime;
end;

function TelWindow.GetWidth: Integer;
begin
  Result := FResolution.X;
end;

function TelWindow.GetHeight: Integer;
begin
  Result := FResolution.Y;
end;

function TelWindow.GetBitsPerPixel: Integer;
begin
  Result := FResolution.Z;
end;

procedure TelWindow.SetBackgroundImage(Surface: PSDL_Surface); {$IFDEF FPC} Overload; {$ENDIF}
begin
  if Surface <> nil then
  begin
    UseBackgroundImage := true;
    fBackgroundImage := Surface;
    FBackgroundChange := true;
  end;
end;

procedure TelWindow.SetBackgroundImage(const Filename: String); {$IFDEF FPC} Overload; {$ENDIF}
var Directory: String;
begin
  
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));
	  if FileExists(Directory+Filename) then
	  begin
	    UseBackgroundImage := true;

		{$IFDEF USE_SDL_IMAGE}
	    fBackgroundImage := IMG_Load(PChar(Directory+Filename));
		{$ENDIF}
		{$IFDEF USE_VAMPYRE}
		fBackgroundImage := LoadSDLSurfaceFromFile(Directory+Filename);
		{$ENDIF}

    {$IFNDEF USE_LOGGER}
	  end;
    {$ELSE}
    end else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specifies.', ltError);
  {$ENDIF}
end;

procedure TelWindow.SetBackgroundColor(Color: TelColor);
begin
  UseBackgroundImage := false;
  FBackgroundChange := true;
  FBackgroundColor := Color;
end;

function TelWindow.GetCaption: String;
begin
  Result := String(fCaption);
end;

procedure TelWindow.SetCaption(Caption: String);
begin
  fCaption := PChar(Caption);
  SDL_WM_SetCaption(fCaption, fCaption);
end;

procedure TelWindow.SetIcon(Filename: String);
var TMP_Icon: PSDL_Surface;
    Directory: String;
begin
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));

    if FileExists(Directory + Filename) then
    begin
	  {$IFDEF USE_SDL_IMAGE}
      TMP_Icon := IMG_Load(PChar(Directory + Filename));
	  {$ENDIF}
	  {$IFDEF USE_VAMPYRE}
	  TMP_Icon := LoadSDLSurfaceFromFile(Directory+Filename);
	  {$ENDIF}
      FIcon := PChar(Filename);
      SDL_WM_SetIcon(TMP_Icon, 0);
    {$IFNDEF USE_LOGGER}
	  end;
    {$ELSE}
    end else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specified.', ltError);
  {$ENDIF}
  
  if TMP_Icon <> nil then SDL_FreeSurface(TMP_Icon);
end;

procedure TelWindow.SetIcon(Filename: String; Mask: TelColor);
var TMP_Icon: PSDL_Surface;
    Directory: String;
begin
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));

    if FileExists(Directory + Filename) then
    begin
      {$IFDEF USE_SDL_IMAGE}
	  TMP_Icon := IMG_Load(PChar(Directory + Filename));
	  {$ENDIF}
	  {$IFDEF USE_VAMPYRE}
	  TMP_Icon := LoadSDLSurfaceFromFile(Directory+Filename);
	  {$ENDIF}
	  if TMP_Icon <> Nil then
        SDL_SetColorKey(TMP_Icon, SDL_SRCCOLORKEY or SDL_RLEACCEL or SDL_HWACCEL,
      SDL_MapRGB(TMP_Icon.Format, Mask.R, Mask.G, Mask.B));
	  
      FIcon := PChar(Filename);
      SDL_WM_SetIcon(TMP_Icon, 0);
    {$IFNDEF USE_LOGGER}
	  end;
    {$ELSE}
    end else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specified.', ltError);
  {$ENDIF}
  
  if TMP_Icon <> nil then SDL_FreeSurface(TMP_Icon);
end;

procedure TelWindow.HideCursor;
begin
  FHideCursor := true;
  SDL_ShowCursor(0);
end;

procedure TelWindow.ShowCursor;
begin
  FHideCursor := false;
  SDL_ShowCursor(1);
end;

procedure TelWindow.TakeScreenshot(Filename: String);
begin
  if LowerCase(GetFilenameExtension(Filename)) <> 'bmp' then Filename := Filename + '.bmp';
  SDL_SaveBMP(SDL_Surface, PChar(Filename));
end;

function TelWindow.OnPoint(Coord: TelPoint; Rect: TelRect): Boolean;
begin
  if (Coord.X >= Rect.x) and
     (Coord.X >= Rect.y) and
     (Coord.X < (Rect.x + Rect.w)) and
     (Coord.Y < (Rect.y + Rect.h)) then Result := true else Result := false;
end;

procedure TelWindow.HandleEvents;
var i: Integer;
begin
   for i := 0 to High(KeyUp) do
  begin
    if KeyUp[i] then KeyUp[i] := false;
  end;

  for i := KMOD_NONE to KMOD_META do
  begin
    if ModKeyUp[i] then ModKeyUp[i] := false;
  end;

  for i := 0 to High(MouseButtonUp) do
  begin
    if MouseButtonUp[i] then MouseButtonUp[i] := false;
  end;

  while (SDL_PollEvent(@FEvent) = 1) do
  begin

    case FEvent.type_ of
    SDL_QUITEV:
       begin
         if ((Application <> nil) and (Self.SDL_Surface <> nil)) then Application.Quit;
       end;

    SDL_KEYUP:
        begin
          KeyDown[FEvent.key.keysym.sym] := false;
          KeyUp[FEvent.key.keysym.sym] := true;

          ModKeyDown[FEvent.key.keysym.modifier] := false;
          ModKeyUp[FEvent.key.keysym.modifier] := true;
        End;

    SDL_KEYDOWN:
        begin
          KeyDown[FEvent.key.keysym.sym] := true;
          KeyUp[FEvent.key.keysym.sym] := false;

          ModKeyDown[FEvent.key.keysym.modifier] := true;
          ModKeyUp[FEvent.key.keysym.modifier] := false;
        end;

    SDL_MOUSEMOTION:
        begin
          SDL_GetMouseState(Cursor.X, Cursor.Y);
        end;
 
    SDL_MOUSEBUTTONDOWN:
        begin
          MouseButtonDown[FEvent.button.button] := true;
          MouseButtonUp[FEvent.button.button] := false;
        end;

    SDL_MOUSEBUTTONUP:
        begin
          MouseButtonDown[FEvent.button.button] := false;
          MouseButtonUp[FEvent.button.button] := true;
        end;

    end;

  end;
end;

function TelWindow.isActive: Boolean;
begin
  if ((SDL_GetAppState = SDL_APPACTIVE) or
      (SDL_GetAppState = SDL_APPMOUSEFOCUS) or
      (SDL_GetAppState = SDL_APPINPUTFOCUS)) then Result := true
                                             else Result := false;
end;


{
  #############################################################################
  # TelCamera                                                                 #
  #############################################################################

  Description:
    Inserts a camera

  Additional Notes: -

}

constructor TelCamera.Create;
begin

end;

procedure TelCamera.Move(Point: TelPoint);
begin

end;

{
  #############################################################################
  # TelGameStates                                                             #
  #############################################################################

  Description:
    Handles different states.

    Should be combinded with TelScene

  Additional Notes: -

}

constructor TelGameState.Create;
begin

end;

destructor TelGameState.Destroy;
begin

end;


constructor TelGameStateManager.Create;
begin

end;

destructor TelGameStateManager.Destroy;
begin
  //if fGameState <> nil then fGameState.Destroy;
end;

procedure TelGameStateManager.Render;
begin
  //if fGameState <> nil then fGameState.Render;
end;

procedure TelGameStateManager.Update;
begin
  //if fGameState <> nil then fGameState.Update;
end;

procedure TelGameStateManager.HandleEvents;
begin
  //if fGameState <> nil then fGameState.HandleEvents;
end;

procedure TelGameStateManager.SetState(GameState: TelGameState);
begin
  //fGameState := GameState;
end;

{
  #############################################################################
  # TelScene                                                                  #
  #############################################################################

  Description:
    Allows the developer to put different graphical objects in one scene.
    Use of TelScene is encouraged, more resource-friendly and clearly arranged
    than using 100 graphical objects for example

    Can be combined with TelSpriteEngine

  Additional Notes: -

}

constructor TelScene.Create(Width: Integer; Height: Integer);
begin
  inherited Create;
end;

constructor TelScene.Create(Width: Integer; Height: Integer; Mask: TRGBAColor);
begin
  inherited Create;
end;

destructor TelScene.Destroy;
begin
  inherited Destroy;
end;

//
// TelTimer
//
constructor TelTimer.Create;
begin
  inherited Create;

  FActive := false;
  FPaused := false;

  FStartTicks := 0;
  FStoredTicks := 0;
  FOldTicks := 0;

  FInterval := 0;
end;

constructor TelTimer.Create(Interval: Integer);
begin
  inherited Create;

  FActive := false;
  FPaused := false;

  FStartTicks := 0;
  FStoredTicks := 0;
  FOldTicks := 0;

  FInterval := Interval;
end;

destructor TelTimer.Destroy;
begin
  inherited Destroy;
end;

procedure TelTimer.Start;
begin
  FActive := true;
  FPaused := false;

  FStartTicks := SDL_GetTicks();
  FOldTicks := 0;
end;

procedure TelTimer.Stop;
begin
  FActive := false;
  FPaused := false;
end;

procedure TelTimer.Pause;
begin
  if (Active) and (not Paused) then
  begin
    FPaused := true;

    FStoredTicks := SDL_GetTicks() - FStartTicks;
  end;
end;

procedure TelTimer.UnPause;
begin
  if Paused then
  begin
    FPaused := false;

    //Reset the starting ticks
    FStartTicks := SDL_GetTicks() - FStoredTicks;

    //Reset the paused ticks
    FStoredTicks := 0;
  end;
end;

function TelTimer.GetTicks;
begin
  Result := 0;

  if Active then
  begin
    if Paused then Result := FStoredTicks
              else Result := SDL_GetTicks() - FStartTicks;
  end;
end;

function TelTimer.Event: Boolean;
begin
  Result := false;

  if (Active) and (not Paused) then
  begin
    if (GetTicks > 0) and (Interval > 0) then
    begin
      if (GetTicks - Interval) >= FOldTicks then
      begin
        Result := true;
        FOldTicks := GetTicks;
      end;
    end;
  end;
end;

{$IFDEF USE_SFONT}
constructor TelBitmapFont.Create;
begin
  inherited Create;
  
  FAlpha := 255;
end;

destructor TelBitmapFont.Destroy;
begin
  if fFontInfo.Surface <> nil then SDL_FreeSurface(fFontInfo.Surface);

  inherited Destroy;
end;

procedure TelBitmapFont.LoadFromFile(Filename: String);
var Directory: String;
begin
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));

    if FileExists(Filename) then
    begin
      {$IFDEF USE_SDL_IMAGE}
	    FFontInfo.Surface := IMG_Load(PChar(Directory + Filename));
	  {$ENDIF}
	  {$IFDEF USE_VAMPYRE}
	    FFontInfo.Surface := LoadSDLSurfaceFromFile(Directory+Filename);
	  {$ENDIF}
      SFont_InitFont2(@FFontInfo);
    {$IFNDEF USE_LOGGER}
	  end;
    {$ELSE}
    end else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specifies.', ltError);
  {$ENDIF}

end;

procedure TelBitmapFont.SetTransparency(Color: TelColor);
begin
  if FFontInfo.Surface <> nil then
    SDL_SetColorKey(FFontInfo.Surface, SDL_SRCCOLORKEY or SDL_RLEACCEL or SDL_HWACCEL,
      SDL_MapRGB(FFontInfo.Surface.Format, Color.R, Color.G, Color.B));
end;

procedure TelBitmapFont.SetTransparency(Coord: TelPoint);
begin
  if FFontInfo.Surface <> nil then
    SDL_SetColorKey(FFontInfo.Surface, SDL_SRCCOLORKEY or SDL_RLEACCEL or SDL_HWACCEL,
      SDL_GetPixel(FFontInfo.Surface, Coord.X, Coord.Y));
end;
    
procedure TelBitmapFont.SetAlpha(Alpha: Byte);
begin
  FAlpha := Alpha;
end;

function TelBitmapFont.GetAlpha: Byte;
begin
  Result := FAlpha;
end;
    
function TelBitmapFont.GetWidth(Text: String): Integer;
begin
  Result := SFont_TextWidth2(@FFontInfo, PChar(Text));
end;
    
function TelBitmapFont.GetHeight: Integer;
begin
  Result := FFontInfo.Surface.h;
end;

procedure TelBitmapFont.TextOut(Point: TelPoint; Text: String;  AlignV: TAlignVertical = avNone; AlignH: TAlignHorizontal = ahNone);
begin
  if FAlpha <> 255 then SDL_SetAlpha(FFontInfo.Surface, SDL_SRCALPHA, FAlpha);

  case AlignV of
    avTop: Point.Y := 0;
	avCenter: Point.Y := (Surface.SDL_Surface.h - GetHeight) div 2;
	avButtom: Point.Y := Surface.SDL_Surface.h - GetHeight;
  end;
  case AlignH of
    ahLeft: Point.X := 0;
	ahCenter: Point.X := (Surface.SDL_Surface.w - GetWidth(Text)) div 2;
	ahRight: Point.X := Surface.SDL_Surface.w - GetWidth(Text);
  end;
  
  SFont_Write2(Surface.SDL_Surface, @FFontInfo, Point.X, Point.Y, PChar(Text));
end;
{$ENDIF}

//
// TelTrueTypeFont
//
function TelTrueTypeFont.RenderFont(Text: String; FontRender: TFontRender; TextStyle: TTextStyle): PSDL_Surface;
var
  TMP_Surface: PSDL_Surface;
begin
  
  Result := nil; 
  
  if Text <> '' then
	begin
      case FontRender of
		rtSolid: 
		begin
		  case TextStyle of
		    tsNormal: TMP_Surface := TTF_RenderText_Solid(fFont, PChar(Text), convCol(fColor));
			tsUnicode: TMP_Surface := TTF_RenderUNICODE_Solid(fFont, PUInt16(Text), convCol(fColor));
			tsUTF8: TMP_Surface := TTF_RenderUTF8_Solid(fFont, PChar(Text), convCol(fColor));
		  end;
		
	    end;
		rtBlended: 
		begin
		  case TextStyle of
		    tsNormal: TMP_Surface := TTF_RenderText_Blended(fFont, PChar(Text), convCol(fColor));
			tsUnicode: TMP_Surface := TTF_RenderUNICODE_Blended(fFont, PUInt16(Text), convCol(fColor));
			tsUTF8: TMP_Surface := TTF_RenderUTF8_Blended(fFont, PChar(Text), convCol(fColor));
		  end;
	    end;
		
	  end;	
	  
	  Result := SDL_DisplayFormatAlpha(TMP_Surface);
	  //Result := TMP_Surface;
	  SDL_FreeSurface(TMP_Surface);
	 
	end;
end;

constructor TelTrueTypeFont.Create();
begin
  inherited Create;
  fColor := makeCol(0, 0, 0);
  fChange := true;
  fFont := nil;

  fTrim := true;
  fMargin := 2;
end;

destructor TelTrueTypeFont.Destroy();
begin
  if fFont <> nil then TTF_CloseFont(fFont);
  if (fSurface <> nil) then SDL_FreeSurface(fSurface);
  
  inherited;
end;

function TelTrueTypeFont.GetFontPointer(): PTTF_Font;
begin
  Result := fFont;
end;

procedure TelTrueTypeFont.VirtualClone(FromFont: TelTrueTypeFont);
  //var tmp: ^PTTF_Font;
begin
  //tmp := nil;
  if FromFont.GetFontPointer <> Nil then 
  begin
    fFont := FromFont.GetFontPointer;
    //New(tmp);
	//tmp^ := FromFont.GetFontPointer;
	//Self.fFont := tmp^;
	//Dispose(tmp);
	
	//Self.SetRenderType(FromFont.GetRenderType);
  end;
end;

procedure TelTrueTypeFont.LoadFromFile(Filename: String; PtSize: Integer = 8);
var Directory: String;
begin
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));

    if FileExists(Directory + Filename) then
	  begin
      fFont := TTF_OpenFont(PChar(Directory + Filename), PtSize);
      Style := [];
      fTextStyle := tsUTF8; // Maybe tsNormal instead?
      fFontRender := rtSolid;
	  {$IFNDEF USE_LOGGER}
	  end;
    {$ELSE}
    end else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specifies.', ltError);
  {$ENDIF}
end;

procedure TelTrueTypeFont.SetFontStyle(FontStyles: TFontStyles);
var FontFlag: Cardinal;
begin
    if FontStyles <> fFontStyles then
    begin
        fFontStyles := FontStyles;
        fChange := true;
    
	    if fFontStyles = [] then TTF_SetFontStyle(fFont, TTF_STYLE_NORMAL)
		else begin
		  if fsBold in fFontStyles then FontFlag := TTF_STYLE_BOLD;
		  if fsItalic in fFontStyles then FontFlag := FontFlag or TTF_STYLE_ITALIC;
		  if fsUnderline in fFontStyles then FontFlag := FontFlag or TTF_STYLE_UNDERLINE;
		  
		  TTF_SetFontStyle(fFont, FontFlag)
		end;
    end;
end;

function TelTrueTypeFont.GetFontStyle: TFontStyles;
begin
  Result := fFontStyles;
end;

procedure TelTrueTypeFont.SetTextStyle(TextStyle: TTextStyle);
begin
  //if TextStyle <> fTextStyle then
  //begin
    //fChange := true;
    fTextStyle := TextStyle;
  //end;
end;

function TelTrueTypeFont.GetTextStyle: TTextStyle;
begin
  Result := fTextStyle;
end;

procedure TelTrueTypeFont.SetRenderType(FontRender: TFontRender);
begin
  //if FontRender <> fFontRender then
  //begin
    //fChange := true;
    fFontRender := FontRender;
  //end;
end;

function TelTrueTypeFont.GetRenderType: TFontRender;
begin
  Result := fFontRender;
end;

procedure TelTrueTypeFont.SetColor(Color: TelColor);
begin
  //if Color <> fColor then
  //begin
    //fChange := true;
    fColor := Color;
  //end;
end;

function TelTrueTypeFont.GetColor: TelColor;
begin
  Result := fColor;
end;

procedure TelTrueTypeFont.SetText(Text: String);
begin
  if Text <> fText then
  begin
    fChange := true;
    fText := Text;
  end;
end;

function TelTrueTypeFont.GetText: String;
begin
  Result := fText;
end;

function TelTrueTypeFont.GetHeight: Integer;
var tmp: Integer;
begin
  case fTextStyle of
    tsNormal: TTF_SizeText(fFont, PChar(fText), tmp, Result);
    tsUTF8: TTF_SizeUTF8(fFont, PChar(fText), tmp, Result);
    tsUnicode: TTF_SizeUNICODE(fFont, PUInt16(fText), tmp, Result);
  end;
end;

function TelTrueTypeFont.GetWidth: Integer;
begin
  result := GetWidth_Text(fText);
end;

function TelTrueTypeFont.GetWidth_Text(Text: String): Integer;
var tmp: Integer;
begin
  case fTextStyle of
    tsNormal: TTF_SizeText(fFont, PChar(Text), Result, tmp);
    tsUTF8: TTF_SizeUTF8(fFont, PChar(Text), Result, tmp);
    tsUnicode: TTF_SizeUNICODE(fFont, PUInt16(Text), Result, tmp);
  end;
end;

procedure TelTrueTypeFont.TextOut(Point: TelVertex; Text: String; 
   AlignV: TAlignVertical = avNone; AlignH: TAlignHorizontal = ahNone);
var   
  initial, intermediary: PSDL_Surface;
  wid,hgt: Integer;
  mytexture: GLuInt;
  TextNew: String;
  TextList: TStringList;
  i, tmpTop, tmpTop2: Integer;
begin
  if Text <> '' then fText := Text;

  TextNew := StringReplace(fText, '\n', #13, [rfReplaceAll, rfIgnoreCase]);
  TextList := Split(TextNew, #13, fTrim);

  tmpTop := Point.Y;

  glEnable(GL_TEXTURE_2D);
  for i := 0 to TextList.Count - 1 do
  begin

    initial := RenderFont(TextList.Strings[i], fFontRender, fTextStyle);
    if initial = nil then Exit;

    LoadTexture(initial, Texture[i]);

    glColor3f(1.0, 1.0, 1.0);
    Bind(Texture[i]);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

      case AlignV of
          avNone:
            begin
              tmpTop2 := tmpTop + (GetHeight + Margin) * i;
              Point.Y := tmpTop2;
            end;
          avTop: Point.Y := 0 + (GetHeight + Margin) * i;
  	  avCenter: Point.Y := ((ActiveWindow.Height - GetHeight) div 2) + (GetHeight + Margin) * i;
  	  avButtom: Point.Y := ActiveWindow.Height - GetHeight - (GetHeight + Margin) * (TextList.Count - 1) + (GetHeight + Margin) * i;
      end;
  	case AlignH of
        ahLeft: Point.X := 0;
  	  ahCenter: Point.X := (ActiveWindow.Width - GetWidth_Text(TextList.Strings[i])) div 2;
  	  ahRight: Point.X := ActiveWindow.Width - GetWidth_Text(TextList.Strings[i]);
      end;

      DrawQuad(Point.X, Point.Y, GetWidth_Text(TextList.Strings[i]), GetHeight, Point.Z);
      glDisable(GL_BLEND);

       glFinish(); // wait till operation is finished

	glDeleteTextures(1, @Texture[i]); // don't forget this line, it muerto importanto! :) or else: Bad memory leak, it's bad, mkay!


  end;
  glDisable(GL_TEXTURE_2D);
	
	// Convert the rendered text to a known format
	(*wid := nextpoweroftwo(initial^.w);
	hgt := nextpoweroftwo(initial^.h);
	
	intermediary := SDL_CreateRGBSurface(0, wid, hgt, 32, 0, 0, 0, $ffffffff);

	SDL_BlitSurface(initial, nil, intermediary, nil);
	
	// Tell GL about our new texture */
	glGenTextures(1, @mytexture);
	glBindTexture(GL_TEXTURE_2D, mytexture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, wid, hgt, 0, GL_BGRA, GL_UNSIGNED_BYTE, intermediary^.pixels );
	
	// GL_NEAREST looks horrible, if scaled... */
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	
	glColor3f(1.0, 1.0, 1.0);
	glBindTexture(GL_TEXTURE_2D, mytexture);
	
	glEnable(GL_ALPHA_TEST);
    glAlphaFunc(GL_GREATER, 0.1);
  
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
	
	DrawQuad(Point.X, Point.Y, wid, GetHeight, Point.Z);
	
	glDisable(GL_BLEND);
	
	glDisable(GL_ALPHA_TEST);
	
	// Bad things happen if we delete the texture before it finishes */
	glFinish();
	
	// Clean up */
	SDL_FreeSurface(initial);
	//SDL_FreeSurface(intermediary);
	glDeleteTextures(1, @mytexture);*)

  (*  fSurface := RenderFont(Text, fFontRender, fTextStyle);
    LoadTexture(fSurface, Texture);
	
	glPushMatrix;
  Bind(Texture);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  DrawQuad(Point.X, Point.Y, GetWidth(Text), GetHeight, Point.Z);
  glDisable(GL_BLEND);
  glPopMatrix;
	*)
	


	
  //SDL_FreeSurface(fSurface);  
  
  
  {if ((fSurface <> nil) and (ParentSurface <> nil)) then
  begin
    case AlignV of
      avTop: Point.Y := 0;
	  avCenter: Point.Y := (ParentSurface.h - GetHeight) div 2;
	  avButtom: Point.Y := ParentSurface.h - GetHeight;
    end;
	case AlignH of
      ahLeft: Point.X := 0;
	  ahCenter: Point.X := (ParentSurface.w - GetWidth(Text)) div 2;
	  ahRight: Point.X := ParentSurface.w - GetWidth(Text);
    end;
    SDL_BlitSurface(fSurface, nil, ParentSurface, PSDLRect(Point.X, Point.Y, fSurface.w, fSurface.h));
  end;}
  
end;

procedure TelTrueTypeFont.Draw;
begin

end;


constructor TelSprite.Create;
begin
  inherited Create;

  fLoaded := false;
  fNull := true;
  fFilename := '';

  FClipRect.X := 0;
  FClipRect.Y := 0;
  FClipRect.W := 0;
  FClipRect.H := 0;

  FRect.X := 0;
  FRect.Y := 0;
  FRect.W := 0;
  FRect.H := 0;
  
  FAlpha := 255;
  FFrame := 0;
  FVisible := true;
  
  Scale := makeP2D(1.0, 1.0);
  
  Color := makeCol(255, 255, 255);
  
  fGenerated := false;
  fCloned := false; 
  fError := false;
  
  Rotation.Angle := 0.0;
  Rotation.Vector := makeP3D(0.0, 0.0, 1.0);
  
  Offset.Rotation := makeP2D(0, 0);
  Offset.Position := makeP2D(0, 0);
end;

destructor TelSprite.Destroy;
begin
  if SDL_Surface <> Nil then
  begin
    SDL_FreeSurface(SDL_Surface);
    glDeleteTextures(1, @Texture);
  end;

  inherited Destroy;
end;

function TelSprite.LoadFromFile(Filename: String): Boolean;
begin
  Result := LoadFromFile(Filename, makeRect(0, 0, -1, -1));
end;

function TelSprite.LoadFromFile(Filename: String; ClipRect: TelRect): Boolean;
var Directory: String;
    tmp_ClipRect: TelRect;
begin
  Result := false;

  FAnim.X := 0;
  FAnim.Y := 0;

  if Filename <> fFilename then
  begin
    fFilename := Filename;
    Directory := ExtractFilePath(ParamStr(0));
    if Filename <> '' then
    begin
	  fImageType := GetFilenameExtension(Filename);

      if FileExists(Directory+Filename) then
      begin
	    {$IFDEF USE_SDL_IMAGE}
        SDL_Surface := IMG_Load(PChar(Directory+Filename));
		{$ENDIF}
		{$IFDEF USE_VAMPYRE}
		SDL_Surface := LoadSDLSurfaceFromFile(Directory+Filename);
		{$ENDIF}

        tmp_ClipRect.X := ClipRect.X;
        tmp_ClipRect.Y := ClipRect.Y;

        if ClipRect.W <= 0 then tmp_ClipRect.W := SDL_Surface.w else tmp_ClipRect.W := ClipRect.W;
        if ClipRect.H <= 0 then tmp_ClipRect.H := SDL_Surface.h else tmp_ClipRect.H := ClipRect.H;

		FOriginalSize.X := SDL_Surface.w;
		FOriginalSize.Y := SDL_Surface.h;

        ClipImage(tmp_ClipRect);

        FAnim.W := GetSurfaceWidth div FClipRect.W;
        FAnim.H := GetSurfaceHeight div FClipRect.H;

        FMaxFrames := FAnim.W * FAnim.H;

        fGenerated := false;
        fNull := false;
		Result := true;
		
      {$IFNDEF USE_LOGGER}
	  end;
    {$ELSE}
    end else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specifies.', ltError);
  {$ENDIF}
  end;
end;

procedure TelSprite.LoadFromStream(Stream: TStream);
begin
  {$IFDEF USE_VAMPYRE}
    SDL_Surface := LoadSDLSurfaceFromStream(Stream);
  {$ENDIF}
  fGenerated := false;
end;

procedure TelSprite.SaveToStream(Stream: TStream);
begin
  {$IFDEF USE_VAMPYRE}
    SaveSDLSurfaceToStream('png', Stream, SDL_Surface);
  {$ENDIF}
end;

procedure TelSprite.Generate( GenerateMode: TGenerateMode = gmAuto );
begin
  fGenerated := true;
  LoadTexture(SDL_ConvertSurface(SDL_Surface, SDL_Surface^.format, SDL_Surface^.flags), Texture);
end;

procedure TelSprite.SetAutoTransparency(Value: Boolean);
begin
  FTransparent := Value;
  if FTransparent then SetTransparency(makeP2D(0, 0));
end;

procedure TelSprite.SetTransparency(Color: TelColor);
begin
  if SDL_Surface <> Nil then
  begin
    SDL_SetColorKey(SDL_Surface, SDL_SRCCOLORKEY or SDL_RLEACCEL or SDL_HWACCEL,
      SDL_MapRGB(SDL_Surface.Format, Color.R, Color.G, Color.B));

    fGenerated := false;
  end;
end;

procedure TelSprite.SetTransparency(Coord: TelPoint);
begin
  if SDL_Surface <> Nil then
  begin
    SDL_SetColorKey(SDL_Surface, SDL_SRCCOLORKEY or SDL_RLEACCEL or SDL_HWACCEL,
      SDL_GetPixel(SDL_Surface, Coord.X, Coord.Y));

    fGenerated := false;
  end;
end;

// Clone
procedure TelSprite.VirtualClone(FromSprite: TelSprite);
begin
  if (FromSprite.SDL_Surface <> nil) then
  begin
    fCloned := true;
	if not Generated then Self.Generate;

    Self.SDL_Surface := SDL_ConvertSurface(FromSprite.SDL_Surface,
                                           FromSprite.SDL_Surface^.format,
                                           FromSprite.SDL_Surface^.flags);
    Self.Alpha := FromSprite.Alpha;
    Self.FOriginalSize := makeP2D(FromSprite.SurfaceWidth, FromSprite.SurfaceHeight);
  end;
end;

function TelSprite.GetImageType: String;
begin
  Result := fImageType;
end;

procedure TelSprite.ClipImage(Rect: TelRect);
begin
    if (fClipRect.X <> Rect.X) then fClipRect.X := Rect.X;
    if (fClipRect.Y <> Rect.Y) then fClipRect.Y := Rect.Y;
    if (fClipRect.W <> Rect.W) then
    begin
        fClipRect.W := Rect.W;
        fRect.W := fClipRect.W;
    end;
    if (fClipRect.H <> Rect.H) then
    begin
        fClipRect.H := Rect.H;
        fRect.H := fClipRect.H;
    end;
end;

function TelSprite.GetClipRect: TelRect;
begin
  Result := FClipRect;
end;

function TelSprite.GetRect: TelRect;
begin
  Result := FRect;
end;

procedure TelSprite.Move(Coord: TelPoint);
begin
  Position.X := Position.X + Coord.X;
  Position.Y := Position.Y + Coord.Y;
end;

procedure TelSprite.Move(Coord: TelVertex);
begin
  Position.X := Position.X + Coord.X;
  Position.Y := Position.Y + Coord.Y;
  Position.Z := Position.Z + Coord.Z;
end;

procedure TelSprite.SetWidth(Width: Integer);
begin
  FRect.W := Width;
end;

function TelSprite.GetWidth: Integer;
begin
  Result := FRect.W;
end;

procedure TelSprite.SetHeight(Height: Integer);
begin
  FRect.H := Height;
end;

function TelSprite.GetHeight: Integer;
begin
  Result := FRect.H;
end;

function TelSprite.GetSurfaceWidth: Integer;
begin
  Result := FOriginalSize.X;
end;

function TelSprite.GetSurfaceHeight: Integer;
begin
  Result := FOriginalSize.Y;
end;

procedure TelSprite.SetFrame(Frame: Integer);
begin
   if Frame <= FMaxFrames then
   begin
     FFrame := Frame - 1;
     if FFrame < 0 then FFrame := 0;


     if FAnim.H = 1 then FAnim.X := FFrame
     else
     begin
       FAnim.Y := FAnim.H - 1;
       FAnim.X := FFrame - FAnim.H;
     end;

     ClipImage(makeRect(FAnim.X * FClipRect.W, FAnim.Y * FClipRect.H, FClipRect.W, FClipRect.H));
   end;
end;

function TelSprite.GetFrame: Integer;
begin
  Result := FFrame;
end;

function TelSprite.GetMaxFrames: Integer;
begin
  Result := FMaxFrames;
end;

procedure TelSprite.Draw();
begin
  if fNull then
  begin
    TelLogger.GetInstance.WriteLog(Self.UniqueID + ' @ Filename: ' + Self.Filename + ' is Null');
    Exit;
  end;


  if Visible then
  begin
    if not Generated then Generate;

    glColor4f(1.0, 1.0, 1.0, 1.0);
    glEnable(GL_TEXTURE_2D);

    glPushMatrix;
      glColor3f(1, 1, 1);
      Bind(Texture);
      if Transparent then
      begin
        glEnable(GL_ALPHA_TEST);
        glAlphaFunc(GL_GREATER, 0.1);
      end;

  	glTranslatef(Position.X - Offset.Position.X + Offset.Rotation.X, Position.Y - Offset.Position.Y + Offset.Rotation.Y, 0);
  	if Abs(Rotation.Angle) >= 360.0 then Rotation.Angle := 0.0;

  	if Rotation.Angle <> 0.0 then glRotatef(Rotation.Angle, Rotation.Vector.X, Rotation.Vector.Y, Rotation.Vector.Z);

      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_BLEND);

  	glColor4f(Color.R / 255, Color.G / 255, Color.B / 255, Alpha / 255);
  	glScalef(Scale.X, Scale.Y, 1);
      DrawQuad(FOriginalSize, FClipRect, makeRect(-Offset.Rotation.X, -Offset.Rotation.Y, FRect.W, FRect.H), Position.Z);
      glDisable(GL_BLEND);
      if Transparent then glDisable(GL_ALPHA_TEST);
    glPopMatrix;

    glDisable(GL_TEXTURE_2D);
  end;
end;

procedure TelSprite.Animate(var Counter: Integer; Loop: Boolean = True);
begin
  Counter := Counter + 1;
  FFrame := Counter;
  if Loop then if GetFrame = GetMaxFrames then Counter := 0;

  Self.SetFrame(FFrame);
end;

procedure TelSprite.Animate(Timer: TelTimer; Loop: Boolean = True);
begin
  //if Timer.SingleAction then FFrame := FFrame + 1;
  if Loop then if GetFrame = GetMaxFrames then FFrame := 0;

  Self.SetFrame(FFrame);
end;

function TelSprite.OnPoint(Coord: TelPoint): Boolean;
begin
  if (Coord.X >= Position.X - Offset.Rotation.X - Offset.Position.X) and
     (Coord.Y >= Position.Y - Offset.Rotation.Y - Offset.Position.Y) and
     (Coord.X < (Position.X - Offset.Rotation.X - Offset.Position.X + FRect.W)) and
     (Coord.Y < (Position.Y - Offset.Rotation.Y - Offset.Position.Y + FRect.H)) then Result := true else Result := false;
end;

function TelSprite.OnMouseOver: Boolean;
begin
 
    {if SDL_Surface <> nil then} Result := Self.OnPoint(Window.Cursor);

end;

function TelSprite.OnClick: Boolean;
begin
  if OnMouseOver and Input.Mouse.LeftClick then Result := true
  else Result := false; 
end;

//
// TelSpriteList
//

constructor TelSpriteList.Create;
begin
  FSpriteList := TList.Create;
  Head := 'TelSpriteList';
end;

destructor TelSpriteList.Destroy;
var
  Counter : integer;
begin

  for Counter := 0 to FSpriteList.Count - 1 do
  begin
    TelSprite(FSpriteList[Counter]).Destroy;
  end;
  FSpriteList.Free;

  inherited Destroy;

end;

function TelSpriteList.GetCount: Integer;
begin
  Result := FSpriteList.Count;
end;

procedure TelSpriteList.Insert(Index: Integer; Sprite: TelSprite);
begin
  if ((Index >= 0) and (Index <= FSpriteList.Count - 1)) then FSpriteList.Insert(Index, Sprite)
  {$IFDEF USE_LOGGER}
  else begin
    if Index > FSpriteList.Count - 1 then TelLogger.GetInstance.WriteLog('SpriteList: Index > Count');
    if Index < 0 then TelLogger.GetInstance.WriteLog('SpriteList : Index < Count');
  end;
  {$ENDIF}
end;

function TelSpriteList.Add(Sprite: TelSprite): Integer;
begin
  Result := FSpriteList.Add(Sprite);
end;

procedure TelSpriteList.Delete(Index: Integer);
var
  TmpSprite: TelSprite;
begin
  if ((Index >= 0) and (Index <= FSpriteList.Count - 1)) then
  begin
    TmpSprite := Get(Index);
    TmpSprite.Destroy;
    FSpriteList.Delete(Index);
  end
  {$IFDEF USE_LOGGER}
  else begin
    if Index > FSpriteList.Count - 1 then TelLogger.GetInstance.WriteLog('SpriteList: Index > Count');
    if Index < 0 then TelLogger.GetInstance.WriteLog('SpriteList : Index < Count');
  end;
  {$ENDIF}
  
end;

function TelSpriteList.Get(Index: Integer): TelSprite;
begin
  if ((Index >= 0) and (Index <= FSpriteList.Count - 1)) then Result := TelSprite(FSpritelist[Index])
  {$IFDEF USE_LOGGER}
  else begin
    if Index > FSpriteList.Count - 1 then TelLogger.GetInstance.WriteLog('SpriteList: Index > Count');
    if Index < 0 then TelLogger.GetInstance.WriteLog('SpriteList : Index < Count');
  end;
  {$ENDIF}
  
end;

function TelSpriteList.GetPos(Index: String): Integer;
Var a, TMP: Integer;
Begin
  Try
    For a := 0 To FSpriteList.Count - 1 Do
    Begin
      if Items[a].Name <> Index then TMP := -1
      else begin
        TMP := a;
        Break;
      end;
    End;
  Finally
    Result := TMP;
  End;

end;

procedure TelSpriteList.Put(Index: Integer; const Item: TelSprite);
var
  TmpSprite: TelSprite;
begin
  if ((Index >= 0) and (Index <= FSpriteList.Count - 1)) then
  begin
    TmpSprite := Get(Index);
    TmpSprite.Destroy;
    Insert(Index, Item);
  end
  {$IFDEF USE_LOGGER}
  else begin
    if Index > FSpriteList.Count - 1 then TelLogger.GetInstance.WriteLog('SpriteList: Index > Count');
    if Index < 0 then TelLogger.GetInstance.WriteLog('SpriteList : Index < Count');
  end;
  {$ENDIF}
  
end;

Function TelSpriteList.GetS(Index: String): TelSprite;
Var TMP: Integer;
Begin
  TMP := GetPos(Index);
  if TMP >= 0 then Result := TelSprite(FSpriteList[TMP])
			  else Result := nil;
End;

Procedure TelSpriteList.PutS(Index: String; const Item: TelSprite);
var
  TMP: Integer;
  TmpSprite: TelSprite;
Begin
  if (Index <> '') then
  begin
    TmpSprite := GetS(Index);
	if TmpSprite <> nil then
	begin
	  TMP := GetPos(Index);
      TmpSprite.Destroy;
      Insert(TMP, Item);
	end
    {$IFDEF USE_LOGGER}
    else TelLogger.GetInstance.WriteLog('SpriteList: Index does not exist');
    {$ENDIF}
  end
  {$IFDEF USE_LOGGER}
  else TelLogger.GetInstance.WriteLog('SpriteList: Index string is empty');
  {$ENDIF}
End;

procedure TelSpriteList.LoadFromStream(Stream: TFileStream);
var
  TmpHead: String[12];
  loop : integer;
  ImgBuf : TelSprite;
begin
  TmpHead := '';

  Stream.Read(TmpHead, SizeOf(TmpHead));

  if TmpHead <> Head then
  begin
    {$IFDEF USE_LOGGER}
    TelLogger.GetInstance.WriteLog('Could not load file: Wrong file');
    {$ENDIF}
  end else
  begin
    Stream.Read(loop, SizeOf(Integer));

    FSpriteList.Count := loop;

    for loop := 0 to FSpriteList.Count - 1 do
    begin
      ImgBuf := TelSprite.Create;
      ImgBuf.LoadFromStream(Stream);
      FSpriteList.Insert(0, ImgBuf);
    end;
  end;
 
end;


procedure TelSpriteList.SaveToStream( Stream : TFileStream );
var
  loop : integer;
begin
  Stream.Write(Head, SizeOf(Head));
  Stream.Write(FSpriteList.Count, SizeOf(Integer));

  for loop := 0 to FSpriteList.Count - 1 do
  begin
    TelSprite(FSpriteList[loop]).SaveToStream(Stream);
  end;

end;

procedure TelSpriteList.SaveToFile(Filename: String);
var
  FileHndl: TFileStream;
begin
  FileHndl := TFileStream.Create( Filename, fmCreate );

  SaveToStream(FileHndl);

  FileHndl.Free;
end;

procedure TelSpriteList.LoadFromFile(Filename: String);
var
  FileHndl: TFileStream;
  Counter: integer;
begin

  for Counter := 0 to FSpriteList.Count - 1 do
  begin
    TelSprite(FSpriteList[Counter]).Destroy;
  end;

  FileHndl := TFileStream.Create(FileName, fmOpenRead);

  loadFromStream(FileHndl);

  FileHndl.Free;
end;


//
// GUI elements
//

//
// TelButton
//

constructor TelButton.Create;
begin
  inherited Create;
  
  fSprite := TelSprite.Create;
  fFont := TelTrueTypeFont.Create;
end;

destructor TelButton.Destroy;
begin
  fSprite.Destroy;
  fFont.Destroy;
  
  inherited;
end;

procedure TelButton.LoadFromFile(Filename: String);
begin
  fSprite.LoadFromFile(Filename);
end;

procedure TelButton.LoadFromFile(Filename: String; Rect: TelRect);
begin
  fSprite.LoadFromFile(Filename, Rect);
end;

procedure TelButton.LoadFromSprite(Sprite: TelSprite);
begin
  fSprite.VirtualClone(Sprite);
end;

procedure TelButton.LoadFromSprite(Sprite: TelSprite; Rect: TelRect);
begin
  fSprite.VirtualClone(Sprite);
  fSprite.ClipImage(Rect);
end;

procedure TelButton.VirtualClone(FromButton: TelButton);
begin
 
  fSprite.VirtualClone(FromButton.GetImage);
  fFont.VirtualClone(FromButton.GetFont);
  //fSprite := FromButton.GetImage;
  //fFont := FromButton.GetFont;
end;	
	
function TelButton.GetImage: TelSprite;
begin
  Result := fSprite;
end;

function TelButton.GetFont: TelTrueTypeFont;
begin
  Result := fFont;
end;
	
procedure TelButton.LoadFont(Filename: String; ptSize: Integer = 10);
begin
  fFont.LoadFromFile(Filename, ptSize);
  fFont.SetRenderType(rtBlended);
  fFont.Style := [fsBold];
end;
	
procedure TelButton.Move(Point: TelPoint);
begin
  fSprite.Move(Point);
end;

procedure TelButton.ClipImage(Rect: TelRect);
begin
  if fSprite <> nil then fSprite.ClipImage(Rect);
end;

function TelButton.GetWidth: Integer;
begin
  if fSprite <> nil then Result := fSprite.Width;
end;

function TelButton.GetHeight: Integer;
begin
  if fSprite <> nil then Result := fSprite.Height;
end;

function TelButton.OnMouseOver: Boolean;
begin
  
  if fSprite <> nil then Result := fSprite.OnMouseOver;

end;

function TelButton.OnClick: Boolean;
begin
  if OnMouseOver and Input.Mouse.isButtonUp(BUTTON_LEFT) then Result := true
  else Result := false;
end;

function TelButton.OnRightClick: Boolean;
begin
  if OnMouseOver and Input.Mouse.isButtonUp(BUTTON_RIGHT) then Result := true
  else Result := false;
end;

procedure TelButton.Draw;
begin
  if ((fSprite <> nil) and (fFont <> nil)) then
  begin
    fSprite.Position := Position;
    fSprite.Draw;
    fFont.TextOut(makeP3D(fSprite.Position.X + ((fSprite.GetWidth - fFont.GetWidth_Text(fCaption)) div 2),fSprite.Position.Y + ((fSprite.GetHeight - fFont.GetHeight) div 2)), fCaption);
  end;
end;

//
// TelGUI
//
constructor TelGUI.Create;
begin

end;

destructor TelGUI.Destroy;
begin

end;

procedure TelGUI.Box(Rect: TelRect; Color: TelColor);
begin
  Box(Rect, makeCol(Color.R, Color.G, Color.B, 255));
end;

procedure TelGUI.Box(Rect: TelRect; Color: TRGBAColor);
begin
  glColor4f(1.0, 1.0, 1.0, 1.0);

  glPushMatrix;
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glColor4f(Color.R / 255, Color.G / 255, Color.B / 255, Color.Alpha / 255);
    DrawQuad(Rect.X, Rect.Y, Rect.W, Rect.H, 0);
    glDisable(GL_BLEND);
  glPopMatrix;
end;

procedure TelGUI.Box(Rect: TelRect; Color: TelGradient);
var
  Vertices: TColorVertices;
begin
  case Color.GradientStyle of
    gsVertical:
      begin
        Vertices[0] := Color.StartColor;
        Vertices[1] := Color.StartColor;
        Vertices[2] := Color.EndColor;
        Vertices[3] := Color.EndColor;
      end;
    gsHorizontal:
      begin
        Vertices[0] := Color.EndColor;
        Vertices[1] := Color.StartColor;
        Vertices[2] := Color.StartColor;
        Vertices[3] := Color.EndColor;
      end;
  end;

  glColor4f(1.0, 1.0, 1.0, 1.0);

  glPushMatrix;
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    DrawQuad(Rect.X, Rect.Y, Rect.W, Rect.H, 0, Vertices);

    glDisable(GL_BLEND);
  glPopMatrix;
end;

function TelGUI.Button(Rect: TelRect; Caption: String): Boolean;
begin

end;

//
// TelMusic
//

{$IFDEF USE_SDL_MIXER}
constructor TelMusic.Create;
begin
  inherited Create;
end;

constructor TelMusic.Create(Filename: String);
begin
  inherited Create;
  LoadFromFile(Filename);
end;

destructor TelMusic.Destroy();
begin
  Mix_HaltMusic();
  Mix_FreeMusic(fMusic);

  inherited Destroy;
end;

procedure TelMusic.LoadFromFile(Filename: String);
var Directory: String;
begin
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));
	
	  if FileExists(Directory+FileName) then
      fMusic := Mix_LoadMUS(PChar(Directory+FileName))
    {$IFDEF USE_LOGGER}
	   else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specifies.', ltError);
  {$ENDIF}
end;

procedure TelMusic.SetVolume(Value: ShortInt);
begin
  FVolume := Value;
  Mix_VolumeMusic(Value);
end;

procedure TelMusic.SetPosition(Position: Double);
begin
  Mix_SetMusicPosition(Position);
end;

procedure TelMusic.Play(Loop: Integer = 0);
begin
  if fPaused then 
  begin
    fPaused := false;
    Mix_ResumeMusic();
  end else Mix_PlayMusic(fMusic, Loop);
  fisPlaying := true;
end;

procedure TelMusic.Pause();
begin
  fPaused := true;
  fisPlaying := false;
  Mix_PauseMusic();
end;

procedure TelMusic.Stop();
begin
  Mix_HaltMusic();
  fisPlaying := false;
end;


//
// TelSound
//

constructor TelSound.Create;
begin
  inherited Create;
end;

constructor TelSound.Create(Filename: String);
begin
  inherited Create;
  LoadFromFile(Filename);
end;

destructor TelSound.Destroy;
Begin
  Mix_FreeChunk(fSound);

  inherited Destroy;
End;

procedure TelSound.LoadFromFile(Filename: String);
var Directory: String;
begin
  
  if Filename <> '' then
  begin
    Directory := ExtractFilePath(ParamStr(0));
	
	  if FileExists(Directory+FileName) then
      fSound := Mix_LoadWAV(PChar(Directory+FileName))
    {$IFDEF USE_LOGGER}
	   else TelLogger.GetInstance.WriteLog('File not found: '+Directory+Filename, ltError);
    {$ENDIF}
  {$IFNDEF USE_LOGGER}
  end;
  {$ELSE}
  end else TelLogger.GetInstance.WriteLog('No filename specifies.', ltError);
  {$ENDIF}
end;

Procedure TelSound.Play;
Begin
  Mix_PlayChannel(0, fSound, 0);
End;

Procedure TelSound.Stop;
Begin
  Mix_HaltChannel(0);
End;
{$ENDIF}


initialization
  Application := TAppContainer.Create;
  Color := TelColorHelper.Create;
  Input := TelInputHelper.Create;
  GUI := TelGUI.Create;

finalization
  Color.Destroy;
  Input.Destroy;
  {$IFDEF USE_LOGGER} TelLogger.GetInstance.Destroy; {$ENDIF}
  GUI.Destroy;
  Application.Destroy;

end.
