unit fwWindowsSDLEngine;

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

interface

uses Windows,
     ShellApi,
     SysUtils,

     SDL,
     SDL_Mixer,
     SDL_Image,
     SDL_TTF,

     fwLogging,
     fwTypes,
     fwCustomEngine,
	   fwCustomSDLEngine;

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

type
  // -----------------------------------------------------------------------
  TfwSDLEngine     = class(TfwCustomSDLEngine)
  protected
    fLastPos         : TRect;
    fParentHandle    : THandle;
    fSDLHandle       : THandle;
    fOldCallback     : Pointer;
    procedure DoFlip(aScreen : TfwCESurface); override;
    function  CreateInputStates : TfwCEInputStates; override;
    procedure SetParentHandle(aValue : THandle);
  public
    constructor Create(aUseInputThread, aShowMouse : Boolean);
    function  Init(aWidth, aHeight, aBPP : Integer) : TfwCESurface; override;
    procedure UpdateInput(); override;
    function  ExitKeyPressed() : Boolean; override;
    property  ParentHandle : THandle read  fParentHandle
                                     write SetParentHandle;
  end;

implementation

// TfwSDLEngine ----------------------------------------------------------
procedure QuitSDLProc;
begin
  SDL_Quit();
end;

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

procedure TfwSDLEngine.DoFlip(aScreen : TfwCESurface);
var
  SDLRect          : TRect;
begin
  if (fSDLHandle <> 0) then begin
    GetWindowRect(fSDLHandle, SDLRect);
    if (SDLRect.Left <> fLastPos.Left) or
       (SDLRect.Top <> fLastPos.Top)
    then begin
      GetClientRect(fSDLHandle, SDLRect);
      MoveWindow(fSDLHandle, SDLRect.left, SDLRect.top, SDLRect.right - SDLRect.left, SDLRect.bottom - SDLRect.top, FALSE);
      fLastPos := SDLRect;
    end;    
  end;
  
  inherited DoFlip(aScreen);
end;

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

procedure TfwSDLEngine.SetParentHandle(aValue : THandle);
begin
  fParentHandle := aValue;
end;

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

  fLastPos.Left   := 0;
  fLastPos.Top    := 0;
  fLastPos.Right  := 0;
  fLastPos.Bottom := 0;
  fSDLHandle      := 0;
  fParentHandle   := 0;
end;

function TfwSDLEngine.Init(aWidth, aHeight, aBPP : Integer) : TfwCESurface;
var
  WMInfo           : TSDL_SysWMinfo;
begin
  Result := nil;

  fVideoFlags := SDL_HWSURFACE or SDL_HWPALETTE or SDL_DOUBLEBUF or SDL_HWACCEL;
  if fFullscreen then begin
    fVideoFlags := fVideoFlags or SDL_FULLSCREEN;
  end;

  if (fParentHandle <> 0) then begin
    fVideoFlags := SDL_HWSURFACE or SDL_NOFRAME;
  end;

  if (SDL_Init (SDL_INIT_VIDEO or SDL_INIT_AUDIO) < 0) then begin
    fwLOG('sdl not initialized');
    Exit;
  end;

  SDL_WM_SetCaption(PANSICHAR(fCaption), nil);

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

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

  if (fParentHandle <> 0) then begin
    SDL_VERSION(WMInfo.version);
    if (SDL_GetWMInfo(@WMInfo) <> -1) then begin
      fSDLHandle := WMInfo.window;
      SetParent(fSDLHandle, fParentHandle);
    end;
  end;

  // 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 TfwSDLEngine.UpdateInput();
begin
  inherited UpdateInput();
  
  if Assigned(fInputStates) then begin
    // Pruefe eine bestimmte Taste
    if (fInputStates.KeyPressed(SDLK_ESCAPE)) then begin
      fExitKeyPressed := True;
    end;
  end;  
end;

function TfwSDLEngine.ExitKeyPressed() : Boolean;
begin
  if fInputStates.KeyPressed(SDLK_ESCAPE) then begin
    fExitKeyPressed := True;
  end;

  Result := fExitKeyPressed;
end;

end.
