unit iz_gfx;

interface
uses
  windows, messages, OpenGL,SysUtils,
  iz_types, iz_utils, iz_input, iz_inifiles, iz_font,iz_texture,iz_sprite;

const
    AppName = 'IZOGLEngine';
    TITLE = 'IZOGLEngine';

type
  IZGFXERROR = (GFX_OK, GFX_ERROR_NONE);
  IZGFXCOORD = (GFX_COORD_2D, GFX_COORD_3D);

type
  IZGfx = class
    Msg : TMsg;
    WindowClass : TWndClass;
    winHWND : HWND;                             // Ссылка на окно
    hrc : HGLRC;
    DC : HDC;
    WinProc : Pointer;
    FActive : Bool;                             // Флаг активности программы
    Quit : bool;                                // Флаг выхода из программы
    CaptionBarHeight : int;
    wndPosX,wndPosY : word;
    Input : izInput;
    coord : IZGFXCOORD;
    loading : bool;
    PercentLoading : byte;
    //Loading\\
    LoadingListID, LoadingTexID : uint;
    //***FPS***\\
    StartTime,
    CurrentTime,
    DeltaTime,
    FPSCount : int;
    FPSstr : string;
    ShowFPS : bool;                             // Показываем ли ФПС
    //***windows***\\
    Fokus : bool;                               // Есть ли у окна фокус
    Minimize : bool;
    TexMan : izTextureManager;
    SprMan : izSpriteManager;
    FontMan : izFontManager;
  private
     //***Log***\\
     FName : string;
     IsOpened : Boolean;
     LogFile : TextFile;
  private
      // Window
      function CreateWin( Width, Height, Bits : int; FullScreen : byte ) : bool;
      procedure DestroyWin( Wnd : hWnd; FullScreen : byte );
      // OpenGL
      procedure SetDCPixelFormat (hdc : HDC);
      function GLInit : bool;
      procedure GLDestroy (hrc : HGLRC);
  public
      constructor Create(Proc : Pointer);
      //**Инициализация OpenGL***\\
      procedure Init;
      function SetFullscreen( Width, Height, Bits , Freq : word ) : bool;        // Устанавилваем полноэкранный режим
      procedure RestoreResolution();                                             // Возвращаем настройки экрана
      function SetCoord(new_coord : IZGFXCOORD; Flag : bool) : IZGFXERROR;
      procedure begin_2D_render();                                               // Начало двухмерного рендера
      procedure end_render();                                                    // Завершение рендеринга
      //***Timer***\\
      procedure FPSUpdate();
      procedure TimerStart();
      function TimerEnd() : longInt;
      destructor DestroyGfx();
      //***Log***\\
      procedure OpenLog(Name : string);
      procedure CloseLog();
      procedure MsgMes(Str : String);
      procedure MsgOk(Str : String);
      procedure MsgErr(Str : String);
end;

var
  Gfx : IZGfx;

procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
implementation
uses
  iz_Options;

{   PRIVATE FUNCTIONS   }
procedure IZGfx.SetDCPixelFormat (hdc : HDC);
var
  pfd : TPixelFormatDescriptor;     //Данные формата пиксела
  nPixelFormat : integer;
begin
  With pfd do begin
    nSize := sizeof(TPixelFormatDescriptor);  //Размер структуры
    nVersion := 1;
    dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER; //Используемые флаги
    iPixelType := PFD_TYPE_RGBA; // режим для изображения цветов
    cColorBits := Options.BitDepth;      // число битовых плоскостей в каждом буфере цвета
    cRedBits := 0;               // число битовых плоскостей красного в каждом буфере RGBA
    cRedShift := 0;              // смещение от начала числа битовых плоскостей красного в каждом буфере RGBA
    cGreenBits := 0;             // число битовых плоскостей зелёного в каждом буфере RGBA
    cGreenShift := 0;            // смещение от начала числа битовых плоскостей зелёного в каждом буфере RGBA
    cBlueBits := 0;              // число битовых плоскостей синего в каждом буфере RGBA
    cBlueShift := 0;             // смещение от начала числа битовых плоскостей синего в каждом буфере RGBA
    cAlphaBits := 0;             // число битовых плоскостей альфа в каждом буфере RGBA
    cAlphaShift := 0;            // смещение от начала числа битовых плоскостей альфа в каждом буфере RGBA
    cAccumBits := 0;             // общее число битовых плоскостей в буфере аккумулятора
    cAccumRedBits := 0;          // число битовых плоскостей красного в буфере аккумулятора
    cAccumGreenBits := 0;        // число битовых плоскостей зелёного в буфере аккумулятора
    cAccumBlueBits := 0;         // число битовых плоскостей синего в буфере аккумулятора
    cAccumAlphaBits := 0;        // число битовых плоскостей альфа в буфере аккумулятора
    cDepthBits := 16;            // размер буфера глубины (ось z)
    cStencilBits := 0;           // размер буфера трафарета
    cAuxBuffers := 0;            // число вспомогательных буферов
    iLayerType := PFD_MAIN_PLANE;// тип плоскости
    bReserved := 0;              // число плоскостей переднего и заднего плана
    dwLayerMask := 0;            //
    dwVisibleMask := 0;          // индекс или цвет прозрачности нижней плоскости
    dwDamageMask := 0;           // игнорируется
