unit fwCustomEngine;

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

interface

uses SysUtils,
     ContNrs,

     fwLogging,
     fwGlobals,
     fwTypes,
     fwObject,
     fwObjectlist;

type
  //----------------------------------------------------------------------
  // fwCEObject
  //
  // Alle Objekte in dieser Datei sind von diesem Objekt abgeleitet
  //----------------------------------------------------------------------
  TfwCEObject      = class(TfwObject)
  end;

  // TfwCELoadable -------------------------------------------------------
  TfwCELoadable   = class(TfwCEObject)
  protected
    fLoaded         : Boolean;
    fParam          : Cardinal;
    fFilename       : String;
  public
    constructor Create();
    destructor Destroy; override;
    function  LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean; virtual; abstract;
    procedure Unload(); virtual;
    property Filename : String read fFilename;
    property Loaded : Boolean read fLoaded;
    property Param : Cardinal read fParam;
  end;

  //----------------------------------------------------------------------
  // fwCEDrawable
  //
  // Alle Objekte, die einen Bild oder Bildauschnitt verwalten, werden von
  // fwCEDrawable abgeleitet.
  //----------------------------------------------------------------------
  TfwCEDrawable     = class(TfwCELoadable)
  private
    fLockCounter      : Integer;
  protected
    function GetHeight : Integer; virtual; abstract;
    function GetWidth : Integer; virtual; abstract;
    function DoLock : Boolean; virtual;
    function DoUnlock : Boolean; virtual;
  public
    constructor Create();
    destructor Destroy; override;
    procedure Unload(); override;

    function  Lock : Boolean;
    procedure Unlock;
    function  Islocked : Boolean;
    function  GetColorAt(aX, aY : Integer) : Cardinal; virtual; abstract;

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

    function  GetSrcRect : TCERect; virtual; abstract;

    property Width : Integer read GetWidth;
    property Height : Integer read GetHeight;
  end;

  TfwCEDrawables   = class(TObject)
  protected
    fList            : TfwObjectlist;
    function  GetCount : Integer;
    function  GetItem(aIndex : Integer) : TfwCEDrawable;
    procedure SetItem(aIndex : Integer; aItem : TfwCEDrawable);
  public
    constructor Create(aOwensSurface : Boolean = True);
    destructor Destroy; override;

    procedure Clear; virtual;
    function  Add(aItem : TfwCEDrawable) : TfwCEDrawable; virtual;
    function  IndexOf(aItem : TfwCEDrawable) : Integer; virtual;
    procedure Delete(aItem : TfwCEDrawable); overload; virtual;
    procedure Delete(aIndex : Integer); overload; virtual;
    procedure Extract(aItem : TfwCEDrawable); overload; virtual;

    property Count : Integer read GetCount;
    property Items[aIndex : Integer] : TfwCEDrawable read GetItem write SetItem; default;
  end;

  //----------------------------------------------------------------------
  // fwCESurface
  //
  // Objekt zur Verwaltung einer grafische Flaeche, z. B. den Bildschirm
  // oder ein Bild
  //----------------------------------------------------------------------
  TfwCESurface     = class(TfwCEDrawable)
  protected
    function  GetHasSurface : Boolean; virtual; abstract;
  public
    procedure SetColorKey(aRed, aGreen, aBlue : Byte); virtual; abstract;

    function  SetAlpha(aAlpha : Byte) : Boolean; virtual;

    procedure SetColorAt(aX, aY : Integer; aRed, aGreen, aBlue : Byte); virtual; abstract;

    procedure ClearClipRect; virtual; abstract;
    procedure SetClipRect(const aRect : TCERect); virtual; abstract;

    procedure FillRect(aRect : PCERect; aRed, aGreen, aBlue : Byte); virtual; abstract;

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

    property HasSurface : Boolean read GetHasSurface;
  end;

  TfwCESubSurface   = class(TfwCEDrawable)
  protected
    fSrcRect          : TCERect;
    fSurface          : TfwCEDrawable;
    fX                : Integer;
    fY                : Integer;
    fWidth            : Integer;
    fHeight           : Integer;
    function GetHeight : Integer; override;
    function GetWidth : Integer; override;
    function DoLock : Boolean; override;
    function DoUnlock : Boolean; override;
  public
    constructor Create(aSurface : TfwCEDrawable; aX, aY, aWidth, aHeight : Integer);
    destructor Destroy; override;
    function  LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean; override;
    procedure Unload(); override;

    function  GetColorAt(aX, aY : Integer) : Cardinal; 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 : TfwCEDrawable read fSurface;
  end;

  TfwCEFont        = class(TfwCELoadable)
  public
    function  LoadFromFile(const aFilename : String; aSize : Cardinal = 0) : Boolean; override; abstract;
    function  RenderText_Blended(const aText : String; aColor : TfwCEColor) : TfwCESurface; virtual; abstract;
    function  RenderText_Shaded(const aText : String; aForeColor, aBackColor : TfwCEColor): TfwCESurface; virtual; abstract;
    procedure RenderTo(aSurface : TfwCESurface; aX, aY : Integer; const aText : String; aForeColor : TfwCEColor); overload;
    procedure RenderTo(aSurface : TfwCESurface; aX, aY : Integer; aValue : Integer; aForeColor : TfwCEColor); overload;
    function TextLen(const aText : String) : Integer; virtual; abstract;
  end;

  TfwCESound       = class(TfwCELoadable)
  protected
    function GetPlaying() : Boolean; virtual; abstract;
  public
    function Play(aVolume : Integer = 60) : Boolean; virtual; abstract;
    property Playing : Boolean read GetPlaying;
  end;

  TfwCEMusic        = class(TfwCELoadable)
  protected
    function GetPlaying() : Boolean; virtual; abstract;
  public
    function Play(aVolume : Integer = 60; aPlayEndless : Boolean = True; aFadeTime : Integer = 0) : Boolean; virtual; abstract;
    function Stop(aFadeTime : Integer = 0) : Boolean; virtual; abstract;
    property Playing : Boolean read GetPlaying;
  end;

  TfwCETimer       = class;
  TfwCETimerEvent  = procedure (aTiimer : TfwCETimer) of Object;
  TfwCETimer       = class(TObject)
  protected
    fID              : Integer;
    fLastCall        : Cardinal;
    fInterval        : Cardinal;
    fOnTimer         : TfwCETimerEvent;
    fEnabled         : Boolean;
  public
    constructor Create(aID : Integer; aInterval : Cardinal = 1000; aOnTimer : TfwCETimerEvent = nil);
    function  TimeOut(UpdateLastCall : Boolean = False) : Boolean;
    procedure Update;
  end;

  // ---------------------------------------------------------------------
  // Verwaltung von Tastuatur-, Maus- und Joystick - Ereignissen
  // ---------------------------------------------------------------------

  TfwCESensorType  = (
                       stUnknown,
                       stKey,
                       stMouse,
                       stPosition
                     );

  //----------------------------------------------------------------------
  // Objekt zur Abfrage eines bestimmten Zustandes, z. B. eine Taste,
  // Joystick - oder Mausbutton. Auch fuer die Bestimmung einer Position,
  // z. B. der Maus, wird ein Sensor bereitgestellt.
  //----------------------------------------------------------------------
  TfwCEInputStates = class;

  TfwCESensorInput = class(TfwObject)
  private
    fOwner           : TfwCEInputStates;
  protected
    fSensorType      : TfwCESensorType;
    function GetX() : Integer; virtual; abstract;
    function GetY() : Integer; virtual; abstract;
  public
    constructor Create();
    procedure Update();
    function Down() : Boolean; virtual; abstract;
    property X : Integer read GetX;
    property Y : Integer read GetY;
    property Owner : TfwCEInputStates read fOwner write fOwner;
    property SensorType : TfwCESensorType read fSensorType;
  end;

  TfwCESensorInputs= class of TfwCESensorInput;

  TfwCEInputState  = class(TfwObject)
  protected
    fOwner           : TfwCEInputStates;
    fSensors         : TfwObjectList;
    fName            : String;          // Kurzbezeichnung der Taste
    fLabel           : String;          // Anzeigetext fuer die Taste
    fDownCounter     : Integer;         //
    fPressed         : Boolean;         // Die Taste war gedrueckt, ist aber nicht abgefragt worden
    fDown            : Boolean;         // Die Taste ist gerade gedrueckt
    fTime            : Cardinal;        // Die Taste ist seit xxx ticks gedrueckt
    function  CheckDown() : Boolean;
    function  GetPressed : Boolean;
  public
    constructor Create(const aName, aLabel : String);
    destructor Destroy; override;

    function  AddSensor(aSensor : TfwCESensorInput) : TfwCESensorInput; overload;

    procedure Clear(); virtual;
    procedure Update(); virtual;

    function  Released(aRepeatTimer : Cardinal) : Boolean;

    property DisplayLabel : String read fLabel;
    property Down : Boolean read fDown;
    property Name : String read fName;
    property Owner : TfwCEInputStates read fOwner write fOwner;
    property Pressed : Boolean read GetPressed;
  end;

  TfwCEInputStates = class(TfwObject)
  protected
    fInputList       : TfwObjectList;
    procedure Execute();
  public
    constructor Create();
    destructor Destroy; override;

    function  AddKey(aKey : TfwCEInputState) : TfwCEInputState;

    procedure Clear(); virtual;
    procedure Update(); virtual;

    function  AnyKeyPressed() : Boolean; virtual;
    function  AnyKeyDown() : Boolean; virtual;
    function  KeyPressed(aKey : Integer; aReapeatTime : Cardinal = 0) : Boolean; virtual;

    function SensorByName(const aName : String) : TfwCEInputState;
  end;

  TfwCEEngineLayers= class of TfwCEEngineLayer;

  TfwCEEngineLayer = class(TfwObject)
  protected
    fScreen          : TfwCESurface;
    fCaption         : String;
    fFullscreen      : Boolean;
    fInputThread     : Boolean;
    fShowMouse       : Boolean;
    fFrameTime       : Cardinal;
    fFrameCounter    : Cardinal;
    fFrameStartTime  : Cardinal;
    fInputStates     : TfwCEInputStates;
    fMediaPath       : String;

    procedure SetMediaPath(const aPath : String);

    procedure SetFullscreen(aValue : Boolean); virtual;
    function  GetFrameTime : Cardinal;
    procedure DoFlip(aScreen : TfwCESurface); virtual; abstract;

    // zu implementierende Lade - Funktionen fuer Bilder, Sound, Music und Schriften
    function  DoCreateSurface(const aFilename : String = ''; aColorKey : Cardinal = 0) : TfwCESurface; virtual; abstract;
    function  DoCreateSound(const aFilename : String = ''; aDuration : Cardinal = 0) : TfwCESound; virtual; abstract;
    function  DoCreateMusic(const aFilename : String = '') : TfwCEMusic; virtual; abstract;
    function  DoCreateFont(const aFilename : String = ''; aSize : Integer = 0) : TfwCEFont; virtual; abstract;
    function  CreateInputStates : TfwCEInputStates; virtual; abstract;
    function  TranslateFilename(const aFilename : String) : String;
  public
    constructor Create(aUseInputThread, aShowMouse : Boolean);
    destructor Destroy; override;

    function  Init(aWidth, aHeight, aBPP : Integer) : TfwCESurface; virtual; abstract;
    procedure Shutdown(); virtual;

    procedure Idle(); virtual; abstract;
    function  GetTicks() : Cardinal; virtual; abstract;
    procedure Sleep(aMilliSeconds : Cardinal); virtual; abstract;

    procedure StartFPS();
    function  GetFPS() : Integer;

    // Tastaturfunktionen
    function  AddKey(aKey : TfwCEInputState) : TfwCEInputState; virtual;
    procedure ClearKeys();
    procedure UpdateInput(); virtual;
    function  AnyKeyPressed() : Boolean; virtual;
    function  AnyKeyDown() : Boolean; virtual;
    function  ExitKeyPressed() : Boolean; virtual;

    // Funktionen fuer die Grafik
    function  MapRGB(aSurface : TfwCEDrawable; aRed, aGreen, aBlue : Byte) : TfwCEColor; virtual; abstract;
    procedure GetRGB(aColor : TfwCEColor; var aRed, aGreen, aBlue : Byte);
    procedure ClearScreen(aColor : TfwCEColor); virtual; abstract;
    procedure UpdateScreen; virtual; abstract;
    procedure Flip(aScreen : TfwCESurface; aWaitForVSnyc : Boolean = True);

    // Funktion fuer die Musik
    procedure SetMusicVolume(aVolume : Integer); virtual; abstract;
    procedure SetGFXVolume(aVolume : Integer); virtual; abstract;
    procedure MusicDone(); virtual;

    function  CreateSurface(const aFilename : String = ''; aColorKey : TfwCEColor = 0; aResourceManager : TObjectlist = nil) : TfwCESurface;
    function  CreateSound(const aFilename : String = ''; aDuration : Cardinal = 0; aResourceManager : TObjectlist = nil) : TfwCESound;
    function  CreateMusic(const aFilename : String = ''; aResourceManager : TObjectlist = nil) : TfwCEMusic;
    function  CreateFont(const aFilename : String = ''; aSize : Integer = 0; aResourceManager : TObjectlist = nil) : TfwCEFont;

    property Caption : String read fCaption write fCaption;
    property Controls : TfwCEInputStates read fInputStates;
    property FrameTime : Cardinal read GetFrameTime;
    property Fullscreen : Boolean read fFullscreen write SetFullscreen;
    property MediaPath : String read fMediaPath write SetMediaPath;
  end;

