unit fwCustomSDLEngine;

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

interface

uses SysUtils,

     SDL,
     SDL_Mixer,
     SDL_Image,
     SDL_TTF,

     fwTypes,
	   fwLogging,
     fwCustomEngine;

type
  // -----------------------------------------------------------------------
  TfwSDLSurface    = class(TfwCESurface)
  private
    fColorKey        : Cardinal;
    fSurface         : PSDL_Surface;
  protected
    function GetHeight : Integer; override;
    function GetWidth : Integer; override;
    function GetHasSurface : Boolean; override;
    function DoLock : Boolean; override;
    function DoUnlock : Boolean; override;
  public
    constructor Create(aSurface : PSDL_Surface = nil);

    procedure Unload(); override;

    procedure SetColorKey(aRed, aGreen, aBlue : Byte); override;

    function  LoadFromFile(const aFilename : String; aColorKey : Cardinal = 0) : Boolean; override;
    function  SetAlpha(aAlpha : Byte) : Boolean; override;

    function  GetColorAt(aX, aY : Integer) : Cardinal; override;
    procedure SetColorAt(aX, aY : Integer; aRed, aGreen, aBlue : Byte); override;

    procedure ClearClipRect; override;
    procedure SetClipRect(const aRect : TCERect); override;
    procedure FillRect(aRect : PCERect; aRed, aGreen, aBlue : Byte); overload; override;

    procedure Update(aX : Integer = 0; aY : Integer = 0; aWidth : Integer = 0; aHeight : Integer = 0); override;

    procedure Blit(aTo : TfwCEDrawable; const aDst : TCERect); overload; override;
    procedure Blit(aTo : TfwCEDrawable; const aSrc, aDst : TCERect); overload; override;
    procedure Blit(aTo : TfwCEDrawable; aX, aY : Integer); overload; override;
    procedure Blit(aTo : TfwCEDrawable; aX, aY, aWidth, aHeight : Integer); overload; override;
    procedure Blit(aTo : TfwCEDrawable; aDestX, aDestY, aSrcX, aSrcY, aWidth, aHeight : Integer); overload; override;

    function GetSrcRect : TCERect; override;

    property Surface : PSDL_Surface read fSurface;
  end;

  // -----------------------------------------------------------------------
  TfwSDLTTFont     = class(TfwCEFont)
  private
    fFont            : PTTF_Font;
  public
    constructor Create();
    procedure Unload(); override;
    function  LoadFromFile(const aFilename : String; aSize : Cardinal = 0) : Boolean; override;
    function  RenderText_Blended(const aText : String; aColor : TfwCEColor) : TfwCESurface; override;
    function  RenderText_Shaded(const aText : String; aForeColor, aBackColor : TfwCEColor): TfwCESurface; override;
    function TextLen(const aText : String) : Integer; override;
  end;

  // -----------------------------------------------------------------------
  TfwSDLSound      = class(TfwCESound)
  private
    fDuration        : Cardinal;
    fPlayStart       : Cardinal;
    fChannel         : Integer;
    fEffect          : PMix_Chunk;
  protected
    function GetPlaying() : Boolean; override;
  public
    constructor Create(aDuration : Cardinal = 0);
    procedure Unload(); override;
    function  LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean; override;
    function  Play(aVolume : Integer = 60) : Boolean; override;
  end;

  // -----------------------------------------------------------------------
  TfwSDLMusic      = class(TfwCEMusic)
  private
    fMusic           : PMix_Music;
  protected
    function GetPlaying() : Boolean; override;
  public
    constructor Create();
    procedure Unload(); override;
    function  LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean; override;
    function  Play(aVolume : Integer = 60; aPlayEndless : Boolean = True; aFadeTime : Integer = 0) : Boolean; override;
    function  Stop(aFadeTime : Integer = 0) : Boolean; override;
  end;

  // -----------------------------------------------------------------------
  TfwSDLInputKey   = class(TfwCESensorInput)
  private
    fKey             : Integer;
  protected
    function GetX() : Integer; override;
    function GetY() : Integer; override;
  public
    constructor Create(aKey : Integer);
    function Down() : Boolean; override;
	property Key : Integer read fKey;
  end;

  PSDLKeyStates    = ^TSDLKeyStates;
  TSDLKeyStates    = array[0..511] of UInt8;

  // -----------------------------------------------------------------------
  TfwSDLInputStates= class(TfwCEInputStates)
  private
    fNumKeys         : Integer;
    fKeystates       : PSDLKeyStates;
  public
    constructor Create();
    procedure Update(); override;
    function  KeyPressed(aKey : Integer; aReapeatTime : Cardinal = 0) : Boolean; override;
    property KeyStates : PSDLKeyStates read fKeystates;
  end;

  // -----------------------------------------------------------------------
  TfwCustomSDLEngine= class(TfwCEEngineLayer)
  protected
    fExitKeyPressed  : Boolean;
    fScreen          : TfwSDLSurface;
    fVideoInfo       : PSDL_VideoInfo;
    fVideoFlags      : Cardinal;
    procedure DoFlip(aScreen : TfwCESurface); override;
    function  CreateCompatibleSurface(aSurface : TfwCESurface; aWidth, aHeight : Integer) : TfwSDLSurface;
    function  DoCreateSurface(const aFilename : String = ''; aColorKey : Cardinal = 0) : TfwCESurface; override;
    function  DoCreateSound(const aFilename : String = ''; aDuration : Cardinal = 0) : TfwCESound; override;
    function  DoCreateMusic(const aFilename : String = '') : TfwCEMusic; override;
    function  DoCreateFont(const aFilename : String = ''; aSize : Integer = 0) : TfwCEFont; override;
  public
    constructor Create(aUseInputThread, aShowMouse : Boolean);
    destructor Destroy; override;

    procedure Shutdown(); override;
    
    function  GetError() : String;

    procedure Idle(); override;
    function  GetTicks() : Cardinal; override;
    procedure Sleep(aMilliSeconds : Cardinal); override;

    // Funktionen fuer die Grafik
    function  MapRGB(aSurface : TfwCEDrawable; aRed, aGreen, aBlue : Byte) : TfwCEColor; override;
    procedure ClearScreen(aColor : TfwCEColor); override;
    procedure UpdateScreen; override;

    procedure SetMusicVolume(aVolume : Integer); override;
    procedure SetGFXVolume(aVolume : Integer); override;
    procedure MusicDone(); override;
  end;