end;
  // FillChar(pfd,SizeOf(pfd),0);
  nPixelFormat := ChoosePixelFormat(hdc,@pfd);
  if nPixelFormat = 0 then
  begin
    DestroyWin(winHWND,Options.FScreen);
    MessageBox(0, 'Unable to find a suitable pixel format', 'Error',
                                                      MB_OK or MB_ICONERROR);
    Exit;
  end;
  if (not SetPixelFormat(hdc,nPixelFormat,@pfd)) then
  begin
    DestroyWin(winHWND,Options.FScreen);
    MessageBox(0, 'Unable to set the pixel format', 'Error', 
                                                     MB_OK or MB_ICONERROR);
    Exit;
  end;
end;

function IZGfx.GLInit : bool;
begin
  result := false;
  loading := true;
  dc := GetDC (winHWND);                                                 // Получаем "адрес" поверхности
  if dc = 0 then Exit;
  SetDCPixelFormat (dc);                                                 // установить формат пикселей
  hrc := wglCreateContext (dc);                                          // создаёт контекст воспроизведения OGL
  if hrc = 0 then Exit;
  if not wglMakeCurrent (dc,hrc) then Exit;                              // Устанавливаем текущий контекст воспроизведения
  //начальные параметры вида
  glClearColor (0.0, 0.0, 0.0, 1.0);                                     // цвет фона
  {glEnable (GL_LIGHTING);                                               // Будем использовать свет
  glEnable (GL_LIGHT0);                                                  // Активируем источник света
  glEnable (GL_COLOR_MATERIAL); }                                        // Учитывать материал
  glEnable(GL_CULL_FACE);                                                // Не отображать
  glCullFace(GL_BACK);                                                   // Заднюю поверхность

  result := true;
end;

function IZGfx.CreateWin( Width, Height, Bits : int; FullScreen : byte ) : bool;
var
  WC : TWndClass;
  Style: Cardinal;
  r : TRect;
begin
  Result := false;

  hInstance := GetModuleHandle(nil);

  ZeroMemory( @WC, sizeof(wc));
  with WC do begin
    Style := cs_HRedraw or cs_VRedraw;
    lpfnWndProc := WinProc;
    cbClsExtra := 0;
    cbWndExtra := 0;
    hInstance := 0;
    hIcon := LoadIcon(hInstance,'ICO');
    hCursor := LoadCursor(0, idc_arrow);
    hbrBackground := HBRUSH(GetStockObject(BLACK_BRUSH));
    lpszMenuName := '';
    lpszClassName := AppName;
  end;

  if RegisterClass(WC) = 0 then
  Halt(255);

  if FullScreen = 1 then
  begin
    Style := WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
    CaptionBarHeight := 0;
    if not SetFullscreen( Width, Height, Options.BitDepth, Options.Freq ) then
    begin
      MsgErr('Неполучилось установить полноэкранный режим');
      Exit;
    end;
  end
  else
  begin
    Style := WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
    CaptionBarHeight := 32;
  end;

    r.Left := 0; r.Top := 0; r.Right := Width; r.Bottom := Height;
     if AdjustWindowRect(r, Style, false) then
     begin
       Width := r.Right-r.Left;
       Height := r.Bottom-r.Top;
     end;

  //wndPosX := GetSystemMetrics(SM_CXSCREEN) div 2 - Width div 2;
  //wndPosY := GetSystemMetrics(SM_CYSCREEN) div 2 - Height div 2;
  WinHWND := CreateWindow(AppName, TITLE, Style,
             0,                       //посередине ширины
             0,                       //посередине высоты
             Width, Height, 0, 0, hInstance, nil);
  SetFocus(WinHWND);
  if FullScreen = 0 then begin
    PostMessage(winHWND, WM_SYSCOMMAND, SC_MINIMIZE, 0);
    PostMessage(winHWND, WM_SYSCOMMAND, SC_RESTORE, 0);
  end;
  ShowWindow(winHWND,SW_SHOW);
  UpdateWindow(WinHWND);
  ShowCursor(false);
  result := RegisterClass(WC) <> 0;

  // Настройка проекции
  Coord := GFX_COORD_2D;

  if SetCoord(Coord, true) = GFX_OK then Quit := false;