function  HAL() : TfwCEEngineLayer;
procedure SetHAL(aHAL : TfwCEEngineLayer);

implementation

// -----------------------------------------------------------------------
// TfwCELoadable
// -----------------------------------------------------------------------
constructor TfwCELoadable.Create;
begin
  inherited Create;

  fLoaded := False;
  fParam  := 0;
  SetLength(fFilename, 0);
end;

destructor TfwCELoadable.Destroy;
begin
  Unload();
  SetLength(fFilename, 0);

  inherited Destroy;
end;

procedure TfwCELoadable.Unload();
begin
  fLoaded := False;
end;

// -----------------------------------------------------------------------
// TfwCEDrawable
// -----------------------------------------------------------------------
function TfwCEDrawable.DoLock : Boolean;
begin
  Result := False;
end;

function TfwCEDrawable.DoUnlock : Boolean;
begin
  Result := False;
end;

constructor TfwCEDrawable.Create();
begin
  inherited Create;

  fLockCounter := 0;
end;

destructor TfwCEDrawable.Destroy;
begin
  Unload();

  inherited Destroy;
end;

procedure TfwCEDrawable.Unload();
begin
end;

function TfwCEDrawable.Lock : Boolean;
begin
  Result := False;
  if (fLockCounter = 0) then begin
    if DoLock then begin
      Inc(fLockCounter);
      Result := True;
    end;
  end else begin
    Inc(fLockCounter);
    Result := True;
  end;
