//  ======================================================================
//               THIS IMPLEMENTATION WRITTEN BY KK - SPRING 1998
//  ======================================================================

#include        "winplus.h"
#pragma         hdrstop

/*
  Дата написания - начало 1998 г.
    сентябрь 98 - исправлен баг в GlobalWindowProc: как параметр WM_COMMAND
  передавалось все двойное слово wParam, а не младшие 16 бит
    15 сентября - возможность создания по готовому классу (версия 2)
    26 сентября - изменен прототип функции отклика на WM_COMMAND, чтобы
  ей передавалась вся информация из сообщения
*/

// Автоматическая генерация имен для классов окна.
static UINT count=0;
static void CreateClassName(char *dst)
{
  wsprintf(dst,"TFCL%0004X",count++);
}

// Эта функция назначается оконной при вызове для 
static long FAR PASCAL
GlobalTemporaryProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  return DefWindowProc(hWnd, message, wParam, lParam);
}

static long FAR PASCAL
GlobalWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  static CWindowBase *mainwindow;
  HDC dc;
  PAINTSTRUCT ps;
  mainwindow=(CWindowBase *)GetWindowLong(hWnd,GWL_USERDATA);
  if(mainwindow==NULL)
  {
    ::MessageBox(NULL,"Пришло собщение от неизвестного окна",
      "Ошибка в каркасной библиотеке: GlobalWindowProc()",
      MB_OK|MB_ICONHAND);
    ::TerminateProcess(::GetModuleHandle(NULL),1);
  }
  if(mainwindow->bSuperclassed)
  {
    if(mainwindow->OnEvent(message,wParam,lParam))
      return ::CallWindowProc(mainwindow->DefaultWindowProc,hWnd,
        message,wParam,lParam);
    else 
      return 0L;
  }
  switch(message)
  {
    case WM_CLOSE:
      mainwindow->bInOnClose=TRUE;
      if(mainwindow->OnClose())
      {
        mainwindow->bInOnClose=FALSE;
        DestroyWindow(hWnd);
        return 0L;
      }
      mainwindow->bInOnClose=FALSE;
      return -1;
    case WM_PAINT:
      dc=BeginPaint(hWnd, &ps);
      mainwindow->OnPaint(dc, ps.rcPaint, ps.fErase);
      EndPaint(hWnd, &ps);
      return 0;
    case WM_COMMAND:
      if(mainwindow->OnCommand(LOWORD(wParam),HIWORD(wParam),(HWND)lParam))
        break;
      return 0;
    case WM_DESTROY:
      PostQuitMessage(0);
      mainwindow=NULL;
      return 0;
    default:
      long lResult=mainwindow->OnEvent(message, wParam, lParam);
      if((lResult==0)||(lResult==-1)) return lResult;
  }
  return DefWindowProc(hWnd,message,wParam,lParam);
}

int CWindowBase::MessageLoop()
{
  MSG msg;
  if(haccel)
  {
    while(1)
    {
      while((::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)))
      {
        if(msg.message==WM_QUIT) goto MessageLoopBreak;
        if(!TranslateAccelerator(msg.hwnd, haccel, &msg))
        {
          ::TranslateMessage(&msg);
          ::DispatchMessage(&msg);
        }
      }
      OnIdle();
    }
  }
  else
  {
    while(TRUE)
    {
      while((::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)))
      {
        if(msg.message==WM_QUIT) goto MessageLoopBreak;
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
      }
      OnIdle();
    }
  }
MessageLoopBreak:
  return msg.wParam;
}

CWindowBase::CWindowBase()
{
  bCreated=FALSE;
  bSuspend=TRUE;
  bInOnClose=FALSE;
}

CWindowBase::~CWindowBase()
{
  ::UnregisterClass(ClassName,hinstance);
}

