unit fwWIZSDLEngine;

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

interface

uses SysUtils,

     SDL,
     SDL_Mixer,
     SDL_Image,
     SDL_TTF,

     fwLogging,
     fwTypes,
     fwCustomEngine,
	 fwCustomSDLEngine;

const
  cKeyLen          = 512;
  cStartVolume     = 25;
  cColorBits       = 16;
  cFXFactor        = 1.5;
  cSoundBufferSize = 2048;

// default joystick button layout
const
  GP2XWIZ_A_BUTTON           =  12;
  GP2XWIZ_X_BUTTON           =  14;
  GP2XWIZ_B_BUTTON           =  13;
  GP2XWIZ_Y_BUTTON           =  15;
  GP2XWIZ_L_BUTTON           =  10;
  GP2XWIZ_R_BUTTON           =  11;
  GP2XWIZ_MENUE_BUTTON       =   8;
  GP2XWIZ_SELECT_BUTTON      =   9;

  GP2XWIZ_VOL_PLUS_BUTTON    =  16;
  GP2XWIZ_VOL_MINUS_BUTTON   =  17;

  GP2XWIZ_UP_BUTTON          =  0;
  GP2XWIZ_LEFT_BUTTON        =  2;
  GP2XWIZ_DOWN_BUTTON        =  4;
  GP2XWIZ_RIGHT_BUTTON       =  6;

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
    fJoystick      : PSDL_Joystick;
    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;

  // -----------------------------------------------------------------------
  TfwWIZSDLEngine  = 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;
  
  fJoystick := nil;
  fButtons  := 0;
  fwLOG('open joystick');
  if (SDL_NumJoysticks() > 0) then begin
    fwLog(IntToStr(SDL_NumJoysticks()) + ' joystick(s) found');
    fJoystick := SDL_JoystickOpen(0);
    if Assigned(fJoystick) then begin
      fButtons := SDL_JoystickNumButtons(fJoystick) + 10;
      if (fButtons > Length(fButtonState)) then begin
        fButtons := Length(fButtonState);
      end;
      fwLOG('joystick 0 has ' + IntToStr(fButtons) + ' buttons');

      //enable joystick events
      SDL_JoystickEventState(SDL_ENABLE);
    end else begin
      fwLOG('SDL_JoystickOpen failed, error code ' + SDL_GetError());
    end;
  end else begin
    fwLOG('no joystick found');
  end;  
end;

procedure TfwSDLInputStates.Update(); 
var
  iLoop            : Integer;
begin
  for iLoop := 0 to Pred(fButtons) do begin
    fButtonState[iLoop] := SDL_JoystickGetButton(fJoystick, iLoop);
  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 QuitSDLProc; 
begin
  SDL_Quit();
end;

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

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

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

  if (SDL_Init (SDL_INIT_VIDEO or SDL_INIT_AUDIO or SDL_INIT_JOYSTICK) < 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);
	end;

    fVideoInfo := SDL_GetVideoInfo();
    if not Assigned(fVideoInfo) then begin
      fwLOG('SDL_GetVideoInfo failed');
    end;

    fVideoFlags := SDL_ANYFORMAT;

    // 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
      fwLOG('show mouse cursor');
      SDL_ShowCursor(SDL_ENABLE);
    end else begin
      fwLOG('hide mouse cursor');
      SDL_ShowCursor(SDL_DISABLE);
    end;

  fwLOG('engine init done');

  Result := fScreen;
end;

procedure TfwWIZSDLEngine.UpdateInput();
begin
  inherited UpdateInput();
  
  if Assigned(fInputStates) then begin
    // Pruefe eine bestimmte Taste
    if (fInputStates.KeyPressed(GP2XWIZ_VOL_MINUS_BUTTON)) then begin
      if (fInputStates.KeyPressed(GP2XWIZ_R_BUTTON)) then begin
        fExitKeyPressed := True;
      end;
    end;
  end;  
end;

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

end.
