unit fwGP2XSDLEngine;

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

interface

uses SysUtils,

{$IFNDEF WIN32}
     uGP2X,
{$ENDIF}

     SDL,
     SDL_Mixer,
     SDL_Image,
     SDL_TTF,

     fwLogging,
     fwTypes,
     fwCustomEngine,
  	 fwCustomSDLEngine;

const
  cKeyLen          = 512;
  cStartVolume     = 25;
  cColorBits       = 16;
  cFXFactor        = 1.5;
  cSoundBufferSize = 128;
  cButtonCount     = 511;

// default joystick button layout
const
  GP2X_A_BUTTON              = gp2x_A;
  GP2X_X_BUTTON              = gp2x_X;
  GP2X_B_BUTTON              = gp2x_B;
  GP2X_Y_BUTTON              = gp2x_Y;

  GP2X_L_BUTTON              = gp2x_LButton;
  GP2X_R_BUTTON              = gp2x_RButton;

  GP2X_MENUE_BUTTON          = gp2x_Start;
  GP2X_SELECT_BUTTON         = gp2x_Select;

  GP2X_VOL_PLUS_BUTTON       = gp2x_VolPlus;
  GP2X_VOL_MINUS_BUTTON      = gp2x_VolMinus;

  GP2X_UP_BUTTON             = gp2x_JoyUp;
  GP2X_LEFT_BUTTON           = gp2x_JoyLeft;
  GP2X_DOWN_BUTTON           = gp2x_JoyDown;
  GP2X_RIGHT_BUTTON          = gp2x_JoyRight;

type

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

  // -----------------------------------------------------------------------
  TfwSDLInputStates= class(TfwCEInputStates)
  private
    fButtons       : Integer;
    fButtonState   : array[0..511] of Byte;
  public
    constructor Create();
    procedure Update(); override;
    function  KeyPressed(aKey : Integer; aReapeatTime : Cardinal = 0) : Boolean; override;
    function  ButtonState(aButton : Integer) : Uint8;
  end;

  // -----------------------------------------------------------------------
  TfwGP2XSDLEngine = class(TfwCustomSDLEngine)
  protected
    function  CreateInputStates : TfwCEInputStates; override;
  public
    function  Init(aWidth, aHeight, aBPP : Integer) : TfwCESurface; override;
    procedure UpdateInput(); override;
    function  ExitKeyPressed() : Boolean; override;
  end;

implementation

// -----------------------------------------------------------------------
// 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;
begin
  Result := False;
  if Assigned(Owner) then begin
    Result := (Owner as TfwSDLInputStates).ButtonState(fKey) <> 0;
  end;
end;

// -----------------------------------------------------------------------
// TfwSDLInputStates
// -----------------------------------------------------------------------
constructor TfwSDLInputStates.Create();
begin
  inherited Create;
  
  fButtons  := cButtonCount;
end;

procedure TfwSDLInputStates.Update(); 
var
  iLoop            : Integer;
begin
  for iLoop := 0 to Pred(fButtons) do begin
    fButtonState[iLoop] := 0;
  end;

  try
    if (gp2x_Initialized()) then begin
      if (gp2x_JoystickPressed(gp2x_JoyUp, True)) then fButtonState[gp2x_JoyUp] := 1;
      if (gp2x_JoystickPressed(gp2x_JoyLeft, True)) then fButtonState[gp2x_JoyLeft] := 1;
      if (gp2x_JoystickPressed(gp2x_JoyDown, True)) then fButtonState[gp2x_JoyDown] := 1;
      if (gp2x_JoystickPressed(gp2x_JoyRight, True)) then fButtonState[gp2x_JoyRight] := 1;

      if (gp2x_ButtonPressed(gp2x_Y)) then fButtonState[gp2x_Y] := 1;
      if (gp2x_ButtonPressed(gp2x_A)) then fButtonState[gp2x_A] := 1;
      if (gp2x_ButtonPressed(gp2x_X)) then fButtonState[gp2x_X] := 1;
      if (gp2x_ButtonPressed(gp2x_B)) then fButtonState[gp2x_B] := 1;

      if (gp2x_ButtonPressed(gp2x_LButton)) then fButtonState[gp2x_LButton] := 1;
      if (gp2x_ButtonPressed(gp2x_RButton)) then fButtonState[gp2x_RButton] := 1;

      if (gp2x_ButtonPressed(gp2x_Select)) then fButtonState[gp2x_Select] := 1;
      if (gp2x_ButtonPressed(gp2x_Start)) then fButtonState[gp2x_Start] := 1;

      if (gp2x_VolumePressed(gp2x_VolMinus)) then fButtonState[gp2x_VolMinus] := 1;
      if (gp2x_VolumePressed(gp2x_VolPlus)) then fButtonState[gp2x_VolPlus] := 1;
	end;
  except
    on E : Exception do begin
	  fwLOG(E.Message);
	  Halt(0);
	end;
  end;

  // Rufe die vorgaenger Methode auf
  inherited Update();