implementation

type
  TPixel           = array[0..3] of Byte;
  PPixel           = ^TPixel;

procedure xSDLSetColorAt(aSurface : PSDL_Surface; aX, aY : Integer; aColor : Cardinal);
var
  bpp              : Byte;
  p                : Pointer;
begin
  if Assigned(aSurface) then begin
    if ((aX < 0) or (aX >= aSurface^.w) or (aY < 0) or (aY >= aSurface^.h)) then begin
      Exit;
    end;

    bpp := aSurface^.Format^.BytesPerPixel;

    // Here p is the address to the pixel we want to retrieve
    p := Pointer(CARDINAL(aSurface^.Pixels) + (CARDINAL(aY) * aSurface^.Pitch) + (CARDINAL(aX) * bpp));

    case bpp of
      1 : PBYTE(p)^ := BYTE(aColor);
      2 : PWORD(p)^ := WORD(aColor);
      3 : begin
            if (SDL_BYTEORDER = SDL_BIG_ENDIAN) then begin
              PPIXEL(p)^[0] := BYTE((aColor shr 16) and $FF);
              PPIXEL(p)^[1] := BYTE((aColor shr  8) and $FF);
              PPIXEL(p)^[2] := BYTE((aColor shr  0) and $FF);
            end else begin
              PPIXEL(p)^[2] := BYTE((aColor shr 16) and $FF);
              PPIXEL(p)^[1] := BYTE((aColor shr  8) and $FF);
              PPIXEL(p)^[0] := BYTE((aColor shr  0) and $FF);
            end;
          end;
      4 : PCARDINAL(p)^ := aColor;
    end;
  end;
