unit uStateManager;

interface

type  pState = ^rState;
      pStateManager = ^cStateManager;

      //sys calls
      proc_ptr    = procedure ();
      proci_ptr   = procedure ( param : pointer );
      procu_ptr   = procedure ( dt    : double; currentTime: Double );
      proca_ptr   = procedure ( active: boolean );
      //input calls
      prock_ptr   = procedure ( key   : Byte );
      procp_ptr   = procedure ( x, y  : Integer );
      procc_ptr   = procedure ( Char  : string );

      rState = record
        Name        : string;
        //sys events
        onInit      : proci_ptr;
        onDraw      : proc_ptr;
        onTimer     : array of procu_ptr;
        onUpdate    : procu_ptr;
        onActivate  : proca_ptr;
        onKill      : proc_ptr;
        //input events
        ////keyboard
        onKeyDown   : prock_ptr;
        onKeyUp     : prock_ptr;
        onKeyChar   : procc_ptr;
        ////mouse
        onMouseDown : prock_ptr;
        onMouseUp   : prock_ptr;
        onMouseMove : procp_ptr;
        ////joy

        prew        : pState;
      end;

      cStateManager = class
        private
          CurrentState : pState;
        public
          constructor Create;

          procedure Push  ( _state : rState ); overload;
          procedure Push  ( _state : pState ); overload;

          procedure Pop   ( kill : Boolean = True );
          procedure PopAll( kill : Boolean = True );

          procedure MakeInit    ( param   : pointer  = nil );
          procedure MakeDraw    ();
          procedure MakeTimer   ( index   : Word; dt : double; currentTime: Double );
          procedure MakeUpdate  ( dt      : double; currentTime: Double );
          procedure MakeActivate( active  : boolean );
          procedure MakeKill    ();

          procedure KeyDown     ( key   : Word );
          procedure KeyUp       ( key   : Word );
          procedure KeyChar     ( char  : string );
          procedure MouseDown   ( key   : Byte );
          procedure MouseUp     ( key   : Byte );
          procedure MouseMove   ( x, y  : Integer );
      end;

implementation

constructor cStateManager.Create;
begin
  CurrentState := nil;
end;

procedure cStateManager.Push( _state: rState );
var buf : pState;
begin
  buf := new( pState );
  buf.Prew := CurrentState;

  with _state do begin
    buf.Name        := Name;
    buf.onInit        := onInit;
    buf.onDraw        := onDraw;
    buf.onTimer       := onTimer;
    buf.onUpdate      := onUpdate;
    buf.onActivate    := onActivate;
    buf.onKill        := onKill;

    buf.onKeyDown   := onKeyDown;
    buf.onKeyUp     := onKeyUp;
    buf.onKeyChar   := onKeyChar;
    buf.onMouseDown := onMouseDown;
    buf.onMouseUp   := onMouseUp;
    buf.onMouseMove := onMouseMove;
  end;

  CurrentState := buf;
end;

procedure cStateManager.Push( _state: pState );
var buf : pState;
begin
  buf := new( pState );
  buf.Prew := CurrentState;

  with _state^ do begin
    buf.Name        := Name;
    buf.onInit        := onInit;
    buf.onDraw        := onDraw;
    buf.onTimer       := onTimer;
    buf.onUpdate      := onUpdate;
    buf.onActivate    := onActivate;
    buf.onKill        := onKill;

    buf.onKeyDown   := onKeyDown;
    buf.onKeyUp     := onKeyUp;
    buf.onKeyChar   := onKeyChar;
    buf.onMouseDown := onMouseDown;
    buf.onMouseUp   := onMouseUp;
    buf.onMouseMove := onMouseMove;
  end;

  CurrentState := buf;
end;

procedure cStateManager.Pop( kill : Boolean = True );
var buf : pState;
begin
  if CurrentState = nil then exit;
  if kill then MakeKill;
  buf := CurrentState;
  CurrentState := CurrentState.Prew;
  dispose( buf );
end;

procedure cStateManager.PopAll( kill : Boolean = True );
var buf : pState;
begin
  if CurrentState = nil then exit;
  buf := CurrentState;
  while buf <> nil do begin
    if kill then MakeKill;
    CurrentState := CurrentState.Prew;
    dispose( buf );
    buf := CurrentState;
  end;
  CurrentState := nil;
end;

procedure cStateManager.MakeInit( param : pointer = nil );
begin
  if CurrentState = nil then exit;
  if Assigned( CurrentState.onInit ) then CurrentState.onInit( param );
end;

procedure cStateManager.MakeDraw;
begin
  if CurrentState = nil then exit;
  if Assigned( CurrentState.onDraw ) then CurrentState.onDraw;
end;

procedure cStateManager.MakeTimer( index : Word; dt : Double; currentTime: Double );
begin
  if CurrentState = nil then exit;
  if Assigned( CurrentState.onTimer ) then CurrentState.onTimer[ index ]( dt, currentTime );
end;

procedure cStateManager.MakeUpdate( dt : double; currentTime: Double );
begin
  if CurrentState = nil then exit;
  if Assigned( CurrentState.onUpdate ) then CurrentState.onUpdate( dt, currentTime );
end;

procedure cStateManager.MakeActivate( active : boolean );
begin
  if CurrentState = nil then exit;
  if Assigned( CurrentState.onActivate ) then CurrentState.onActivate( active );
end;

procedure cStateManager.MakeKill;
begin
  if CurrentState = nil then exit;
  if Assigned( CurrentState.onKill ) then CurrentState.onKill;
end;

procedure cStateManager.KeyDown(key: word);
begin
  if CurrentState = nil then Exit;
  if Assigned( CurrentState.onKeyDown ) then CurrentState.onKeyDown( key );
end;

procedure cStateManager.KeyUp(key: word);
begin
  if CurrentState = nil then Exit;
  if Assigned( CurrentState.onKeyUp ) then CurrentState.onKeyUp( key );
end;

procedure cStateManager.KeyChar(char: string);
begin
  if CurrentState = nil then Exit;
  if Assigned( CurrentState.onKeyChar ) then CurrentState.onKeyChar( char );
end;

procedure cStateManager.MouseDown(key: Byte);
begin
  if CurrentState = nil then Exit;
  if Assigned( CurrentState.onMouseDown ) then CurrentState.onMouseDown( key );
end;

procedure cStateManager.MouseUp(key: Byte);
begin
  if CurrentState = nil then Exit;
  if Assigned( CurrentState.onMouseUp ) then CurrentState.onMouseUp( key );
end;

procedure cStateManager.MouseMove(x: Integer; y: Integer);
begin
  if CurrentState = nil then Exit;
  if Assigned( CurrentState.onMouseMove ) then CurrentState.onMouseMove( x, y );  
end;

end.