BOOL CWindowBase::Create(HINSTANCE hInstance, int nCmdShow)
{
  DWORD Override;
  HMENU hMenu;
  bSuperclassed=FALSE;
  hinstance = hInstance;
  DefaultWindowProc=DefWindowProc;
  Override=OverrideDefaults(OD_CLASSNAME);
  CreateClassName(ClassName);    
  if(Override!=OD_DEFAULT)
  {
    if(::GetClassInfo(hInstance,(LPSTR)Override,&wc))
    {
      if(wc.lpfnWndProc!=GlobalWindowProc)
      bSuperclassed=TRUE;
    }
    else
      if(::GetClassInfo(NULL,(LPSTR)Override,&wc))
      {
        if(wc.lpfnWndProc!=GlobalWindowProc)
        bSuperclassed=TRUE;
      }
  }
  wc.lpszClassName=ClassName;
  wc.hInstance = hInstance;
  if(bSuperclassed==FALSE)
  {
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = GlobalTemporaryProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    Override=OverrideDefaults(OD_HICON);
    if(Override!=OD_DEFAULT)
      wc.hIcon = (HICON)Override;
    else
      wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    Override=OverrideDefaults(OD_HCURSOR);
    if(Override!=OD_DEFAULT)
      wc.hCursor = (HCURSOR)Override;
    else
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    Override=OverrideDefaults(OD_HBRUSH);
    if(Override!=OD_DEFAULT)
      wc.hbrBackground = (HBRUSH)Override;
    else
      wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    Override=OverrideDefaults(OD_HMENU);
    hMenu=(HMENU)Override;
    if(Override!=OD_DEFAULT)
      wc.lpszMenuName=(LPSTR)Override;
    else
      wc.lpszMenuName=NULL;
  }
  else
  {
    hMenu=(HMENU)OverrideDefaults(OD_HMENU);
    DefaultWindowProc=wc.lpfnWndProc;
  }
  if((DWORD)hMenu==OD_DEFAULT) hMenu=NULL;
  ClassId=::RegisterClass(&wc);
  if(!ClassId) return FALSE;
  Override=OverrideDefaults(OD_X);
  if(Override!=OD_DEFAULT)
    x=(int)Override;
  else
    x=CW_USEDEFAULT;
  Override=OverrideDefaults(OD_Y);
  if(Override!=OD_DEFAULT)
    y=(int)Override;
  else
    y=CW_USEDEFAULT;
  Override=OverrideDefaults(OD_WIDTH);
  if(Override!=OD_DEFAULT)
    width=(int)Override;
  else
    width=CW_USEDEFAULT;
  Override=OverrideDefaults(OD_HEIGHT);
  if(Override!=OD_DEFAULT)
    height=(int)Override;
  else
    height=CW_USEDEFAULT;
  Override=OverrideDefaults(OD_TITLE);
  if(Override!=OD_DEFAULT)
    title=(LPSTR)Override;
  else
    title="Derived from 'CWindowBase'";
  Override=OverrideDefaults(OD_STYLE);
  if(Override!=OD_DEFAULT)
    style=(DWORD)Override;
  else
    style=WS_OVERLAPPEDWINDOW;
  Override=OverrideDefaults(OD_HACCEL);
  if(Override!=OD_DEFAULT)
    haccel=(HACCEL)Override;
  else
    haccel=NULL;
  Override=OverrideDefaults(OD_HPARENTWND);
  if(Override!=OD_DEFAULT)
    parent=(HWND)Override;
  else
    parent=NULL;
  hwnd = ::CreateWindow(
      wc.lpszClassName,
      title,
      style,
      x, y,
      width, height,
      parent,
      (style&WS_CHILD) ? hMenu : NULL,
      hInstance,
      NULL );
  if(hwnd)
  {
    ::SetWindowLong(hwnd,GWL_USERDATA,(DWORD)this);
    ::SetWindowLong(hwnd,GWL_WNDPROC,(DWORD)GlobalWindowProc);
    if(OnCreate(hwnd)==-1)
    {
      ::DestroyWindow(hwnd);
      return FALSE;
    }
    else bCreated=TRUE;
  }
  return bCreated;
}

void CWindowBase::OnIdle()
{
  ::WaitMessage();
}