end;

function xSDLGetColorAt(aSurface : PSDL_Surface; aX, aY : Integer) : Cardinal;
var
  bpp              : Byte;
  p                : Pointer;
begin
  Result := 0;
  if Assigned(aSurface) then begin
    if ((aX < 0) or (aX >= aSurface^.w) or (aY < 0) or (aY >= aSurface^.h)) then begin
      Exit;
    end;

    bpp := aSurface^.Format^.BytesPerPixel;

    // Here p is the address to the pixel we want to retrieve
    p := Pointer(CARDINAL(aSurface^.Pixels) + (CARDINAL(aY) * aSurface^.Pitch) + (CARDINAL(aX) * bpp));

    case bpp of
      1 : Result := PBYTE(p)^;
      2 : Result := PWORD(p)^;
      3 : begin
            if (SDL_BYTEORDER = SDL_BIG_ENDIAN) then begin
              Result := (PPIXEL(p)^[0] shl 16) or (PPIXEL(p)^[1] shl 8) or (PPIXEL(p)^[2]);
            end else begin
              Result := (PPIXEL(p)^[2] shl 16) or (PPIXEL(p)^[1] shl 8) or (PPIXEL(p)^[0]);
            end;
          end;
      4 : Result := PCARDINAL(p)^;

    else
      Result := 0;
    end;
  end;
end;

// -----------------------------------------------------------------------
// fwSDLSurface
// -----------------------------------------------------------------------
function TfwSDLSurface.GetHasSurface() : Boolean;
begin
  Result := Assigned(fSurface);
end;

function TfwSDLSurface.GetWidth() : Integer;
begin
  if Assigned(fSurface) then begin
    Result := fSurface^.w;
  end else begin
    Result := 0;
  end;
end;

function TfwSDLSurface.DoLock : Boolean;
begin
  Result := False;
  if Assigned(fSurface) then begin
    Result := (SDL_LockSurface(fSurface) = 0);
  end;
end;

function TfwSDLSurface.DoUnlock : Boolean;
begin
  Result := False;
  if Assigned(fSurface) then begin
    SDL_UnlockSurface(fSurface);
    Result := True;
  end;
end;

function TfwSDLSurface.GetHeight() : Integer;
begin
  if Assigned(fSurface) then begin
    Result := fSurface^.h;
  end else begin
    Result := 0;
  end;
end;

constructor TfwSDLSurface.Create(aSurface : PSDL_Surface = nil);
begin
  inherited Create;

  fColorKey := 0;
  fSurface  := aSurface;
end;

procedure TfwSDLSurface.Unload();
var
  Screen           : TfwSDLSurface;
begin
  if Assigned(fSurface) then begin
    if (HAL() is TfwCustomSDLEngine) then begin
      Screen := (HAL() as TfwCustomSDLEngine).fScreen;
      if Assigned(Screen) then begin
        if (Screen.Surface <> fSurface) then begin
          SDL_FreeSurface(fSurface);
        end;
      end;
    end;
    fSurface := nil;
  end;

  inherited Unload();
end;

procedure TfwSDLSurface.SetColorKey(aRed, aGreen, aBlue : Byte);
begin
  if Assigned(fSurface) then begin
    fColorKey := SDL_MapRGB(fSurface^.format, aRed, aGreen, aBlue);
    SDL_SetColorKey(fSurface, SDL_SRCCOLORKEY, fColorKey);
  end;
end;

function TfwSDLSurface.LoadFromFile(const aFilename : String; aColorKey : Cardinal = 0) : Boolean;
var
  cFilename        : array[0..255] of Char;
  bRed             : Byte;
  bGreen           : Byte;
  bBlue            : Byte;
  Loaded           : PSDL_Surface;
