///////////////////////////////////////////////////////////////////////
// Copyright (C) Andru 2007                                          //
// mail: dr.andru@gmail.com                                          //
// site: http://andru.2x4.ru                                         //
//                                                                   //
// OMEGA Engine is free software.                                    //
//                                                                   //
// You may redistribute it and/or modify it under the terms of the   //
// GNU General Public License, as published by the Free Software     //
// Foundation; either version 2 of the License, or (at your option)  //
// any later version.                                                //
//                                                                   //
// this module is distributed in the hope that it will be useful,    //
// but WITHOUT ANY WARRANTY; without even the implied warranty of    //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              //
// See the GNU General Public License for more details.              //
//                                                                   //
// You should have received a copy of the GNU General Public License //
// along with this module.  If not, write to:                        //
// 	The Free Software Foundation, Inc.,                              //
// 	51 Franklin Street, Fifth Floor                                  //
// 	Boston, MA  02110-1301, USA.                                     //
///////////////////////////////////////////////////////////////////////
unit ENGINE_MAIN;

{$I DEFINES.pas}

interface
uses
  Windows,
  Messages,
  OpenGL,

  ENGINE_VAR,
  ENGINE_CONST,

  ENGINE_SCREEN,

  ENGINE_FLAGS,

  ENGINE_TIMERS,

  ENGINE_UTILS,

  ENGINE_CONSOLE,
  ENGINE_LOG,

  INPUT_KEYBOARD,
  INPUT_MOUSE,

  G2D_CAMERA,
  G2D_TEXT,

  TEXTURE_MANAGER,
  TEXTURE_EFFECTS,

  SOUND_WAVE,
  MEDIA_DSHOW,

  MATH_FAST,

  UTILS;

function eng_Main(
                   hInstance,
                   hPrevInstance : HINST;
                   lpCmdLine     : PChar;
                   nCmdShow      : Integer
                 ) : Integer; stdcall;

procedure eng_MainLoop; stdcall;

function eng_Mess(
                   hWnd   : HWND;
                   Msg    : UINT;
                   wParam : WPARAM;
                   lParam : LPARAM
                 ) : LRESULT;  stdcall;

function eng_Quit : Boolean;

//----------------------------------------------------------------------------//
                                 IMPLEMENTATION
//----------------------------------------------------------------------------//
uses
  ENGINE_WINDOW;

procedure eng_MainLoop;
  var
    i, z   : Integer;
    j      : Double;
    fTimer : PTimer;
    Mess   : tagMsg;
Begin

while app_Work do
  begin
    while PeekMessage( Mess, wnd_HWND, 0, 0, PM_REMOVE ) do
      asm
        lea  eax, Mess
        push eax
        call TranslateMessage

        lea  eax, Mess
        push eax
        call DispatchMessage
      end;

    glClear( GL_COLOR_BUFFER_BIT * F_ColorBufferClear or GL_DEPTH_BUFFER_BIT );

    CanKillTimers := FALSE;    
    if not app_Pause Then
      begin
        fTimer := app_Timer^.Next;
        if fTimer <> nil Then
          for z := 0 to app_Timers do
            begin
              j := U_GetTime;
              if fTimer^.Active then
                begin
                  for i := 0 to fTimer^.Precision do
                    if j > fTimer^.LastTime + fTimer^.Interval Then
                      begin
                        fTimer^.OnTimer;
                        fTimer^.LastTime := fTimer^.LastTime + fTimer^.Interval;
                        j := U_GetTime;
                      end else
                        break;
                end else fTimer^.LastTime := U_GetTime;

              fTimer := fTimer^.Next;
            end;
        end else Sleep( 10 );

    CanKillTimers := TRUE;
    for i := 1 to CTimersToKill do
      Timer_Free( TimersToKill[ i ] );
    CTimersToKill  := 0;

    asm call app_PDraw end;
//    if Console.Font <> nil Then Console_Draw;

    if WGL_EXT_swap_control then
      begin
        i := wglGetSwapIntervalEXT;
        case scr_VSync of
          TRUE  : if i <> 1 then wglSwapIntervalEXT( 1 );
          FALSE : if i <> 0 then wglSwapIntervalEXT( 0 );
        end;
      end;

    SwapBuffers( wnd_DC );
    INC( app_FPSCount );
  end;

End;

function eng_Main;
  var
    i      : Integer;
    fTimer : PTimer;
    Mess   : TMsg;
Begin
SetThreadAffinityMask( GetCurrentThread, 1 );
SetTimer( wnd_HWND, app_FPS_TIMER, 1000, nil );

glClear( GL_COLOR_BUFFER_BIT );
asm call app_PFInit end;
SwapBuffers( wnd_DC );
asm call app_PInit end;