BOOL CWindowBase::CreateAndShow(HINSTANCE hInstance, int nCmdShow)
{
  if(!Create(hInstance, nCmdShow)) return FALSE;
  ::ShowWindow(hwnd, nCmdShow);
  ::UpdateWindow(hwnd);
  OnInitialUpdate();
  return TRUE;
}

void CWindowBase::SetTitle(LPSTR lpszTitle)
{
  strcpy(title, lpszTitle);
  ::SetWindowText(hwnd,lpszTitle);
}

void CWindowBase::SetPos(RECT &rc)
{
  width=rc.right-rc.left;
  height=rc.bottom-rc.top;
  if(bCreated)
    ::SetWindowPos(hwnd, HWND_TOP, rc.left, rc.top, width, height,
        SWP_DRAWFRAME|SWP_NOACTIVATE|SWP_NOZORDER);
}

void CWindowBase::Invalidate()
{
  RECT rc;
  ::GetClientRect(GetWnd(),&rc);
  ::InvalidateRect(GetWnd(),&rc,TRUE);
  ::UpdateWindow(GetWnd());
}

LRESULT CWindowBase::Perform(UINT Msg,WPARAM wParam,LPARAM lParam)
{
  return 
    ::CallWindowProc(DefaultWindowProc,hwnd,Msg,wParam,lParam);
}

void CFatalErrorReport::DisplayString(char *lpszStr, DWORD dwButtons)
{
  CFormattedMessage::DisplayString(lpszStr,MB_OK|MB_ICONHAND);
  CWindowBase *CurTask;
  CurTask=(CWindowBase *)::GetWindowLong(::GetActiveWindow(),
    GWL_USERDATA);
  if(CurTask!=NULL) 
  {
    if(CurTask->InOnClose()==FALSE)
      CurTask->OnClose();
  }
  ::TerminateProcess(::GetModuleHandle(NULL),1);
}

static char szBuffer[256];
void *MemOverride::malloc(DWORD dwSize)
{
  void *lpBlock = ::malloc(dwSize);
  // Замечание. НЕ СТОИТ использовать следующий код:
  // ... = (void*)GlobalAlloc(GMEM_FIXED,dwSize);
  // так как стандартные функции библиотеки языка WATCOM C используют
  // методику, похожую на методику работы функции z_malloc в DOOM-е:
  // они распределяют большие куски памяти (через VirtualAlloc),
  // которые потом выделяют приложению по частям.
  if(lpBlock) return lpBlock;
  wsprintf(szBuffer,
    "ошибка распределения %8X (%u) байт памяти.\n"
    "Повторить попытку распределения памяти - ?\n",
    dwSize,dwSize);
  // Предоставляем пользователю возможность повторить попытку 
  // распределения памяти либо завершаем выполнение.
  while(1)
  {
    CFatalErrorReport FER;
    int Response;
    Response=::MessageBox(::GetActiveWindow(),szBuffer,
      "В системе заканчивается виртуальная память.",
      MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_TASKMODAL);
    switch(Response)
    {
      case IDABORT:
        FER.Report("Ошибка распределения памяти.\n"
          "Приложение заканчивает свою работу");
      case IDRETRY:
        lpBlock = ::malloc(dwSize);
        if(lpBlock) return lpBlock;
        continue;
      case IDIGNORE:
        return NULL;
    }
  }
}

void MemOverride::free(void *lpBlock)
{
  // Справедливо то же замечание.
  ::free(lpBlock);
}

void __declspec(dllexport) __stdcall utCenterWindow(HWND win)
{
  int w=GetSystemMetrics(SM_CXSCREEN);
  int h=GetSystemMetrics(SM_CYSCREEN);
  RECT rc;
  GetWindowRect(win,&rc);
  int ww=rc.right-rc.left;
  int wh=rc.bottom-rc.top;
  SetWindowPos(win,HWND_NOTOPMOST,(w-ww) >> 1,(h-wh) >> 1,0,0,SWP_NOSIZE);
}