end;

procedure TfwCEDrawable.Unlock;
begin
  if (fLockCounter > 0) then begin
    if (fLockCounter = 1) then begin
      if DoUnlock then begin
        Dec(fLockCounter);
      end;
    end else begin
      Dec(fLockCounter);
    end;
  end;
end;

function  TfwCEDrawable.Islocked : Boolean;
begin
  Result := (fLockCounter > 0);
end;

// -----------------------------------------------------------------------
// TfwCEDrawables
// -----------------------------------------------------------------------
function TfwCEDrawables.GetCount : Integer;
begin
  Result := fList.Count;
end;

function TfwCEDrawables.GetItem(aIndex : Integer) : TfwCEDrawable;
begin
  Result := (fList[aIndex] as TfwCEDrawable);
end;

procedure TfwCEDrawables.SetItem(aIndex : Integer; aItem : TfwCEDrawable);
begin
  fList[aIndex] := aItem;
end;

constructor TfwCEDrawables.Create(aOwensSurface : Boolean = True);
begin
  inherited Create;

  fList := TfwObjectList.Create(aOwensSurface);
end;

destructor TfwCEDrawables.Destroy;
begin
  FreeAndNil(fList);
  
  inherited Destroy;
end;

procedure TfwCEDrawables.Clear;
begin
  fList.Clear;