fTimer := app_Timer^.Next;
if fTimer <> nil Then
  for i := 0 to app_Timers do
    begin
      fTimer^.LastTime := U_GetTime;
      fTimer := fTimer^.Next;
    end;

eng_MainLoop;

asm call app_PQuit end;

if not eng_Quit Then
  E_Error( ERR_QUIT );

Result := Mess.wParam;

FreeLibrary( wnd_INST );

End;

function eng_Mess;
  var
    i      : Integer;
    fTimer : PTimer;
Begin

Result := 0;

if @app_PWndProcBefor <> nil Then
  app_PWndProcBefor( hWnd, Msg, wParam, lParam );
case Msg of
{-------------------------------- Приложение ----------------------------------}
  // Создание
  WM_CREATE:
    begin
      app_Init := TRUE;

      Key_LoadLayout( K_LAYOUT_ENG );

      inpM_CanClick[ 0 ] := TRUE;
      inpM_CanClick[ 1 ] := TRUE;
      inpM_CanClick[ 2 ] := TRUE;
    end;
  // Закрытие
  WM_CLOSE, WM_DESTROY, WM_QUIT:
    begin
      if app_CanClose Then app_Work := FALSE;
    end;

  // Активация
  WM_ACTIVATE:
    begin
      if not app_Focus Then
        Scr_SetSettings;
      app_Focus := TRUE;

      for i := 0 to 2 do
        begin
          inpM_Down    [ i ] := FALSE;
          inpM_Up      [ i ] := FALSE;
          inpM_Wheel   [ i ] := FALSE;
          inpM_Click   [ i ] := FALSE;
          inpM_CanClick[ i ] := TRUE;
          inpM_DblClick[ i ] := FALSE;
        end;
      for i := 0 to 255 do
        begin
          inpK_Down[ i ] := FALSE;
          inpK_Up  [ i ] := FALSE;
        end;
    end;
  // Получение фокуса
  WM_SETFOCUS:
    begin
      if not app_Focus Then
        Scr_SetSettings;
        
      if app_AutoPause Then
        begin
          app_Pause := FALSE;

          fTimer := app_Timer^.Next;
          if fTimer <> nil Then
            for i := 0 to app_Timers do
              begin
                fTimer^.LastTime := U_GetTime;
                fTimer := fTimer^.Next;
              end;
        end;
    end;
  // Потеря фокуса
  WM_KILLFOCUS:
    begin
      if app_Focus Then
        ChangeDisplaySettings( scr_OLDSettings, CDS_FULLSCREEN );
      if app_AutoPause Then
        app_Pause := TRUE;
      app_Focus := FALSE;
      ShowWindow( wnd_HWND, SW_MINIMIZE );
    end;

  WM_SIZE:
    begin
      Wnd_Resize;
    end;

  WM_MOVING:
    begin
      wnd_X := PRect( lParam ).Left;
      wnd_Y := PRect( lParam ).Top;
    end;
{-------------------------------- Клавиатура ----------------------------------}
  // Нажатие клавиш
  WM_KEYDOWN:
    begin
      inpK_Down[ wParam ] := TRUE;
      inpK_Up  [ wParam ] := FALSE;
      inpK_LastU          := wParam;
    end;
  // Отпуск клавиш
  WM_KEYUP:
    begin
      inpK_Down[ wParam ] := FALSE;
      inpK_Up  [ wParam ] := TRUE;
      inpK_LastD          := wParam;
    end;
  // Ввод текста
  WM_CHAR:
    begin
      if ( inpK_Max <> -1 ) Then
        begin
          if ( Length( app_Text ) < inpK_Max ) Then
            if wParam >= 32 Then
              app_Text := app_Text + Chr( wParam );
        end else
          if wParam >= 32 Then
            app_Text := app_Text + Chr( wParam );
      if wParam = 8 Then
        Delete( app_Text, Length( app_Text ), 1 );
      Console_Process( wParam );
    end;
{----------------------------------- Мышь -------------------------------------}
  // Перемещение
  WM_MOUSEMOVE:
    begin
      inpM_X    := WORD( lParam );
      inpM_Y    := lParam shr 16;
      inpM_MOVE := TRUE;
    end;

  // Нажатие/двойное/отпуск левой кнопки
  WM_LBUTTONDOWN:
    begin
      inpM_Up   [ M_LBUTTON ] := FALSE;
      inpM_Down [ M_LBUTTON ] := TRUE;
      if inpM_CanClick[ M_LBUTTON ] Then
        begin
          inpM_Click   [ M_LBUTTON ] := TRUE;
          inpM_CanClick[ M_LBUTTON ] := FALSE;
        end;
    end;
  WM_LBUTTONUP:
    begin
      inpM_Down    [ M_LBUTTON ] := FALSE;
      inpM_Up      [ M_LBUTTON ] := TRUE;
      inpM_CanClick[ M_LBUTTON ] := TRUE;
      inpM_Click   [ M_LBUTTON ] := FALSE;
      inpM_DblClick[ M_LBUTTON ] := FALSE;
    end;
  WM_LBUTTONDBLCLK:
    begin
      inpM_DblClick[ M_LBUTTON ] := TRUE;
      inpM_Down [ M_LBUTTON ]    := TRUE;
    end;

  // Нажатие/двойное/отпуск правой кнопки
  WM_RBUTTONDOWN:
    begin
      inpM_Up   [ M_RBUTTON ] := FALSE;
      inpM_Down [ M_RBUTTON ] := TRUE;
      if inpM_CanClick[ M_RBUTTON ] Then
        begin
          inpM_Click   [ M_RBUTTON ] := TRUE;
          inpM_CanClick[ M_RBUTTON ] := FALSE;
        end;
    end;
  WM_RBUTTONUP:
    begin
      inpM_Down    [ M_RBUTTON ] := FALSE;
      inpM_Up      [ M_RBUTTON ] := TRUE;
      inpM_CanClick[ M_RBUTTON ] := TRUE;
      inpM_Click   [ M_RBUTTON ] := FALSE;
      inpM_DblClick[ M_RBUTTON ] := FALSE;
    end;
  WM_RBUTTONDBLCLK:
    begin
      inpM_DblClick[ M_RBUTTON ] := TRUE;
      inpM_Down [ M_RBUTTON ]    := TRUE;
    end;

  // Нажатие/двойное/отпуск средней кнопки
  WM_MBUTTONDOWN:
    begin
      inpM_Up   [ M_MBUTTON ] := FALSE;
      inpM_Down [ M_MBUTTON ] := TRUE;
      if inpM_CanClick[ M_MBUTTON ] Then
        begin
          inpM_Click   [ M_MBUTTON ] := TRUE;
          inpM_CanClick[ M_MBUTTON ] := FALSE;
        end;
    end;
  WM_MBUTTONUP:
    begin
      inpM_Down    [ M_MBUTTON ] := FALSE;
      inpM_Up      [ M_MBUTTON ] := TRUE;
      inpM_CanClick[ M_MBUTTON ] := TRUE;
      inpM_Click   [ M_MBUTTON ] := FALSE;
      inpM_DblClick[ M_MBUTTON ] := FALSE;
    end;
  WM_MBUTTONDBLCLK:
    begin
      inpM_DblClick[ M_MBUTTON ] := TRUE;
      inpM_Down [ M_MBUTTON ]    := TRUE;
    end;

  // Прокрутка скролла
  WM_MOUSEWHEEL:
    begin
      if wParam > 0 Then
        begin
          inpM_Wheel[ 1 ] := TRUE;
          inpM_Wheel[ 0 ] := FALSE;
        end else
          begin
            inpM_Wheel[ 0 ] := TRUE;
            inpM_Wheel[ 1 ] := FALSE;
          end;
    end;

  WM_TIMER:
    begin
      if wParam = app_FPS_TIMER Then
        begin
          if MediaLoop Then
            Media_Loop;
          app_FPS      := app_FPSCount;
          app_FPSCount := 0;

          INC( app_AllFPS, app_FPS );
          INC( app_AllTime );