begin
  if Assigned(fSurface) then begin
    Unload();
  end;

  StrPLCopy(cFilename, aFilename, SizeOf(cFilename) - 1);
  Loaded := IMG_Load(cFilename);
  if Assigned(Loaded) then begin
    if (aColorKey <> 0) then begin
      bRed   := ((aColorKey and $00FF0000) shr 16);
      bGreen := ((aColorKey and $0000FF00) shr  8);
      bBlue  := ((aColorKey and $000000FF) shr  0);
      SDL_SetColorKey(Loaded, SDL_SRCCOLORKEY, SDL_MapRGB(Loaded^.format, bRed, bGreen, bBlue));
    end;

    fSurface := SDL_DisplayFormat(Loaded);
    SDL_FreeSurface(Loaded);
    if Assigned(fSurface) then begin
      fLoaded   := True;
      fFilename := aFilename;
      fwLog('TfwSDLSurface.LoadFromFile: "' + aFilename + '" loaded');
    end else begin
      fwLog(SDL_GetError());
	  end;
  end else begin
    fwLog(SDL_GetError());
  end;

  Result := Assigned(fSurface);
end;

function TfwSDLSurface.SetAlpha(aAlpha : Byte) : Boolean;
begin
  Result := (SDL_SetAlpha(fSurface, SDL_SRCALPHA, aAlpha) = 0);
end;

function TfwSDLSurface.GetColorAt(aX, aY : Integer) : Cardinal;
begin
  Result := xSDLGetColorAt(fSurface, aX, aY);
end;

procedure TfwSDLSurface.SetColorAt(aX, aY : Integer; aRed, aGreen, aBlue : Byte);
begin
  if Assigned(fSurface) then begin
    xSDLSetColorAt(fSurface, aX, aY, SDL_MapRGB(fSurface^.format, aRed, aGreen, aBlue));
  end;
end;

procedure TfwSDLSurface.ClearClipRect;
begin
  SDL_SetClipRect(fSurface, NIL);
end;

procedure TfwSDLSurface.SetClipRect(const aRect : TCERect);
var
  Rect             : SDL_Rect;
begin
  Rect.x := aRect.X;
  Rect.y := aRect.Y;
  Rect.w := aRect.Width;
  Rect.h := aRect.Height;
  SDL_SetClipRect(fSurface, @Rect);
end;

procedure TfwSDLSurface.FillRect(aRect : PCERect; aRed, aGreen, aBlue : Byte);
var
  Rect             : SDL_Rect;
begin
  if Assigned(fSurface) then begin
    if Assigned(aRect) then begin
      Rect.x := aRect^.X;
      Rect.y := aRect^.Y;
      Rect.w := aRect^.Width;
      Rect.h := aRect^.Height;
      SDL_FillRect(fSurface, @Rect, SDL_MapRGB(fSurface^.format, aRed, aGreen, aBlue));
    end else begin
      SDL_FillRect(fSurface, nil, SDL_MapRGB(fSurface^.format, aRed, aGreen, aBlue));
    end;
  end;
end;

procedure TfwSDLSurface.Update(aX, aY, aWidth, aHeight : Integer);
begin
  SDL_UpdateRect(fSurface, aX, aY, aWidth, aHeight);
end;

procedure TfwSDLSurface.Blit(aTo : TfwCEDrawable; const aDst : TCERect);
var
  Dst              : SDL_Rect;
begin
  Dst.x := aDst.X;
  Dst.y := aDst.Y;
  Dst.w := aDst.Width;
  Dst.h := aDst.Height;

  if Assigned(fSurface) then begin
    SDL_BlitSurface(fSurface, nil, TfwSDLSurface(aTo).Surface, @Dst);
  end;
end;

procedure TfwSDLSurface.Blit(aTo : TfwCEDrawable; const aSrc, aDst : TCERect);
var
  Src              : SDL_Rect;
  Dst              : SDL_Rect;
begin
  Src.x := aSrc.X;
  Src.y := aSrc.Y;
  Src.w := aSrc.Width;
  Src.h := aSrc.Height;

  Dst.x := aDst.X;
  Dst.y := aDst.Y;
  Dst.w := aDst.Width;
  Dst.h := aDst.Height;

  if Assigned(fSurface) then begin
    SDL_BlitSurface(fSurface, @Src, TfwSDLSurface(aTo).Surface, @Dst);
  end;
