#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <process.h>
#include <stdio.h>
#include "resource.h"
#include "afxres.h"
#include "multisdi.h"

HINSTANCE hInst;
char szAppName[]  = "MultiSDI";
char szAppTitle[] = "Multithread SDI Application";

// Критическая секция для рисования в окне
CRITICAL_SECTION csWindowPaint; 

// Признак завершения всех задач
BOOL fTerminate = FALSE;

// Массив идентификаторов запущенных задач
HANDLE hThreads[3];

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  // Сохраняем идентификатор приложения
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    // Если было, выдвигаем окно приложения на
    // передний план
    if(IsIconic(hWnd))
	    ShowWindow(hWnd, SW_RESTORE);
	  SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), 
    IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), 
    IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
	  return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
     NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Отображаем окно и запускаем цикл 
  // обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, 
        LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_PAINT,      WndProc_OnPaint);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, 
                      LPCREATESTRUCT lpCreateStruct)
{
  // Инициализируем критическую секцию
  InitializeCriticalSection(&csWindowPaint);

  // Сбрасываем флаг завершения задач
  fTerminate = FALSE;

  // Запускаем три задачи, сохраняя их идентификаторы
  // в массиве
  hThreads[0] = (HANDLE)_beginthread(PaintEllipse, 
    0, (void*)hWnd);
  hThreads[1] = (HANDLE)_beginthread(PaintRect, 
    0, (void*)hWnd);
  hThreads[2] = (HANDLE)_beginthread(PaintText, 
    0, (void*)hWnd);

  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  // Устанавливаем флаг завершения задач
  fTerminate = TRUE;

  // Дожидаемся завершения всех трех задач
  WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);
  
  // Перед удаляем критическую секцию
  DeleteCriticalSection(&csWindowPaint);
  
  // Останавливаем цикл обработки сообщений, расположенный 
  // в главной задаче
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnPaint
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnPaint(HWND hWnd)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;

  // Входим в критическую секцию
  EnterCriticalSection(&csWindowPaint);

  // Перерисовываем внутреннюю область окна
  hdc = BeginPaint(hWnd, &ps);
  
  GetClientRect(hWnd, &rc);
  DrawText(hdc, "SDI Window", -1, &rc,
    DT_SINGLELINE | DT_CENTER | DT_VCENTER);

  EndPaint(hWnd, &ps);

  // Выходим из критической секции
  LeaveCriticalSection(&csWindowPaint);
  return 0;
}
// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case ID_FILE_EXIT:  
    {
	    // Завершаем работу приложения
      PostQuitMessage(0);
	    return 0L;
	    break;
    }
	  
  	case ID_HELP_ABOUT:
    {
      MessageBox(hWnd, 
        "Multithread SDI Application\n"
        "(C) Alexandr Frolov, 1996\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
	     return 0L;
	     break;
    }

  	default:
	    break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify,
    DefWindowProc);
}

// -----------------------------------------------------
// Функция задачи PaintEllipse
// -----------------------------------------------------
void PaintEllipse(void *hwnd)
{
  HDC hDC;
  RECT rect;
  LONG xLeft, xRight, yTop, yBottom;
  short nRed, nGreen, nBlue;
  HBRUSH hBrush, hOldBrush;

  // Инициализация генератора случайных чисел
  srand((unsigned int)hwnd);

  // Задача работает в бесконечном цикле,
  // который будет прерван при завершении приложения
  while(!fTerminate) 
  {
    // Входим в критическую секцию
    EnterCriticalSection(&csWindowPaint);

    // Отображаем эллипс, имеющий случайный цвет,
    // форму и расположение
    
    // Получаем контекст отображения
    hDC = GetDC(hwnd);

    // Получаем случайные цветовые компоненты
    nRed   = rand() % 255;
    nGreen = rand() % 255;
    nBlue  = rand() % 255;
    
    // Получаем случйные размеры эллипса
    GetWindowRect(hwnd, &rect);
    
    xLeft   = rand() % (rect.left   + 1);
    xRight  = rand() % (rect.right  + 1);
    yTop    = rand() % (rect.top    + 1);
    yBottom = rand() % (rect.bottom + 1);

    // Создаем кисть на основе случайных цветов
    hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue));
    
    // Выбираем кисть в контекст отображения
    hOldBrush = SelectObject(hDC, hBrush);

    // Рисуем эллипс
    Ellipse(hDC, min(xLeft, xRight), min(yTop, yBottom),
                 max(xLeft, xRight), max(yTop, yBottom));

    // Выбираем старую кисть в контекст отображения
    SelectObject(hDC, hOldBrush);

    // Удаляем созданную кисть
    DeleteObject(hBrush);

    // Освобождаем контекст отображения
    ReleaseDC(hwnd, hDC);
    
    // Выходим из критической секции
    LeaveCriticalSection(&csWindowPaint);

    // Выполняем задержку на 500 мс
    Sleep(500);
  }
}

// -----------------------------------------------------
// Функция задачи PaintRect
// -----------------------------------------------------
void PaintRect(void *hwnd)
{
  HDC hDC;
  RECT rect;
  LONG xLeft, xRight, yTop, yBottom;
  short nRed, nGreen, nBlue;
  HBRUSH hBrush, hOldBrush;

  srand((unsigned int)hwnd + 1);

  while(!fTerminate) 
  {
    EnterCriticalSection(&csWindowPaint);

    hDC = GetDC(hwnd);

    nRed   = rand() % 255;
    nGreen = rand() % 255;
    nBlue  = rand() % 255;
    
    GetWindowRect(hwnd, &rect);
    
    xLeft   = rand() % (rect.left   + 1);
    xRight  = rand() % (rect.right  + 1);
    yTop    = rand() % (rect.top    + 1);
    yBottom = rand() % (rect.bottom + 1);

    hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue));
    hOldBrush = SelectObject(hDC, hBrush);

    Rectangle(hDC, min(xLeft, xRight), min(yTop, yBottom),
                 max(xLeft, xRight), max(yTop, yBottom));

    SelectObject(hDC, hOldBrush);
    DeleteObject(hBrush);
    ReleaseDC(hwnd, hDC);
    
    LeaveCriticalSection(&csWindowPaint);
    Sleep(500);
  }
}

// -----------------------------------------------------
// Функция задачи PaintText
// -----------------------------------------------------
void PaintText(void *hwnd)
{
  HDC hDC;
  RECT rect;
  LONG xLeft, xRight, yTop, yBottom;
  short nRed, nGreen, nBlue;

  srand((unsigned int)hwnd + 2);

  while(!fTerminate) 
  {
    EnterCriticalSection(&csWindowPaint);

    hDC = GetDC(hwnd);

    GetWindowRect(hwnd, &rect);
    
    xLeft   = rand() % (rect.left   + 1);
    xRight  = rand() % (rect.right  + 1);
    yTop    = rand() % (rect.top    + 1);
    yBottom = rand() % (rect.bottom + 1);

    // Устанавливаем случайный цвет текста
    nRed   = rand() % 255;
    nGreen = rand() % 255;
    nBlue  = rand() % 255;
    SetTextColor(hDC, RGB(nRed, nGreen, nBlue));

    // Устанавливаем случайный цвет фона
    nRed   = rand() % 255;
    nGreen = rand() % 255;
    nBlue  = rand() % 255;
    SetBkColor(hDC, RGB(nRed, nGreen, nBlue));

    TextOut(hDC, xRight - xLeft,
      yBottom - yTop,"TEXT", 4);

    ReleaseDC(hwnd, hDC);
    
    LeaveCriticalSection(&csWindowPaint);
    Sleep(500);
  }
}