//          SetWindowText( wnd_HWND, PChar( wnd_Caption + ' [ FPS: ' + U_IntToStr( app_FPS ) + ' ]' ) );
        end;
    end else Result := DefWindowProc( hWnd, Msg, wParam, lParam );
end;
if @app_PWndProcAfter <> nil Then
  app_PWndProcAfter( hWnd, Msg, wParam, lParam );

End;

function eng_Quit;
Begin
Result := FALSE;

Timer_FreeAll;
Cam2D_FreeAll;
Text_FreeFontAll;
Tex_FreeAll;
Snd_FreeAll;

if Mask <> nil Then
  Tex_FreeMask;

Snd_DeInit;
Media_DeInit;

if ( app_Log ) Then
  LOG_AddMessage( LOGT_ENGINE, PChar( LOG_AVERAGE_FPS + U_IntToStr( M_Round( app_AllFPS / app_AllTime ) ) ) );

if app_Log Then
  begin
    LOG_AddMessage( LOGT_ENGINE, LOG_OMEGA_EXIT );
    LogString := LogString + '</body>' + Chr( 13 ) + '</html>';
    LOG_Flush;
  end;

if app_InitType = 0 Then
  wnd_Destroy
else
  if app_HaltOnExit Then
    HALT( 0 );

Result := TRUE;
End;

initialization
InitOpenGL;
finalization
CloseOpenGL;

end.