end;

procedure TfwSDLSurface.Blit(aTo : TfwCEDrawable; aX, aY : Integer);
begin
  if Assigned(fSurface) then begin
    Blit(aTo, aX, aY, fSurface^.w, fSurface^.h);
  end;
end;

procedure TfwSDLSurface.Blit(aTo : TfwCEDrawable; aX, aY, aWidth, aHeight : Integer);
var
  Dst              : TCERect;
begin
  if Assigned(fSurface) then begin
    Dst.X      := aX;
    Dst.Y      := aY;
    Dst.Width  := aWidth;
    Dst.Height := aHeight;

    Blit(aTo, Dst);
  end;
end;

procedure TfwSDLSurface.Blit(aTo : TfwCEDrawable; aDestX, aDestY, aSrcX, aSrcY, aWidth, aHeight : Integer);
var
  Src              : TCERect;
  Dst              : TCERect;
begin
  if Assigned(fSurface) then begin
    Src.X      := aSrcX;
    Src.Y      := aSrcY;
    Src.Width  := aWidth;
    Src.Height := aHeight;

    Dst.X      := aDestX;
    Dst.Y      := aDestY;
    Dst.Width  := aWidth;
    Dst.Height := aHeight;

    Blit(aTo, Src, Dst);
  end;
end;

function TfwSDLSurface.GetSrcRect : TCERect;
begin
  Result.X      := 0;
  Result.Y      := 0;
  Result.Width  := Width;
  Result.Height := Height;
end;

// -----------------------------------------------------------------------
// fwSDLTTFont
// -----------------------------------------------------------------------
constructor TfwSDLTTFont.Create();
begin
  inherited Create;

  fFont := nil;
end;

procedure TfwSDLTTFont.Unload();
begin
  if Assigned(fFont) then begin
    TTF_CloseFont(fFont);
    fFont := nil;
  end;

  inherited Unload;
end;

function TfwSDLTTFont.LoadFromFile(const aFilename : String; aSize : Cardinal = 0) : Boolean;
var
  cFilename        : array[0..255] of Char;
begin
  StrPLCopy(cFilename, aFilename, SizeOf(cFilename) - 1);
  if (TTF_WasInit() = 0) then begin
    // Init truetype fonts
    TTF_Init();
  end;

  Result := False;
  fFont  := TTF_OpenFont(cFilename, aSize);
  if Assigned(fFont) then begin
    fLoaded   := True;
    fFilename := aFilename;
    Result := True;
  end;
end;

function TfwSDLTTFont.RenderText_Blended(const aText : String; aColor : TfwCEColor) : TfwCESurface;
var
  cText            : array[0..255] of Char;
  ForeColor        : TSDL_Color;
  Surface          : PSDL_Surface;
begin
  Result := nil;

  if Assigned(fFont) then begin
    ForeColor.r      := ((aColor and $00FF0000) shr 16);
    ForeColor.g      := ((aColor and $0000FF00) shr  8);
    ForeColor.b      := ((aColor and $000000FF) shr  0);
    ForeColor.unused := 255;

    StrPLCopy(cText, aText, SizeOf(cText) - 1);
    Surface := TTF_RenderText_Blended(fFont, cText, ForeColor);
    if Assigned(Surface) then begin
      Result := TfwSDLSurface.Create(Surface);
    end;
  end;
end;

function TfwSDLTTFont.RenderText_Shaded(const aText : String; aForeColor, aBackColor : TfwCEColor) : TfwCESurface;
var
  cText            : array[0..255] of Char;
  ForeColor        : TSDL_Color;
  BackColor        : TSDL_Color;
  Surface          : PSDL_Surface;