end;

procedure IZGfx.GLDestroy (hrc : HGLRC);
begin
  wglMakeCurrent(0, 0);
  wglDeleteContext (hrc);
  ReleaseDC (WinHWND, DC);
  DeleteDC (DC);
  MsgOK('Контекст OpenGL - удалён.');
end;

{   PUBLIC FUNCTIONS   }
// Инициализация класса
constructor IZGfx.Create(Proc : Pointer);
var
  s: string;
  i : byte;
begin
  {s := ParamStr(0);
  i := pos('BaseAttack',s);
  delete(s,i+11,Length(s)-i+11);}
  iz_dir_local := ExtractFilePath(ParamStr(0));
  iz_dir_local_data     := iz_dir_local+'data\';
  iz_dir_local_sys      := iz_dir_local+'sys\';
  iz_dir_local_gfx      := iz_dir_local_data+'graphics\';
  iz_dir_local_sfx      := iz_dir_local_data+'sfx\';
  iz_dir_local_gfx_gui  := iz_dir_local_gfx+'gui\';
  iz_dir_local_temp     := iz_dir_local + 'temp\';
  iz_dir_local_temp_gfx := iz_dir_local + 'temp\graphics\';
  iz_dir_local_temp_sfx := iz_dir_local + 'temp\sfx\';
  WinProc := Proc;
  Options_Load(@Options,iz_dir_local_sys+'options.txt');
end;

procedure IZGfx.Init;
begin
  Quit := false;
  FName := 'log.txt';
  OpenLog(FName);
  ShowFPS := true;
  Options.Freq := 75;
  if not(CreateWin(Options.ScreenWidth,Options.ScreenHeight,Options.BitDepth,Options.FScreen))  then
  MsgOK('Окно создано');
  if not GLInit then Exit;
  Input := izInput.Create();
  input.Clear();
  MsgOK('Обработчик управления создан');
  //***Инициализируем текстурный и спрайтовый менеджеры\\
  TexMan := izTextureManager.Create();
  MsgOk('Менеджер текстурный инициализирован');
  SprMan := izSpriteManager.Create();
  MsgOk('Менеджер спрайтовый инициализирован');
  //***Инициализируем шрифты***\\
  FontMan := izFontManager.Create(@TexMan);
  MsgOk('Менеджер шрифтов инициализирован');
  //iz_SetMouseBound(0, 0, Options.ScreenWidth, Options.ScreenHeight);
  MsgOK('OpenGL - инициализирован');
end;

function IZGfx.SetFullscreen( Width, Height, Bits , Freq : word ) : bool;
var DeviceMode: TDevMode;
begin
  with DeviceMode do
  begin
    dmSize := SizeOf(DeviceMode);
    dmPelsWidth := Width;
    dmPelsHeight := Height;
    dmBitsPerPel := Bits;
    dmDisplayFrequency := Freq;
    dmFields:= DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL or DM_DISPLAYFREQUENCY;
  end;
  Result:=ChangeDisplaySettings(DeviceMode, CDS_FULLSCREEN)= DISP_CHANGE_SUCCESSFUL;;
end;

procedure IZGfx.RestoreResolution();
begin
  ChangeDisplaySettings(TDevMode(nil^), CDS_FULLSCREEN);
end;