end;

function TfwCEDrawables.Add(aItem : TfwCEDrawable) : TfwCEDrawable;
begin
  Result := aItem;
  if Assigned(Result) then begin
    fList.Add(aItem);
  end;
end;

function TfwCEDrawables.IndexOf(aItem : TfwCEDrawable) : Integer;
begin
  Result := fList.IndexOf(aItem);
end;

procedure TfwCEDrawables.Delete(aItem : TfwCEDrawable);
begin
  fList.Delete(fList.IndexOf(aItem));
end;

procedure TfwCEDrawables.Delete(aIndex : Integer);
begin
  fList.Delete(aIndex);
end;

procedure TfwCEDrawables.Extract(aItem : TfwCEDrawable);
begin
  fList.Extract(aItem);
end;

// -----------------------------------------------------------------------
// TfwCESurface
// -----------------------------------------------------------------------
function TfwCESurface.SetAlpha(aAlpha : Byte) : Boolean;
begin
  Result := False;
end;

// -----------------------------------------------------------------------
// TfwCESubSurface
// -----------------------------------------------------------------------
function TfwCESubSurface.GetHeight : Integer;
begin
  Result := fHeight;
end;

function TfwCESubSurface.GetWidth : Integer;
begin
  Result := fWidth;
end;

function TfwCESubSurface.DoLock : Boolean;
begin
  Result := fSurface.DoLock;