begin
  Result := nil;

  if Assigned(fFont) then begin
    ForeColor.r      := ((aForeColor and $00FF0000) shr 16);
    ForeColor.g      := ((aForeColor and $0000FF00) shr  8);
    ForeColor.b      := ((aForeColor and $000000FF) shr  0);
    ForeColor.unused := 255;

    BackColor.r      := ((aBackColor and $00FF0000) shr 16);
    BackColor.g      := ((aBackColor and $0000FF00) shr  8);
    backColor.b      := ((aBackColor and $000000FF) shr  0);
    BackColor.unused := 255;

    StrPLCopy(cText, aText, SizeOf(cText) - 1);
    Surface := TTF_RenderText_Shaded(fFont, cText, ForeColor, BackColor);
    if Assigned(Surface) then begin
      Result := TfwSDLSurface.Create(Surface);
    end;
  end;
end;

function TfwSDLTTFont.TextLen(const aText : String) : Integer;
var
  cText            : array[0..255] of Char;
  Width            : Integer;
  Height           : Integer;
begin
  Result := 0;
  if Assigned(fFont) then begin
    StrPLCopy(cText, aText, SizeOf(cText) - 1);
    TTF_SizeText(fFont, cText, Width, Height);
    Result := Width;
  end;
end;

// -----------------------------------------------------------------------
// fwSDLSound
// -----------------------------------------------------------------------
function TfwSDLSound.GetPlaying() : Boolean;
var
  uLastTime        : Cardinal;
begin
  if (fPlayStart > 0) then begin
    Result := (Mix_Playing(fChannel) <> 0);
    if not Result then begin
      uLastTime := (HAL().GetTicks() - fPlayStart);
      if (uLastTime > fDuration) then begin
        Result := False;
      end else begin
        Result := True;
      end;
    end;
  end else begin
    Result := False;
  end;
end;

constructor TfwSDLSound.Create(aDuration : Cardinal = 0);
begin
  inherited Create;

  fDuration  := aDuration;
  fPlayStart := 0;
  fEffect    := nil;
end;

procedure TfwSDLSound.Unload();
begin
  if Assigned(fEffect) then begin
    Mix_FreeChunk(fEffect);
    fEffect := nil;
  end;
end;

function TfwSDLSound.LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean;
var
  cFilename        : array[0..255] of Char;
begin
  Result := False;

  Unload();

  StrPLCopy(cFilename, aFilename, SizeOf(cFilename) - 1);
  fEffect := Mix_LoadWAV(cFilename);
  if Assigned(fEffect) then begin
    fLoaded   := True;
    fFilename := aFilename;
    Result := True;
  end;
end;

function TfwSDLSound.Play(aVolume : Integer = 60) : Boolean;
begin
  Result := False;
  if Assigned(fEffect) then begin
    fChannel := Mix_PlayChannel(-1, fEffect, 0);
    if (fChannel = -1) then begin
      Exit;
    end;

    Mix_VolumeChunk(fEffect, aVolume);
    fPlayStart := HAL().GetTicks;
    Result     := True;
  end;
end;

// -----------------------------------------------------------------------
// fwSDLMusic
// -----------------------------------------------------------------------
function TfwSDLMusic.GetPlaying() : Boolean;
begin
  Result := (Mix_PlayingMusic() <> 0);
end;

constructor TfwSDLMusic.Create();
begin
  inherited Create;

  fMusic := nil;
end;

procedure TfwSDLMusic.Unload();
begin
  if Assigned(fMusic) then begin
    if (Mix_PlayingMusic() <> 0) then begin
      Mix_HaltMusic();
    end;

    Mix_FreeMusic(fMusic);
    fMusic := nil;
  end;
end;

function TfwSDLMusic.LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean;
var
  cFilename        : array[0..255] of Char;
begin
  Result := False;

  Unload();

  StrPLCopy(cFilename, aFilename, SizeOf(cFilename) - 1);
  fMusic := Mix_LoadMUS(cFilename);
  if Assigned(fMusic) then begin
    fLoaded   := True;
    fFilename := aFilename;
    Result := True;
  end;
end;