function IZGfx.SetCoord(new_coord : IZGFXCOORD; Flag : bool) : IZGFXERROR;
begin
  Result := GFX_ERROR_NONE;
  if not Flag then
   if new_coord=coord then exit;
   case new_coord of
     GFX_COORD_2D : begin
       glDisable(GL_DEPTH_TEST);
       glMatrixMode(GL_PROJECTION);
       glLoadIdentity();
       glOrtho(0, Options.ScreenWidth, Options.ScreenHeight, 0, -1, 1);
       glMatrixMode(GL_MODELVIEW);
       glLoadIdentity();
       glTranslatef(0,0,-0.0001);
     end;
     GFX_COORD_3D : begin
       glEnable(GL_DEPTH_TEST);
       glMatrixMode(GL_PROJECTION);
       glLoadIdentity();
       gluPerspective(90, 640/480, 0.0, 100);
       glMatrixMode(GL_MODELVIEW);
       glLoadIdentity();
       glTranslatef(0,0,-10.0);
     end;
   end;
   coord := new_coord;
   Result := GFX_OK;
end;

procedure IZGfx.begin_2D_render();
begin
  glClear(GL_COLOR_BUfFER_BIT or GL_DEPTH_BUfFER_BIT);
end;

procedure IZGfx.End_render();
begin
  glFlush();
  SwapBuffers(DC);
  glLoadIdentity();
end;

// Удаление окна
procedure IZGfx.DestroyWin( Wnd : hWnd; FullScreen : byte );
begin
  if Fullscreen = 1 then
  begin
    ChangeDisplaySettings(TDevMode(nil^), CDS_FULLSCREEN);
    MsgOk('Установлено прежнее разрешение экрана');
  end;
  PostMessage(Wnd,WM_SYSCOMMAND,SC_MINIMIZE,0);
  ReleaseDC(WinHWND,hrc);
  MsgOK('Окно уничтожено');
  CloseLog();
  DestroyWindow(Wnd);  //Уничтожаем окно
end;

//Удаляем класс
destructor IZGfx.DestroyGfx();
begin
  Quit := True;
  FActive := False;
  Input.Destroy();
  MsgOK('Обработчик управления удалён');
  FontMan.Destroy();
  MsgOk('Менеджер шрифтов удалён');
  SprMan.Destroy();
  MsgOk('Менеджер спрайтовый удалён');
  TexMan.Destroy();
  MsgOk('Менеджер текстурный удалён');
  GLDestroy (hrc);
  DestroyWin( WinHWND, Options.FScreen);
  Windows.UnregisterClass( AppName, hInstance );
end;

procedure IZGfx.FPSUpdate();
begin
  CurrentTime := GetTickCount();
  DeltaTime := CurrentTime-StartTime;
  inc(FPSCount);
  if DeltaTime > 1000 then
  begin
    FPSstr := 'FPS='+IntToStr(FPSCount);
    if ShowFPS then if Options.FScreen = 0 then SetWindowText(winHWND,pchar(FPSstr));
    FPSCount := 0;
    DeltaTime := DeltaTime-1000;
    StartTime := CurrentTime;
  end;
end;

procedure IZGfx.TimerStart();
begin
  StartTime := GetTickCount;
  CurrentTime := GetTickCount;
  FPSCount := 0;
end;

function IZGfx.TimerEnd() : longInt;
begin
  RESULT := GetTickCount - StartTime;
end;

procedure IZGfx.OpenLog(Name : String);
begin
 AssignFile(LogFile, Name);
 Rewrite(LogFile);
 WriteLn(LogFile,'/*************************************************\');
 Writeln(LogFile,'IZOGLengine_Game LOG : ');
 IsOpened := True;
end;

procedure IZGfx.MsgMes(Str : String);
begin
 if not IsOpened then
  begin
   OpenLog(FName);
   IsOpened := True;
  end;
 WriteLn(LogFile,Str);
 Flush(LogFile);
end;

procedure IZGfx.MsgErr(Str : String);
begin
 if not IsOpened then
  begin
   OpenLog(FName);
   IsOpened := True;
  end;
 WriteLn(LogFile,'Error : ' + Str);
 Flush(LogFile);
end;

procedure IZGfx.MsgOK(Str : String);
begin
 if not IsOpened then
  begin
   OpenLog(FName);
   IsOpened := True;
  end;
 WriteLn(LogFile,'OK : ' + Str);
 Flush(LogFile);
end;

procedure IZGfx.CloseLog();
begin
  MsgMes('IZOGLengine_Game LOG : CLOSE');
  MsgMes('\*************************************************/');
  CloseFile(LogFile);
end;

end.