end;

function TfwCESubSurface.DoUnlock : Boolean;
begin
  Result := fSurface.DoUnlock;
end;

constructor TfwCESubSurface.Create(aSurface : TfwCEDrawable; aX, aY, aWidth, aHeight : Integer);
begin
  inherited Create;

  fSurface := aSurface;
  fX       := aX;
  fY       := aY;
  fWidth   := aWidth;
  fHeight  := aHeight;

  fSrcRect.X      := fX;
  fSrcRect.Y      := fY;
  fSrcRect.Width  := fWidth;
  fSrcRect.Height := fHeight;
end;

destructor TfwCESubSurface.Destroy;
begin
  fSurface := nil;

  inherited Destroy;
end;

function TfwCESubSurface.LoadFromFile(const aFilename : String; aParam : Cardinal = 0) : Boolean;
begin
  raise Exception.Create('TfwCESubSurface: Method LoadFromFile not supported!');
end;

procedure TfwCESubSurface.Unload();
begin
  fSurface := nil;
end;

function TfwCESubSurface.GetColorAt(aX, aY : Integer) : Cardinal; 
begin
  Result := $FFFFFFFF;
  if Assigned(fSurface) then begin
    Result := fSurface.GetColorAt(fSrcRect.X + aX, fSrcRect.Y + aY);
  end;
end;

procedure TfwCESubSurface.Blit(aTo : TfwCEDrawable; const aDst : TCERect);
begin
  if Assigned(fSurface) then begin
    fSurface.Blit(aTo, fSrcRect, aDst);
  end;
end;

procedure TfwCESubSurface.Blit(aTo : TfwCEDrawable; const aSrc, aDst : TCERect);
begin
  if Assigned(fSurface) then begin
    
  end;
end;

procedure TfwCESubSurface.Blit(aTo : TfwCEDrawable; aX, aY : Integer);
begin
  if Assigned(fSurface) then begin
    fSurface.Blit(aTo, aX, aY, fX, fY, fWidth, fHeight);
  end;
end;

procedure TfwCESubSurface.Blit(aTo : TfwCEDrawable; aX, aY, aWidth, aHeight : Integer);
begin
  if Assigned(fSurface) then begin
  end;
end;

procedure TfwCESubSurface.Blit(aTo : TfwCEDrawable; aDestX, aDestY, aSrcX, aSrcY, aWidth, aHeight : Integer);
begin
  if Assigned(fSurface) then begin
    fSurface.Blit(aTo, aDestX, aDestY, fX + aSrcX, fY + aSrcY, fWidth, fHeight);
  end;
end;

function TfwCESubSurface.GetSrcRect : TCERect;
begin
  Result.X      := fX;
  Result.Y      := fY;
  Result.Width  := fWidth;
  Result.Height := fHeight;
end;

// -----------------------------------------------------------------------
// TfwCEFont
// -----------------------------------------------------------------------
procedure TfwCEFont.RenderTo(aSurface : TfwCESurface; aX, aY : Integer; const aText : String; aForeColor : TfwCEColor);
var
  TextSurface      : TfwCESurface;
begin
  TextSurface := RenderText_Blended(aText, aForeColor);
  if Assigned(TextSurface) then begin
    TextSurface.Blit(aSurface, aX, aY);
    FreeAndNil(TextSurface);
  end;
end;

procedure TfwCEFont.RenderTo(aSurface : TfwCESurface; aX, aY : Integer; aValue : Integer; aForeColor : TfwCEColor);
begin
  RenderTo(aSurface, aX, aY, Format('%d', [aValue]), aForeColor);
end;


// -----------------------------------------------------------------------
// TfwCETimer
// -----------------------------------------------------------------------
constructor TfwCETimer.Create(aID : Integer; aInterval : Cardinal = 1000; aOnTimer : TfwCETimerEvent = nil);
begin
  inherited Create;

  fID       := aID;
  fLastCall := HAL().GetTicks;
  fInterval := aInterval;
  fOnTimer  := aOnTimer;
  fEnabled  := True;