function TfwSDLMusic.Play(aVolume : Integer = 60; aPlayEndless : Boolean = True; aFadeTime : Integer = 0) : Boolean;
var
  iBack            : Integer;
begin
  Result := False;
  if (not Playing) then begin
    if (aVolume >= 0) then begin
      Mix_VolumeMusic(aVolume);
    end;

    if (aPlayEndless) then begin
      iBack := Mix_FadeInMusic(fMusic, -1, aFadeTime);
    end else begin
      iBack := Mix_FadeInMusic(fMusic, 1, aFadeTime);
    end;

    if (iBack <> -1) then begin
      Result := False;
    end;
  end;
end;

function TfwSDLMusic.Stop(aFadeTime : Integer) : Boolean;
begin
  Result := False;
  if (Mix_PlayingMusic() <> 0) then begin
    if (aFadeTime > 0) then begin
      Mix_FadeOutMusic(aFadeTime);
    end else begin
      Mix_HaltMusic();
    end;
    Result := True;
  end;
end;

// -----------------------------------------------------------------------
// fwSDLInputKey
// -----------------------------------------------------------------------
function TfwSDLInputKey.GetX() : Integer;
begin
  Result := 0;
end;

function TfwSDLInputKey.GetY() : Integer;
begin
  Result := 0;
end;

constructor TfwSDLInputKey.Create(aKey : Integer);
begin
  inherited Create();

  fKey        := aKey;
  fSensorType := stKey;
end;

function TfwSDLInputKey.Down() : Boolean;
var
  Keys             : PSDLKeyStates;
begin
  Result := False;
  if Assigned(Owner) then begin
    Keys := (Owner as TfwSDLInputStates).KeyStates;
    if (Keys <> nil) then begin
      Result := (Keys^[fKey] <> 0);
    end;
  end;
end;

// -----------------------------------------------------------------------
// fwSDLInputStates
constructor TfwSDLInputStates.Create();
begin
  inherited Create;
  
  fKeystates := PSDLKEYSTATES(SDL_GetKeyState(@fNumKeys));
end;

procedure TfwSDLInputStates.Update();
begin
  fKeystates := PSDLKEYSTATES(SDL_GetKeyState(nil));
  
  inherited Update();
end;

function TfwSDLInputStates.KeyPressed(aKey : Integer; aReapeatTime : Cardinal = 0) : Boolean;
begin
  Result := False;
  if Assigned(fKeystates) then begin
    if (fNumKeys > aKey) and (aKey < SizeOf(TSDLKeyStates))
    then begin
      Result := (fKeystates^[aKey] <> 0);
    end;
  end;
end;

// -----------------------------------------------------------------------
// fwSDLEngine
// -----------------------------------------------------------------------

procedure TfwCustomSDLEngine.DoFlip(aScreen : TfwCESurface);
begin
  SDL_Flip((fScreen as TfwSDLSurface).Surface);
end;

function TfwCustomSDLEngine.CreateCompatibleSurface(aSurface : TfwCESurface; aWidth, aHeight : Integer) : TfwSDLSurface;
var
  Surface          : PSDL_Surface;
  CompSurface      : PSDL_Surface;
begin
  Result  := nil;
  Surface := (aSurface as TfwSDLSurface).Surface;
  if Assigned(Surface) then begin
    CompSurface := SDL_CreateRGBSurface(SDL_HWSURFACE or  SDL_SRCALPHA, aWidth, aHeight, 16, 0, 0, 0, 0);
    if Assigned(CompSurface) then begin
      Result := TfwSDLSurface.Create(CompSurface);
    end;
  end;
end;

function TfwCustomSDLEngine.DoCreateSurface(const aFilename : String = ''; aColorKey : Cardinal = 0) : TfwCESurface;
var
  Image            : TfwCESurface;
begin
  Result := nil;
  Image  := TfwSDLSurface.Create();
  if Assigned(Image) then begin
    Result := Image;
    if (Length(aFilename) > 0) then begin
      if Image.LoadFromFile(aFilename, aColorKey) then begin
        Exit;
	    end else begin
	      fwLog('File "' + aFilename + '" not found');
      end;
    end;
  end;
