(*
   Elysion Frameworks
   Branch: Legacy
   Version: 09-08 "Apollo"

   Copyright (c) 2005 - 2009 Freeze.Dev (Johannes Stein and contributors)

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

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

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

  Thanks to: @br
  Sam Latinga - The father of SDL @br
  Dominique Louis - Who took the time and translated SDL to Pascal @br
  Robért Kisnémeth - Thanks a lot for the Sprite Engine. Helped me a lot @br
                     by setting up TelSpriteEngine and including Z sorting @br
  David Herzog - Made some pretty cool stuff with this engine

  @link http://www.freeze-dev.de
  info@freeze-dev.de

  Please, just use it if you are extremely sure about it. Actually, I created
  this for myself. There could be a lot of API changes in the future.

  Why everything in a single unit?
  I don't want to include thirty-something different units, so it's all
  compressed in one.

  *)

unit ElysionLegacy;

{$IFDEF FPC}
	{$MODE DELPHI}
{$ENDIF}

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

interface

uses
    SDL,
    {$IFDEF USE_SDL_IMAGE} SDL_image, {$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}
    SDLUtils,
    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;

// 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;

// 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;
    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;
	fText: TextFile;
  protected
    {$WARNINGS OFF}
    // Creates this class
    constructor Create;
    {$WARNINGS ON}
    
  public
    {	TelLogger.GetInstance
    	
    	}
    class function GetInstance: TelLogger;
	destructor Destroy; Override;
  
	procedure Free;
	
	{	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;
    procedure Free;

	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;
    procedure Free;

	  function isKeyDown(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;
    procedure Free;


  published

end;

TelMouseHelper = class
    private
        fLastCursorPos: TelPoint;
    public
        constructor Create;
	
        function isButtonDown(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;

    procedure Free;
end;

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

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

    FUseLogger: Boolean;
  
    {	Application.GetUnicodeSupport @br
    	@return
    	
    	}
    function GetUnicodeSupport: Boolean;
    
    {	Application.SetUnicodeSupport
    	
    	}
    procedure SetUnicodeSupport(Value: Boolean);
  public
    {
      Application.Create


      Returns: Instance of class Application
    }
    constructor Create;

    {
      Application.Destroy


    }
    destructor Destroy; Override;

    {
      Application.Free

    }
    procedure Free;

    {
      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;

// 
TelSurface = class(TelObject)
  private
    // Resolution is stored in a vertex
    FResolution: TelVertex;

    // VideoFlags
    FVideoFlag: TelVideoFlags;

	  FFullscreen: Boolean;
	  FHideCursor: Boolean;

    fFlags, fGFlags: Cardinal;
    fFullscreenFlag: Cardinal;

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

        fEvent: TSDL_Event;
    
        fCaption, fIcon: PChar;

        FStartTick, FCurTick, FTickNow: UInt32;
        FFrames: UInt32;
    
        {$IFNDEF DARWIN}
	{$IFDEF USE_SDL_GFX}
        fFPSManager: TFPSmanager;
	fLimitFPS: Boolean;
        {$ENDIF}
	{$ENDIF}

        fnext_time, fnow: UInt32;
        ffps: Integer;
        f__fps: Integer;

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

	// Sets the window title
        procedure SetCaption(Caption: String);
    
        {$IFNDEF DARWIN}
        {$IFDEF USE_SDL_GFX}
        procedure SetFPS(FPS: Integer);
        {$ENDIF}
        {$ENDIF}
        function GetFPS: Integer;

        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;
  destructor Destroy; Override;
  procedure Free;

  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; Overload;

        // Expert function (recommended): Use rects to update surface; Improves performance
	// Use only in software mode
        procedure EndScene(UpdateRects: array of TelRect); Overload;

        // 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;

    {$IFDEF DARWIN}
	    property FPS: Integer read GetFPS;
	  {$ELSE}
	    property FPS: Integer read GetFPS write SetFPS;
	    property LimitFPS: Boolean read fLimitFPS write fLimitFPS;
	  {$ENDIF}
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;

TelGameStates = class(TelObject)
  private
    FStates: TList;

		function GetActiveState: String;
		function GetLastUsedState: String;
  public
    constructor Create;
		procedure Free;

    procedure Add(Name: String);
    procedure Delete(Name: String); Overload;
    procedure Delete(Index: Integer); Overload;

    procedure SetState(Name: String); Overload;
    procedure SetState(Index: Integer); Overload;

    function GetState(Name: String): Boolean; Overload;
    function GetState(Index: Integer): Boolean; Overload;
	published
	  property LastUsedState: String read GetLastUsedState;
	  property ActiveState: String read GetActiveState;
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
    HotSpot: TelPoint;

    Color: TelColor;

		// Create Object
        constructor Create;
        // Destructor
        destructor Destroy; Override;
		// Free object
        procedure Free;

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

    {

    }
    procedure SaveToBitmap(Filename: String);

    procedure Generate(GenerateMode: TGenerateMode = gmAuto);

    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;

    {

    }
    procedure Free;


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;

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

    // Destructor
    destructor Destroy; Override;
    procedure Free;

    // 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 Action: Boolean;
  published
    property Active: Boolean read FActive;
    property Interval: Integer read FInterval write FInterval;

    property Paused: Boolean read FPaused;
end;


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

	// Free allocated memory
        procedure Free;

	// 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;
	
	fText: String;
	
	procedure ChangeFont;
    public
        constructor Create();
	procedure Free;
	
	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; Overload;
	function GetWidth(Text: String): Integer; Overload;
        function GetHeight: Integer;
        
	procedure TextOut(Point: TelPoint; Text: String;  AlignV: TAlignVertical = avNone; AlignH: TAlignHorizontal = ahNone);
  published
        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;

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

    fImageType: String;

    FFrame, FMaxFrames: Integer;
    FAnim: TelRect;
    FZ: Integer;
    FVisible: 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
    HotSpot: TelPoint;
	  Position: TelPoint;
	  
	  Color: TRGBAColor;
	
	  SDL_Surface: PSDL_Surface;
  
    constructor Create;
    destructor Destroy; Override;
    procedure Free;

    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);
    procedure SetZ(Z: Integer);
    function GetZ: Integer;

    function GetMaxFrames: Integer;

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

    procedure Draw; Overload;
	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 OnMouseHover: 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 Frame: Integer read GetFrame write SetFrame;
	
	property Transparent: Boolean read FTransparent write SetAutoTransparency;
	
	property Visible: Boolean read fVisible write fVisible;
	
	
	property Generated: Boolean read fGenerated;
	property Cloned: Boolean read fCloned;
	property Error: Boolean read fError;
end;

TelSpriteList = class(TelObject)
  private
    FSpriteList: TList;
	Head: String[12];
  
    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;
	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: TelPoint;
  
    constructor Create; Overload;
	procedure Free;
	
	procedure VirtualClone(FromButton: TelButton);
	
	function GetImage: TelSprite;
	function GetFont: TelTrueTypeFont;
	
	procedure LoadFromFile(Filename: String); Overload;
	procedure LoadFromFile(Filename: String; Rect: TelRect); Overload;
	
	procedure LoadFont(Filename: String; ptSize: Integer = 10);
	
	procedure Move(Point: TelPoint);
	
    procedure ClipImage(Rect: TelRect);
        
    function OnMouseHover: Boolean;
    function OnClick: Boolean;
        
	procedure Draw;
  published	
    property Caption: String read fCaption write fCaption;
	
	property Enabled: Boolean read FEnabled write FEnabled;
	
	property Width: Integer read GetWidth;
	property Height: Integer read GetHeight;
end;

{TelCheckButton = class
  private

  public

  published
    property Checked: Boolean read FChecked
end;}
  
{$IFDEF USE_SDL_MIXER}
TelMusic = class
  private
    fMusic: PMix_Music;
	fPaused: Boolean;
  public
    constructor Create(); overload;
    constructor Create(Filename: String); overload;
    procedure Free();

	procedure SetVolume(Volume: ShortInt);
	procedure SetPosition(Position: Double);
	
    procedure LoadFromFile(Filename: String);
    procedure Play();
	procedure Pause();
    procedure Stop();
end;

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

    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 convCol(S: TelSurface; Color: TelColor): Cardinal; Overload;
function convCol(S: TelSurface; 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;
function makeRect(X, Y, W, H: Integer): TelRect;

function CompareZ(Item1, Item2: TelSprite): Integer;
function OnPoint(Coord: TelPoint; Rect: TelRect): Boolean;
function GetFilenameExtension(Filename: String): String;
function SubPoints(Point1, Point2: TelPoint): TelPoint;

function ActiveSurface: TelSurface;

// Use operator overloading if supported


function Clone(Sprite: TelSprite): TelSprite; // Deprecated, use TelSprite.Clone instead


var
  // Application
  Application: TAppContainer;

  // ColorHelper: To use
  Color: TelColorHelper;


  Input: TelInputHelper;

  // Use Surface directly for backward compability if necessary
  // Better way: Use ActiveSurface function
  Surface: TelSurface;

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;

// deprecated, please use function convCol(Color: TelColor): Cardinal; instead
function convCol(S: TelSurface; 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: TelSurface; 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(Surface.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 makeRect(X, Y, W, H: Integer): TelRect;
begin
  Result.X := X;
  Result.Y := Y;
  Result.W := W;
  Result.H := H;
end;

function CompareZ(Item1, Item2 : TelSprite): Integer;
begin
  if Item1.GetZ < Item2.GetZ then Result := -1
  else if Item1.GetZ > Item2.GetZ then Result := 1
                                  else Result := 0;
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;

// Deprecated
// Use TelSprite.Clone insted
// Warning: Clones complete sprite, TelSprite.Clone uses pointers
function Clone(Sprite: TelSprite): TelSprite;
begin
  Result := Sprite;
end;

function ActiveSurface: TelSurface;
begin
  Result := Surface;
end;

//
// TelObject
//

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

destructor TelObject.Destroy;
begin
  Free;
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 Self <> nil then Free;
end;

procedure TelGraphicObject.Free;
begin
  if not Cloned then SDL_FreeSurface(SDL_Surface);
end;

procedure TelGraphicObject.Generate(GenerateMode: TGenerateMode);
begin
  if not Generated then
  begin
    FGenerated := true;
    case GenerateMode of
      gmAuto:
      begin
        if FImageType = 'BMP' then SDL_Surface := SDL_DisplayFormat(SDL_Surface)
	        		   		          else SDL_Surface := SDL_DisplayFormatAlpha(SDL_Surface);
      end;
      gmRGB: SDL_Surface := SDL_DisplayFormat(SDL_Surface);
      gmRGBA: SDL_Surface := SDL_DisplayFormatAlpha(SDL_Surface);
    end;
  end;
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
  try
    AssignFile(fText, ExtractFilename(ParamStr(0))+'.log');
    Rewrite(fText);
    WriteLog('Start log', ltNote);  
  finally
  
  end;
end;

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

destructor TelLogger.Destroy;
begin
  if Self <> Nil then Free;
end;

procedure TelLogger.Free;
begin
  try
    WriteLog('End log', ltNote);
  
    CloseFile(fText);
  finally
    //inherited;
  end;
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) + ' - ';

    WriteLn(fText, PreString + LogString);
    Flush(fText);
  finally
  
  end;
end;
	
{$ENDIF}
	

constructor TelColorHelper.Create;
begin
  inherited Create;

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

destructor TelColorHelper.Destroy;
begin
  if Self <> nil then Free;

  //inherited Destroy;
end;

procedure TelColorHelper.Free;
begin
  ClearBuffer;

  //inherited Free;
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
  if Self <> nil then Free;
end;

procedure TelKeyboardHelper.Free;
begin
  FLastUsedKey := 0;
end;

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

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

end;

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

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

end;

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

constructor TelJoystickHelper.Create;
begin
  inherited Create;
end;

destructor TelJoystickHelper.Destroy;
begin
  if Self <> nil then Free;
end;

procedure TelJoystickHelper.Free;
begin

end;

constructor TelMouseHelper.Create;
begin
  inherited Create;
end;

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

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

function TelMouseHelper.LeftClick: Boolean;
begin
  Result := Surface.MouseButtonUp[BUTTON_LEFT];
end;

function TelMouseHelper.RightClick: Boolean;
begin
  Result := Surface.MouseButtonUp[BUTTON_RIGHT];
end;

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

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

function TelMouseHelper.Motion(): Boolean;
begin
  fLastCursorPos := Surface.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
  if Self <> Nil then Free;
end;

procedure TelInputHelper.Free;
begin
  Keyboard.Free;
  Joystick.Free;
  Mouse.Free;
end;

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

  Description:


  Additional Notes: Essential class

}

constructor TAppContainer.Create;
begin
  inherited Create;

  FRun := true;
  UnicodeSupport := true;
end;

destructor TAppContainer.Destroy;
begin
  if Self <> Nil then Free;
end;

procedure TAppContainer.Free;
begin
  Surface.Destroy;
  {$IFDEF USE_SDL_MIXER} Mix_CloseAudio; {$ENDIF}
  SDL_Quit;
  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;

  {$IFDEF USE_SDL_MIXER}
  
  {$IFDEF DARWIN}
  //Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096);
  {$ELSE}
  Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096);
  {$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 Surface = nil then
  begin
    if Name = '' then Name := 'Elysion Application';
    Surface := TelSurface.Create;
    Surface.ID := 0;
    Surface.Name := Name;
    Result := Surface.SetVideoMode(Width, Height, Bits, Fullscreen, VideoFlag);
  end else
  begin
    {$IFDEF USE_LOGGER} TelLogger.GetInstance.WriteLog('ElysionLegacy supports only one window at a time.'); {$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;

{
  #############################################################################
  # TelSurface                                                                #
  #############################################################################

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

  Additional Notes: -

}

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

  Cursor := makeP2D(0, 0);

  FHideCursor := true;
  {$IFNDEF DARWIN}
  fLimitFPS := true;
  {$ENDIF}
  FFullscreenFlag := SDL_FULLSCREEN;

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

  FVideoFlag := vfNull;

  FStartTick := 0;
  FCurTick := 0;
  FTickNow := 0;
  FFrames := 0;
  FFPS := 0;

  fnext_time := 0;
  ffps := 0;
  f__fps := 0;

  fCaption := nil;
  fIcon := nil;

  {$IFNDEF DARWIN}
  {$IFDEF USE_SDL_GFX}
  SDL_initFramerate(@fFPSManager);
  SetFPS(60);
  {$ENDIF}
  {$ENDIF}

  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;

  FStartTick := SDL_GetTicks;

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

destructor TelSurface.Destroy;
begin
  if Self <> Nil then Free;
end;

procedure TelSurface.Free;
begin
  if FFullscreen then ToggleFullscreen();
  SDL_FreeSurface(Self.SDL_Surface);
end;

function TelSurface.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
        Free;
      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;


    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);

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

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

procedure TelSurface.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 TelSurface.BeginScene;
begin
  if UseBackgroundImage then
    SDL_BlitSurface(FBackgroundImage, PSDLRect(0, 0, FBackgroundImage.w, FBackgroundImage.h), SDL_Surface, PSDLRect(0, 0, FBackgroundImage.w, FBackgroundImage.h))
  else
    SDL_FillRect(Self.SDL_Surface, Nil, convCol(Self, FBackgroundColor));
end;

procedure TelSurface.EndScene;
begin
 
  {$IFNDEF DARWIN}
  {$IFDEF USE_SDL_GFX}
  if (fLimitFPS) then SDL_framerateDelay(@fFPSManager);
  {$ENDIF}
  {$ENDIF}
 
   
  if SDL_Flip(Self.SDL_Surface) = -1 then Free;
  HandleEvents;

end;

procedure TelSurface.EndScene(UpdateRects: array of TelRect);
var i: Integer;
begin
  for i := 0 to High(UpdateRects) do
    SDL_UpdateRect(Self.SDL_Surface, UpdateRects[i].X, UpdateRects[i].Y, UpdateRects[i].W, UpdateRects[i].H);
	
  HandleEvents;
  
  {$IFNDEF DARWIN}
  {$IFDEF USE_SDL_GFX}
  if (fLimitFPS) then SDL_framerateDelay(@fFPSManager);
  {$ENDIF}
  {$ENDIF}

end;

{$IFNDEF DARWIN}
{$IFDEF USE_SDL_GFX}
procedure TelSurface.SetFPS(FPS: Integer);
begin
  if FPS > 0 then SDL_setFramerate(@fFPSManager, FPS);
end;
{$ENDIF}
{$ENDIF}

function TelSurface.GetFPS: Integer;
begin

  {$IFNDEF DARWIN}
  {$IFDEF USE_SDL_GFX}
  Result := SDL_getFramerate(@fFPSManager);
  {$ELSE}
  f__fps := f__fps + 1;
  fnow := SDL_GetTicks;
  if fnext_time <= fnow then
  begin
    fnext_time := fnow + 1000;
    ffps := f__fps;
    f__fps := 0;
  end;

  Result := ffps;
  {$ENDIF}
  {$ELSE}
  f__fps := f__fps + 1;
  fnow := SDL_GetTicks;
  if fnext_time <= fnow then
  begin
    fnext_time := fnow + 1000;
    ffps := f__fps;
    f__fps := 0;
  end;

  Result := ffps;
  {$ENDIF}
end;

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

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

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

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

procedure TelSurface.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;

	    fBackgroundImage := IMG_Load(PChar(Directory+Filename));
	    if GetFilenameExtension(Filename) = 'BMP' then fBackgroundImage := SDL_DisplayFormat(fBackgroundImage)
						                                    else fBackgroundImage := SDL_DisplayFormatAlpha(fBackgroundImage);
    {$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 TelSurface.SetBackgroundColor(Color: TelColor);
begin
  UseBackgroundImage := false;
  FBackgroundColor := Color;
end;

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

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

procedure TelSurface.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
      TMP_Icon := IMG_Load(PChar(Directory + Filename));
      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 specifies.', ltError);
  {$ENDIF}
  
  if TMP_Icon <> nil then SDL_FreeSurface(TMP_Icon);
end;

procedure TelSurface.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
      TMP_Icon := IMG_Load(PChar(Directory + Filename));
	  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 specifies.', ltError);
  {$ENDIF}
  
  if TMP_Icon <> nil then SDL_FreeSurface(TMP_Icon);
end;

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

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

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

function TelSurface.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 TelSurface.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 TelSurface.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 TelGameStates.Create;
begin
  FStates := TList.Create;
end;

procedure TelGameStates.Free;
begin

end;
procedure TelGameStates.Add(Name: string);
begin

end;

procedure TelGameStates.Delete(Name: string);
begin
 
end;

procedure TelGameStates.Delete(Index: Integer);
begin

end;

procedure TelGameStates.SetState(Name: string);
begin

end;

procedure TelGameStates.SetState(Index: Integer);
begin

end;

function TelGameStates.GetState(Name: string): Boolean;
begin

end;

function TelGameStates.GetState(Index: Integer): Boolean; 
begin

end;

function TelGameStates.GetActiveState: String;
begin

end;

function TelGameStates.GetLastUsedState: String;
begin

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
  if Self <> Nil then Free;
end;

procedure TelScene.Free;
begin

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
  if Self <> Nil then Free;
end;

procedure TelTimer.Free;
begin

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.Action: 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;


constructor TelBitmapFont.Create;
begin
  inherited Create;
  
  FAlpha := 255;
end;

procedure TelBitmapFont.Free;
begin
  if fFontInfo.Surface <> nil then SDL_FreeSurface(fFontInfo.Surface);
end;

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

    if FileExists(Filename) then
    begin
      FFontInfo.Surface := IMG_Load(PChar(Directory + Filename));
      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;

//
// TelTrueTypeFont
//

procedure TelTrueTypeFont.ChangeFont();
begin
  if fChange then
  begin
    fChange := false;
	
	if fText <> '' then
	begin
      case fFontRender of
		rtSolid: 
		begin
		  case fTextStyle of
		    tsNormal: fSurface := TTF_RenderText_Solid(fFont, PChar(Text), convCol(fColor));
			tsUnicode: fSurface := TTF_RenderUNICODE_Solid(fFont, PUInt16(Text), convCol(fColor));
			tsUTF8: fSurface := TTF_RenderUTF8_Solid(fFont, PChar(Text), convCol(fColor));
		  end;
	    end;
		rtBlended: 
		begin
		  case fTextStyle of
		    tsNormal: fSurface := TTF_RenderText_Blended(fFont, PChar(Text), convCol(fColor));
			tsUnicode: fSurface := TTF_RenderUNICODE_Blended(fFont, PUInt16(Text), convCol(fColor));
			tsUTF8: fSurface := TTF_RenderUTF8_Blended(fFont, PChar(Text), convCol(fColor));
		  end;
	    end;
		
	  end;	
	  
	  fSurface := SDL_DisplayFormatAlpha(fSurface);
	 
	end;
  end;

end;

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

procedure TelTrueTypeFont.Free();
begin
  if fFont <> nil then TTF_CloseFont(fFont);
  
  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 := [];
	  {$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
  TTF_SizeText(fFont, PChar(fText), tmp, Result);
end;

function TelTrueTypeFont.GetWidth: Integer; {$IFDEF FPC} Overload; {$ENDIF}
var tmp: Integer;
begin
  TTF_SizeText(fFont, PChar(fText), Result, tmp);
end;

function TelTrueTypeFont.GetWidth(Text: String): Integer; {$IFDEF FPC} Overload; {$ENDIF}
var tmp: Integer;
begin
  TTF_SizeText(fFont, PChar(Text), Result, tmp);
end;

procedure TelTrueTypeFont.TextOut(Point: TelPoint; Text: String; 
   AlignV: TAlignVertical = avNone; AlignH: TAlignHorizontal = ahNone);
begin
  if Text <> '' then
  begin
    SetText(Text);
	if fChange then ChangeFont;
  end;
  
  if fSurface <> nil then
  begin
    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;
    SDL_BlitSurface(fSurface, nil, Surface.SDL_Surface, PSDLRect(Point.X, Point.Y, fSurface.w, fSurface.h));
  end;
  
end;


constructor TelSprite.Create;
begin
  fLoaded := false;
  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;
  
  Color := makeCol(0, 0, 0, 0);
  
  fGenerated := false;
  fCloned := false; 
  fError := false;
end;

destructor TelSprite.Destroy;
begin
  if Self <> Nil then Free;
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;
    if Filename <> '' then
    begin
      Directory := ExtractFilePath(ParamStr(0));
	  fImageType := GetFilenameExtension(Filename);

      if FileExists(Directory+Filename) then
      begin
        SDL_Surface := IMG_Load(PChar(Directory+Filename));

        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;

        ClipImage(tmp_ClipRect);

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

        FMaxFrames := FAnim.W * FAnim.H;

		    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

end;

procedure TelSprite.SaveToStream(Stream: TStream);
begin

end;

procedure TelSprite.Generate(GenerateMode: TGenerateMode);
begin
  if not Generated then
  begin
    fGenerated := true;

    {$IFDEF USE_SDL_GFX}
	    if ((Color.R > 0) and (Color.G > 0) and (Color.B > 0) and (Color.Alpha > 0)) then 
		  boxRGBA(Self.SDL_Surface, 0, 0, FClipRect.W, FClipRect.H, Color.R - 1, Color.G - 1, Color.B - 1, Color.Alpha - 1);
		  
		// TODO: Transparency !!!!
	  {$ELSE}
	    // Create new surface, fill this surface with color and blit
		// see /branches/SpriteColoring/WO_SDL_GFX
	  {$ENDIF}

	case GenerateMode of
      gmAuto:
      begin
        if fImageType = 'BMP' then SDL_Surface := SDL_DisplayFormat(SDL_Surface)
	        		   		  else SDL_Surface := SDL_DisplayFormatAlpha(SDL_Surface);
      end;
      gmRGB: SDL_Surface := SDL_DisplayFormat(SDL_Surface);
      gmRGBA: SDL_Surface := SDL_DisplayFormatAlpha(SDL_Surface);
    end;
  end;
end;

// Clone
// Pointer from the old SDL_Surface of FromSprite to the new SDL_Surface of this class
// Works, but Problem with memory allocation; Use with caution!
procedure TelSprite.VirtualClone(FromSprite: TelSprite);
begin
  fCloned := true;
  if FromSprite.SDL_Surface <> Nil then SDL_Surface := FromSprite.SDL_Surface;
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;

    {if ((fClipRect.X <> Rect.X) and
        (fClipRect.Y <> Rect.Y) and
        (fClipRect.W <> Rect.W) and
        (fClipRect.H <> Rect.H)) then
    begin
        FClipRect := Rect;
        FRect.W := FClipRect.W;
        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.SetZ(Z: Integer);
begin
  FZ := Z;
end;

function TelSprite.GetZ;
begin
  Result := FZ;
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 := SDL_Surface.w;
end;

function TelSprite.GetSurfaceHeight: Integer;
begin
  Result := SDL_Surface.h;
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.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
    SDL_SetColorKey(SDL_Surface, SDL_SRCCOLORKEY or SDL_RLEACCEL or SDL_HWACCEL,
      SDL_MapRGB(SDL_Surface.Format, Color.R, Color.G, Color.B));
end;

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

procedure TelSprite.Draw(Target: PSDL_Surface);
{$IFDEF USE_SDL_GFX}
var Scale: TelfPoint;
{$ENDIF}
begin
  if ((SDL_Surface <> nil) and (Target <> nil)) then
  begin
    if (Visible) then
    begin
      if fAlpha <> 255 then SDL_SetAlpha(SDL_Surface, SDL_SRCALPHA, FAlpha)
                       else SDL_SetAlpha(SDL_Surface, SDL_SRCALPHA, 255);

      if (FClipRect.W <> FRect.W) or (FClipRect.H <> FRect.H) then
        SDL_BlitSurface(SDL_ScaleSurfaceRect(SDL_Surface, FClipRect.X, FClipRect.Y, FClipRect.W, FClipRect.H, FRect.W, FRect.H),
                        PSDLRect(FClipRect.X, FClipRect.Y, FRect.W, FRect.H),
                        Target, PSDLRect(FClipRect.X, FClipRect.Y, FRect.W, FRect.H))
      else
        SDL_BlitSurface(SDL_Surface, PSDLRect(FClipRect.X, FClipRect.Y, FClipRect.W, FClipRect.H),
                                Target, PSDLRect(Position.X - HotSpot.X, Position.Y - HotSpot.Y, FClipRect.W, FClipRect.H));
    end;


  end;
end;

procedure TelSprite.Draw(TargetSprite: TelSprite);
begin
  Draw(TargetSprite.SDL_Surface);
end;

procedure TelSprite.Draw;
begin
  Draw(Surface.SDL_Surface);
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) and
     (Coord.Y >= Position.Y) and
     (Coord.X < (Position.X + FRect.W)) and
     (Coord.Y < (Position.Y + FRect.H)) then Result := true else Result := false;
end;

function TelSprite.OnMouseHover: Boolean;
begin
 
    if SDL_Surface <> nil then Result := Self.OnPoint(Surface.Cursor);

end;

function TelSprite.OnClick: Boolean;
begin
  Result := false;

  if OnMouseHover then
  begin
    if Input.Mouse.LeftClick then Result := true;
  end;  
end;

procedure TelSprite.Free;
begin
  if ((SDL_Surface <> nil) and (not Cloned)) then SDL_FreeSurface(SDL_Surface);
  
  inherited;
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]).Free;
  end;
  FSpriteList.Free;

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.Free;
    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.Free;
    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.Free;
      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]).Free;
  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;

procedure TelButton.Free;
begin
  fSprite.Free;
  fFont.Free;
  
  inherited;
end;

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

procedure TelButton.LoadFromFile(Filename: String; Rect: TelRect);
begin
  fSprite.LoadFromFile(Filename, Rect);
  
  fSprite.Generate;
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.SetTextStyle(tsNormal);
  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.OnMouseHover: Boolean;
begin
  
  if fSprite <> nil then Result := fSprite.OnMouseHover;

end;

function TelButton.OnClick: Boolean;
begin
  Result := false;

  if OnMouseHover then
  begin
    if Input.Mouse.LeftClick then Result := true;
  end;
end;

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

//
// TelMusic
//

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

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

procedure TelMusic.Free();
begin
  Mix_HaltMusic();
  Mix_FreeMusic(fMusic);
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(Volume: ShortInt);
begin
  Mix_VolumeMusic(Volume);
end;

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

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

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

procedure TelMusic.Stop();
begin
  Mix_HaltMusic();
end;


//
// TelSound
//

constructor TelSound.Create;
begin
  inherited Create;
end;

constructor TelSound.Create(Filename: String);
begin
  inherited Create;
  LoadFromFile(Filename);
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.Free;
Begin
  inherited Free;

  Mix_FreeChunk(fSound);
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;

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

end.