end;

function TfwCETimer.TimeOut(UpdateLastCall : Boolean = False) : Boolean;
var
  Ticks            : Cardinal;
begin
  Result := False;
  if fEnabled then begin
    Ticks := HAL().GetTicks();
    if (Ticks >= (fLastCall + fInterval)) then begin
      Result := True;
      if UpdateLastCall then begin
        fLastCall := Ticks;
      end;
    end;
  end;
end;

procedure TfwCETimer.Update;
begin
  fLastCall := HAL().GetTicks();
end;

// -----------------------------------------------------------------------
// TfwCESensorInput
// -----------------------------------------------------------------------
constructor TfwCESensorInput.Create();
begin
  inherited Create;

  fOwner := nil;
end;

procedure TfwCESensorInput.Update();
begin
end;

// -----------------------------------------------------------------------
// fwCEInputState
// -----------------------------------------------------------------------
function TfwCEInputState.CheckDown() : Boolean;
var
  iLoop            : Integer;
  Sensor           : TfwCESensorInput;

begin
  Result := False;

  for iLoop := 0 to Pred(fSensors.Count) do begin
    Sensor := (fSensors[iLoop] as TfwCESensorInput);
    if Assigned(Sensor) then begin
      Sensor.Update;
      Result := Result or Sensor.Down();
    end;
  end;
end;

function TfwCEInputState.GetPressed() : Boolean;
begin
  if fPressed then begin
    fPressed := False;
    Result   := True;
  end else begin
    Result := False;
  end;
end;

constructor TfwCEInputState.Create(const aName, aLabel : String);
begin
  inherited Create;

  fOwner       := nil;
  fName        := aName;
  fLabel       := aLabel;
  fDownCounter := 1;
  fDown        := false;
  fPressed     := false;
  fSensors     := TfwObjectList.Create;
end;

destructor TfwCEInputState.Destroy;
begin
  FreeAndNil(fSensors);

  inherited Destroy;
end;

function TfwCEInputState.AddSensor(aSensor : TfwCESensorInput) : TfwCESensorInput;
begin
  Result := aSensor;
  if Assigned(Result) then begin
    Result.Owner := fOwner;
    fSensors.Add(Result);
  end;
end;

procedure TfwCEInputState.Clear();
begin
  fTime    := 0;
  fPressed := False;
end;

procedure TfwCEInputState.Update();
var
  bIsDown          : Boolean;
begin
  if Assigned(fOwner) then begin
    bIsDown := CheckDown();

    // Verschiedene Situationen
    // a) fDown = False und Sensor.Down = False
    // b) fDown = False und Sensor.Down = True
    // c) fDown = True und Sensor.Down = False
    // d) fDown = True und Sensor.Down = True
    if (not fDown) then begin
        // Es war bisher keine Taste gedrueckt
        if (bIsDown) then begin
            fDownCounter := 0;
            fTime        := HAL().GetTicks();
        end else begin
          fTime := 0;
        end;
    end else begin
      Inc(fDownCounter);
    end;

    fDown    := bIsDown;
    fPressed := fPressed or bIsDown;
  end;
end;

function TfwCEInputState.Released(aRepeatTimer : Cardinal) : Boolean;
var
  uTime            : Cardinal;
  bPressed         : Boolean;
begin
  bPressed := fPressed;

  if Assigned(fOwner) then begin
      fPressed := False;
      uTime    := HAL().GetTicks();
      if (fDown) then begin
          if (fDownCounter = 0) then begin
              // Ok, die Taste wurde gerade zum ersten mal gedrueckt
              Result := True;
              Exit;
          end else if ((fTime > 0) and (aRepeatTimer > 0)) then begin
              // Ist das Wiederholungsintervall erreicht?
              if ((uTime - fTime) > aRepeatTimer) then begin
                  // Ja, Taste gilt als erneut betaetigt
                  fTime  := uTime;
                  Result := True;
                  Exit;
              end else begin
                  // Nein, dann wird der druecken der Taste ignoriert
                  Result := False;
                  Exit;
              end;
          end;
      end;
  end;

  Result := bPressed;
end;

// -----------------------------------------------------------------------
// TfwCEInputStates
// -----------------------------------------------------------------------
procedure TfwCEInputStates.Execute();
begin
end;