end;

function TfwCustomSDLEngine.DoCreateSound(const aFilename : String = ''; aDuration : Cardinal = 0) : TfwCESound;
var
  Sound            : TfwCESound;
begin
  Sound := TfwSDLSound.Create(aDuration);
  if Assigned(Sound) then begin
    Result := Sound;
    if (Length(aFilename) > 0) then begin
      if Sound.LoadFromFile(aFilename) then begin
        Exit;
	    end else begin
        FreeAndNil(Sound);
	      fwLog('File "' + aFilename + '" not found');
      end;
    end;
  end;
  Result := Sound;
end;

function TfwCustomSDLEngine.DoCreateMusic(const aFilename : String = '') : TfwCEMusic;
var
  Music            : TfwCEMusic;
begin
  Music := TfwSDLMusic.Create();
  if Assigned(Music) then begin
    Result := Music;
    if (Length(aFilename) > 0) then begin
      if Music.LoadFromFile(aFilename) then begin
        Exit;
	  end else begin
	    fwLog('File "' + aFilename + '" not found');
      end;
    end;
  end;
  Result := nil;
end;

function TfwCustomSDLEngine.DoCreateFont(const aFilename : String = ''; aSize : Integer = 0) : TfwCEFont;
var
  Font             : TfwCEFont;
begin
  Font := TfwSDLTTFont.Create();
  if Assigned(Font) then begin
    Result := Font;
    if (Length(aFilename) > 0) then begin
      if Font.LoadFromFile(aFilename, aSize) then begin
        Exit;
      end else begin
	    fwLog('File "' + aFilename + '" not found');
	  end;
    end;
    FreeAndNil(Font);
  end;
  Result := nil;
end;

constructor TfwCustomSDLEngine.Create(aUseInputThread, aShowMouse : Boolean);
begin
  inherited Create(aUseInputThread, aShowMouse);

  fScreen         := nil;
  fExitKeyPressed := False;
end;

destructor TfwCustomSDLEngine.Destroy;
begin
  SDL_Quit();

  inherited Destroy;
end;

procedure TfwCustomSDLEngine.Shutdown();
begin
  if Assigned(fScreen) then begin
    FreeAndNil(fScreen);
    SDL_Quit();
  end;
end;

function TfwCustomSDLEngine.GetError() : String;
begin
  Result := StrPas(SDL_GetError());
end;

procedure TfwCustomSDLEngine.Idle();
begin
  SDL_PumpEvents();
end;

function TfwCustomSDLEngine.GetTicks() : Cardinal;
begin
  Result := SDL_GetTicks();
end;

procedure TfwCustomSDLEngine.Sleep(aMilliSeconds : Cardinal);
begin
  SDL_Delay(aMilliSeconds);
end;

function TfwCustomSDLEngine.MapRGB(aSurface : TfwCEDrawable; aRed, aGreen, aBlue : Byte) : TfwCEColor;
begin
  if (aSurface is TfwCESubSurface) then begin
    Result := MapRGB((aSurface as TfwCESubSurface).Surface, aRed, aGreen, aBlue);
  end else begin
    Result := SDL_MapRGB((aSurface as TfwSDLSurface).Surface^.format, aRed, aGreen, aBlue);
  end;
end;

procedure TfwCustomSDLEngine.ClearScreen(aColor : TfwCEColor);
begin
  SDL_FillRect(fScreen.Surface, nil, aColor);
end;

procedure TfwCustomSDLEngine.UpdateScreen;
begin
  SDL_UpdateRect(nil, 0, 0, 0, 0);
end;

procedure TfwCustomSDLEngine.SetMusicVolume(aVolume : Integer);
begin
  Mix_VolumeMusic(aVolume);
end;

procedure TfwCustomSDLEngine.SetGFXVolume(aVolume : Integer);
begin
  Mix_Volume(-1, aVolume);
end;

procedure TfwCustomSDLEngine.MusicDone();
begin
end;

end.