end;

function TfwSDLInputStates.KeyPressed(aKey : Integer; aReapeatTime : Cardinal = 0) : Boolean; 
begin
  Result := (ButtonState(aKey) <> 0);
end;

function  TfwSDLInputStates.ButtonState(aButton : Integer) : Uint8;
begin
  Result := 0;
  if (aButton < fButtons) then begin
    Result := fButtonState[aButton];
  end;
end;

// -----------------------------------------------------------------------
// TfwCaanooSDLEngine
// -----------------------------------------------------------------------
procedure QuitGP2X();
begin
  gp2x_DeinitMem();
end;

procedure QuitSDLProc();
begin
  SDL_Quit();
end;

procedure HandleMusicDoneEvent(); stdcall;
begin
  HAL().MusicDone();
end;

function TfwGP2XSDLEngine.CreateInputStates : TfwCEInputStates;
begin
  Result := TfwSDLInputStates.Create;
end;

function TfwGP2XSDLEngine.Init(aWidth, aHeight, aBPP : Integer) : TfwCESurface;
begin
  Result := nil;

  if (not gp2x_InitMem()) then begin
    fwLOG('calling gp2x_InitMem() failed');
    Exit;
  end;

  // Automatische Deinitialisierung
  AddExitProc(QuitGP2X);
  
  if (SDL_Init (SDL_INIT_VIDEO or SDL_INIT_AUDIO) < 0) then begin
    fwLOG('sdl not initialized');
    Exit;
  end;

  // Automatische Deinitialisierung
  AddExitProc(QuitSDLProc);

  // Erzeuge die Schnittstelle zu den Eingabegeraeten
  fInputStates := CreateInputStates();

  // Initialize SDL_mixer
  if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16, MIX_DEFAULT_CHANNELS, cSoundBufferSize) = -1)
  then begin
    fwLOG('audio not initialized');
  end else begin
    // Standardeinstellung, damit einem nicht die Ohren wegfliegen
  	Mix_VolumeMusic(cStartVolume);
  	Mix_Volume(-1, Trunc(cStartVolume * cFXFactor));

  	// Wann ist die Hintergrundmusik zu ende?
  	Mix_HookMusicFinished(@HandleMusicDoneEvent);

    fVideoInfo := SDL_GetVideoInfo();
    if not Assigned(fVideoInfo) then begin
      fwLOG('SDL_GetVideoInfo failed');
    end;
	
    fVideoFlags := SDL_SWSURFACE or SDL_HWPALETTE or SDL_FULLSCREEN;

    // Set 320x240 16-bits video mode
    fScreen := TfwSDLSurface.Create(SDL_SetVideoMode(aWidth, aHeight, cColorBits, fVideoFlags));
    if (not fScreen.HasSurface) then begin
      FreeAndNil(fScreen);
      fwLOG('SDL_SetVideoMode failed');
      Exit;
    end;
	
    SDL_SetClipRect(fScreen.Surface, nil);

    // disable mouse cursor, must be after calling SDL_SetVideoMode()
    if fShowMouse then begin
      SDL_ShowCursor(SDL_ENABLE);
    end else begin
      SDL_ShowCursor(SDL_DISABLE);
    end;

    Result := fScreen;
  end;
end;

procedure TfwGP2XSDLEngine.UpdateInput();
begin
  inherited UpdateInput();
  
  if Assigned(fInputStates) then begin
    // Pruefe eine bestimmte Taste
    if (fInputStates.KeyPressed(GP2X_VOL_MINUS_BUTTON)) then begin
      if (fInputStates.KeyPressed(GP2X_R_BUTTON)) then begin
        fExitKeyPressed := True;
      end;
    end;
  end;  
end;

function TfwGP2XSDLEngine.ExitKeyPressed() : Boolean;
begin
  Result := fExitKeyPressed;
end;

end.