constructor TfwCEInputStates.Create();
begin
  inherited Create;

  fInputList := TfwObjectList.Create;
end;

destructor TfwCEInputStates.Destroy;
begin
  FreeAndNil(fInputList);
  
  inherited Destroy;
end;

function TfwCEInputStates.AddKey(aKey : TfwCEInputState) : TfwCEInputState;
begin
  if Assigned(aKey) then begin
    aKey.Owner := Self;
    fInputList.Add(aKey);
  end;
  Result := aKey;
end;

procedure TfwCEInputStates.Clear();
var
  iLoop            : Integer;
  State            : TfwCEInputState;
begin
  for iLoop := 0 to Pred(fInputList.Count) do begin
    State := (fInputList[iLoop] as TfwCEInputState);
    if Assigned(State) then begin
      State.Clear();
    end;
  end;
end;

procedure TfwCEInputStates.Update();
var
  iLoop            : Integer;
  State            : TfwCEInputState;
begin
  if Assigned(fInputList) then begin
    for iLoop := 0 to Pred(fInputList.Count) do begin
      State := (fInputList[iLoop] as TfwCEInputState);
      if Assigned(State) then begin
        State.Update();
      end;
    end;
  end;
end;

function TfwCEInputStates.AnyKeyPressed() : Boolean;
var
  iLoop            : Integer;
  State            : TfwCEInputState;
begin
  Result := False;
  for iLoop := 0 to Pred(fInputList.Count) do begin
    State := (fInputList[iLoop] as TfwCEInputState);
    if Assigned(State) then begin
      if (State.Pressed) then begin
        Result := True;
        Exit;
      end;
    end;
  end;
end;

function TfwCEInputStates.AnyKeyDown() : Boolean;
var
  iLoop            : Integer;
  State            : TfwCEInputState;
begin
  Result := False;
  for iLoop := 0 to Pred(fInputList.Count) do begin
    State := (fInputList[iLoop] as TfwCEInputState);
    if Assigned(State) then begin
      if (State.Down) then begin
        Result := True;
        Exit;
      end;
    end;
  end;
end;

function TfwCEInputStates.KeyPressed(aKey : Integer; aReapeatTime : Cardinal = 0) : Boolean;
begin
  Result := False;
end;

function TfwCEInputStates.SensorByName(const aName : String) : TfwCEInputState;
var
  iLoop            : Integer;
  State            : TfwCEInputState;
begin
  Result := nil;
  for iLoop := 0 to Pred(fInputList.Count) do begin
    State := (fInputList[iLoop] as TfwCEInputState);
    if Assigned(State) then begin
      if SameText(State.Name, aName) then begin
        Result := State;
        Exit;
      end;
    end;
  end;
end;

// -----------------------------------------------------------------------
// fwCEEngineLayer
// -----------------------------------------------------------------------
procedure TfwCEEngineLayer.SetMediaPath(const aPath : String);
begin
  fMediaPath := aPath;
end;

procedure TfwCEEngineLayer.SetFullscreen(aValue : Boolean);
begin
  fFullscreen := aValue;
end;

function TfwCEEngineLayer.GetFrameTime : Cardinal;
begin
  Result := GetTicks() - fFrameTime;
end;

