/* Copyright (c) 2002-2012 Croteam Ltd. 
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation


This program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

#include "SeriousSam/StdH.h"
#include "MainWindow.h"
#include "resource.h"

// !!! FIXME : Make a clean abstraction, remove these #ifdefs.
#ifdef PLATFORM_UNIX
#include "SDL.h"
#endif

BOOL _bWindowChanging = FALSE;    // ignores window messages while this is set
#ifdef SE1_VULKAN
#ifdef PLATFORM_UNIX
SDL_Window * _hwndMain = NULL;
#else
HWND _hwndMain = NULL;
#endif
#else //  PLATFORM_UNIX
HWND _hwndMain = NULL;
#endif // SE1_VULKAN

static char achWindowTitle[256]; // current window title

// for window reposition function
static PIX _pixLastSizeI, _pixLastSizeJ;


#ifdef PLATFORM_WIN32
static HBITMAP _hbmSplash = NULL;
static BITMAP  _bmSplash;

// window procedure active while window changes are occuring
LRESULT WindowProc_WindowChanging( HWND hWnd, UINT message, 
			    WPARAM wParam, LPARAM lParam )
{
    switch( message ) {
    case WM_PAINT: {
      PAINTSTRUCT ps;
      BeginPaint(hWnd, &ps); 
      EndPaint(hWnd, &ps); 
 
      return 0;
                   } break;
    case WM_ERASEBKGND: {

      PAINTSTRUCT ps;
      BeginPaint(hWnd, &ps); 
      RECT rect;
      GetClientRect(hWnd, &rect); 
      FillRect(ps.hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
      HDC hdcMem = CreateCompatibleDC(ps.hdc); 
      SelectObject(hdcMem, _hbmSplash); 
      BitBlt(ps.hdc, 0, 0, _bmSplash.bmWidth, _bmSplash.bmHeight, hdcMem, 0, 0, SRCCOPY); 
//      StretchBlt(ps.hdc, 0, 0, rect.right, rect.bottom,
//        hdcMem, 0,0, _bmSplash.bmWidth, _bmSplash.bmHeight, SRCCOPY); 

      EndPaint(hWnd, &ps); 

      return 1; 
                   } break;
    case WM_CLOSE:
      return 0;
      break;
    }
    return DefWindowProcA(hWnd, message, wParam, lParam);
}

// window procedure active normally
LRESULT WindowProc_Normal( HWND hWnd, UINT message, 
			    WPARAM wParam, LPARAM lParam )
{
  switch( message ) {

  // system commands
  case WM_SYSCOMMAND: {
    switch( wParam & ~0x0F) {
    // window resizing messages
    case SC_MINIMIZE:
    case SC_RESTORE:
    case SC_MAXIMIZE:
      // relay to application
  	  PostMessage(NULL, message, wParam & ~0x0F, lParam);
      // do not allow automatic resizing
      return 0;
      break;
    // prevent screen saver and monitor power down
    case SC_SCREENSAVE:
    case SC_MONITORPOWER:
      return 0;
    }
                      } break;
  // when close box is clicked
  case WM_CLOSE:
    // relay to application
  	PostMessage(NULL, message, wParam, lParam);
    // do not pass to default wndproc
    return 0;

  // some standard focus loose/gain messages
  case WM_LBUTTONUP:
  case WM_LBUTTONDOWN:
  case WM_RBUTTONDOWN:
  case WM_CANCELMODE:
  case WM_KILLFOCUS:
  case WM_ACTIVATEAPP:
    // relay to application
  	PostMessage(NULL, message, wParam, lParam);
    // pass to default wndproc
    break;
  }

  // if we get to here, we pass the message to default procedure
  return DefWindowProcA(hWnd, message, wParam, lParam);
}

// main window procedure
LRESULT CALLBACK WindowProc( HWND hWnd, UINT message, 
			    WPARAM wParam, LPARAM lParam )
{
  // dispatch to proper window procedure
  if(_bWindowChanging) {
    return WindowProc_WindowChanging(hWnd, message, wParam, lParam);
  } else {
    return WindowProc_Normal(hWnd, message, wParam, lParam);
  }
}
#endif

// init/end main window management
void MainWindow_Init(void)
{
#ifdef PLATFORM_WIN32
  // register the window class
  WNDCLASSEXA wc;
  wc.cbSize = sizeof(wc);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = _hInstance;
  wc.hIcon = LoadIconA( _hInstance, (LPCSTR)IDR_MAINFRAME );
  wc.hCursor = NULL;
  wc.hbrBackground = NULL;
  wc.lpszMenuName = APPLICATION_NAME;
  wc.lpszClassName = APPLICATION_NAME;
  wc.hIconSm = NULL;
  if (0 == RegisterClassExA(&wc)) {
    DWORD dwError = GetLastError();
    CTString strErrorMessage(TRANS("Cannot open main window!"));
    CTString strError;
    strError.PrintF("%s Error %d", (const char*)strErrorMessage, dwError);
    FatalError(strError);
  }

  // load bitmaps
  _hbmSplash = LoadBitmapA(_hInstance, (char*)IDB_SPLASH);
  ASSERT(_hbmSplash!=NULL);
  GetObject(_hbmSplash, sizeof(BITMAP), (LPSTR) &_bmSplash); 
  // here was loading and setting of no-windows-mouse-cursor

#else
  STUBBED("load window icon");
#endif
}


void MainWindow_End(void)
{
#ifdef PLATFORM_WIN32
  DeleteObject(_hbmSplash);
#else
  STUBBED("");
#endif
}


// close the main application window
void CloseMainWindow(void)
{
  // if window exists
  if( _hwndMain!=NULL) {
    // destroy it
    #ifdef PLATFORM_WIN32
    DestroyWindow(_hwndMain);
    #else
    SDL_DestroyWindow((SDL_Window *) _hwndMain);
    #endif
    _hwndMain = NULL;
  }
}

void ResetMainWindowNormal(void)
{
#ifdef PLATFORM_WIN32
  ShowWindow( _hwndMain, SW_HIDE);
  // add edges and title bar to window size so client area would have size that we requested
  RECT rWindow, rClient;
  GetClientRect( _hwndMain, &rClient);
  GetWindowRect( _hwndMain, &rWindow);
  const PIX pixWidth  = _pixLastSizeI + (rWindow.right-rWindow.left) - (rClient.right-rClient.left);
  const PIX pixHeight = _pixLastSizeJ + (rWindow.bottom-rWindow.top) - (rClient.bottom-rClient.top);
  const PIX pixPosX   = (::GetSystemMetrics(SM_CXSCREEN) - pixWidth ) /2;
  const PIX pixPosY   = (::GetSystemMetrics(SM_CYSCREEN) - pixHeight) /2;
  // set new window size and show it
  SetWindowPos( _hwndMain, NULL, pixPosX,pixPosY, pixWidth,pixHeight, SWP_NOZORDER);
  ShowWindow(   _hwndMain, SW_SHOW);
#endif
}


// open the main application window for windowed mode
void OpenMainWindowNormal( PIX pixSizeI, PIX pixSizeJ)
{
#ifdef PLATFORM_WIN32
  ASSERT(_hwndMain==NULL);

  // create a window, invisible initially
  _hwndMain = CreateWindowExA(
	  WS_EX_APPWINDOW,
	  APPLICATION_NAME,
	  "",   // title
    WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU,
	  10,10,
	  100,100,  // window size
	  NULL,
	  NULL,
	  _hInstance,
	  NULL);
  // didn't make it?
  if( _hwndMain==NULL) FatalError(TRANS("Cannot open main window!"));
  SE_UpdateWindowHandle( _hwndMain);

  // set window title
  sprintf( achWindowTitle, TRANS("Serious Sam (Window %dx%d)"), pixSizeI, pixSizeJ);
  SetWindowTextA( _hwndMain, achWindowTitle);
  _pixLastSizeI = pixSizeI;
  _pixLastSizeJ = pixSizeJ;
  ResetMainWindowNormal();

#else
  SDL_snprintf( achWindowTitle, sizeof (achWindowTitle), TRANSV("Serious Sam (Window %dx%d)"), pixSizeI, pixSizeJ);
#ifdef SE1_VULKAN
  if( sam_iGfxAPI == 1) {
    _hwndMain = SDL_CreateWindow((const char*) strWindow1251ToUtf8(achWindowTitle), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pixSizeI, pixSizeJ, SDL_WINDOW_VULKAN);
    if( _hwndMain!=NULL) {
      CPrintF("SDL: CreateWindow with flag (SDL_WINDOW_VULKAN) Done.\n");
    }
  } else {
    _hwndMain = SDL_CreateWindow((const char*) strWindow1251ToUtf8(achWindowTitle), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pixSizeI, pixSizeJ, SDL_WINDOW_OPENGL);
  }
#else
  _hwndMain = SDL_CreateWindow((const char*) strWindow1251ToUtf8(achWindowTitle), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pixSizeI, pixSizeJ, SDL_WINDOW_OPENGL);
#endif // SE1_VULKAN
  if( _hwndMain==NULL) FatalError(TRANS("Cannot open main window!"));
  SE_UpdateWindowHandle( _hwndMain);
  _pixLastSizeI = pixSizeI;
  _pixLastSizeJ = pixSizeJ;
#endif
}


// open the main application window for fullscreen mode
void OpenMainWindowFullScreen( PIX pixSizeI, PIX pixSizeJ)
{
#ifdef PLATFORM_WIN32
  ASSERT( _hwndMain==NULL);
  // create a window, invisible initially
  _hwndMain = CreateWindowExA(
    WS_EX_TOPMOST | WS_EX_APPWINDOW,
    APPLICATION_NAME,
    "",   // title
    WS_POPUP,
    0,0,
    pixSizeI, pixSizeJ,  // window size
    NULL,
    NULL,
    _hInstance,
    NULL);
  // didn't make it?
  if( _hwndMain==NULL) FatalError(TRANS("Cannot open main window!"));
  SE_UpdateWindowHandle( _hwndMain);

  // set window title and show it
  sprintf( achWindowTitle, TRANS("Serious Sam (FullScreen %dx%d)"), pixSizeI, pixSizeJ);
  SetWindowTextA( _hwndMain, achWindowTitle);
  ShowWindow(    _hwndMain, SW_SHOWNORMAL);

#else
  SDL_snprintf( achWindowTitle, sizeof (achWindowTitle), TRANSV("Serious Sam (FullScreen %dx%d)"), pixSizeI, pixSizeJ);
#ifdef SE1_VULKAN
  if( sam_iGfxAPI == 1) {
    _hwndMain = SDL_CreateWindow((const char*)strWindow1251ToUtf8(achWindowTitle), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pixSizeI, pixSizeJ, SDL_WINDOW_VULKAN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS);
    if( _hwndMain!=NULL) {
      CPrintF("SDL: CreateWindow with flag (SDL_WINDOW_VULKAN) Done.\n");
    }
  } else {
    _hwndMain = SDL_CreateWindow((const char*)strWindow1251ToUtf8(achWindowTitle), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pixSizeI, pixSizeJ, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS);
  }
#else
    _hwndMain = SDL_CreateWindow((const char*)strWindow1251ToUtf8(achWindowTitle), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pixSizeI, pixSizeJ, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS);
#endif // SE1_VULKAN
  if( _hwndMain==NULL) FatalError(TRANS("Cannot open main window!"));
  SE_UpdateWindowHandle( _hwndMain);
  _pixLastSizeI = pixSizeI;
  _pixLastSizeJ = pixSizeJ;
#endif
}


// open the main application window invisible
void OpenMainWindowInvisible(void)
{
#ifdef PLATFORM_WIN32
  ASSERT(_hwndMain==NULL);
  // create a window, invisible initially
  _hwndMain = CreateWindowExA(
	  WS_EX_APPWINDOW,
	  APPLICATION_NAME,
	  "",   // title
    WS_POPUP,
	  0,0,
	  10, 10,  // window size
	  NULL,
	  NULL,
	  _hInstance,
	  NULL);
  // didn't make it?
  if( _hwndMain==NULL) {
    DWORD dwError = GetLastError();
    CTString strErrorMessage(TRANS("Cannot open main window!"));
    CTString strError;
    strError.PrintF("%s Error %d", (const char *)strErrorMessage, dwError);
    FatalError(strError);
  }
  SE_UpdateWindowHandle( _hwndMain);

  // set window title
  sprintf( achWindowTitle, "Serious Sam");
  SetWindowTextA( _hwndMain, achWindowTitle);

#else

  STUBBED("Need SDL invisible window or something");

#endif
}