function TfwCEEngineLayer.TranslateFilename(const aFilename : String) : String;
begin
  Result := aFilename;
  if (aFilename <> '') then begin
    if (Copy(aFilename, 1, 1) = '.') then Exit;
    if (Copy(aFilename, 1, 1) = '/') then Exit;
    if (Copy(aFilename, 1, 1) = '\') then Exit;
    Result := MediaPath + aFilename;
  end;
end;

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

  fFullscreen     := False;
  fCaption        := 'fwGameEngine (C) by Thomas Seban';
  fScreen         := nil;
  fInputStates    := nil;
  fInputThread    := aUseInputThread;
  fShowMouse      := aShowMouse;
  fFrameCounter   :=  0;
  fFrameTime      :=  0;
  fFrameStartTime :=  0;
  fInputStates    := nil;
  fMediaPath      := './';
end;

destructor TfwCEEngineLayer.Destroy;
begin
  FreeAndNil(fInputStates);

  inherited Destroy;
end;

procedure TfwCEEngineLayer.Shutdown();
begin
end;

procedure TfwCEEngineLayer.StartFPS();
begin
  fFrameStartTime := GetTicks();
  fFrameCounter   := 0;
end;

function TfwCEEngineLayer.GetFPS() : Integer;
var
  uTime            : Cardinal;
begin
  uTime := GetTicks() - fFrameStartTime;

  if (uTime > 1000) then begin
    uTime  := uTime div 1000;
    Result := Trunc(fFrameCounter / uTime);
    Exit;
  end;

  Result := 0;
end;

function TfwCEEngineLayer.AddKey(aKey : TfwCEInputState) : TfwCEInputState;
begin
  Result := nil;
  if Assigned(fInputStates) then begin
    if Assigned(aKey) then begin
      Result := fInputStates.AddKey(aKey);
    end;
  end;
end;

procedure TfwCEEngineLayer.ClearKeys();
begin
  if Assigned(fInputStates) then begin
    fInputStates.Clear();
  end;
end;

procedure TfwCEEngineLayer.UpdateInput();
begin
  if Assigned(fInputStates) then begin
    fInputStates.Update();
  end;
end;

function TfwCEEngineLayer.AnyKeyPressed() : Boolean;
begin
  Result := False;
  if Assigned(fInputStates) then begin
    Result := fInputStates.AnyKeyPressed();
  end;
end;

function TfwCEEngineLayer.AnyKeyDown() : Boolean;
begin
  Result := False;
  if Assigned(fInputStates) then begin
    Result := fInputStates.AnyKeyDown();
  end;
end;

function TfwCEEngineLayer.ExitKeyPressed() : Boolean;
begin
  Result := False;
end;

procedure TfwCEEngineLayer.Flip(aScreen : TfwCESurface; aWaitForVSnyc : Boolean = True);
begin
  Inc(fFrameCounter);
{$IFDEF WIN32}
  if aWaitForVSnyc then begin
    if (FrameTime < 18) then begin
      Sleep(18 - HAL.FrameTime);
    end;
  end;
{$ENDIF}
  DoFlip(aScreen);
  fFrameTime := GetTicks();
end;

procedure TfwCEEngineLayer.GetRGB(aColor : TfwCEColor; var aRed, aGreen, aBlue : Byte);
begin
  aRed   := ((aColor and $00FF0000) shr 16);
  aGreen := ((aColor and $0000FF00) shr  8);
  aBlue  := ((aColor and $000000FF) shr  0);
end;

procedure TfwCEEngineLayer.MusicDone();
begin
end;

function TfwCEEngineLayer.CreateSurface(const aFilename : String = ''; aColorKey : TfwCEColor = 0; aResourceManager : TObjectlist = nil) : TfwCESurface;
begin
  Result := DoCreateSurface(TranslateFilename(aFilename), aColorKey);
  if Assigned(Result) then begin
    if Assigned(aResourceManager) then begin
      aResourceManager.Add(Result);
    end;
  end;
end;

function TfwCEEngineLayer.CreateSound(const aFilename : String = ''; aDuration : Cardinal = 0; aResourceManager : TObjectlist = nil) : TfwCESound;
begin
  Result := DoCreateSound(TranslateFilename(aFilename), aDuration);
  if Assigned(Result) then begin
    if Assigned(aResourceManager) then begin
      aResourceManager.Add(Result);
    end;
  end;
end;

function TfwCEEngineLayer.CreateMusic(const aFilename : String = ''; aResourceManager : TObjectlist = nil) : TfwCEMusic;
begin
  Result := DoCreateMusic(TranslateFilename(aFilename));
  if Assigned(Result) then begin
    if Assigned(aResourceManager) then begin
      aResourceManager.Add(Result);
    end;
  end;
end;

function TfwCEEngineLayer.CreateFont(const aFilename : String = ''; aSize : Integer = 0; aResourceManager : TObjectlist = nil) : TfwCEFont;
begin
  Result := DoCreateFont(TranslateFilename(aFilename), aSize);
  if Assigned(Result) then begin
    if Assigned(aResourceManager) then begin
      aResourceManager.Add(Result);
    end;
  end;
end;

// -----------------------------------------------------------------------

var
  mHALayer         : TfwCEEngineLayer;

function HAL() : TfwCEEngineLayer;
begin
  Result := mHALayer;
end;

procedure SetHAL(aHAL : TfwCEEngineLayer);
begin
  mHALayer := aHAL;
end;

initialization

  mHALayer := nil;

finalization

  FreeAndNil(mHALayer);

end.
