#include <InterViews\interact.h>
#include <InterViews\canvas.h>
#include <InterViews\X11\palette.h>
#include <InterViews\X11\worldrep.h>
#include <InterViews\itable.h>
#include <stdio.h>
#include <string.h>

const int MAXSIZE = 100;
const int MAXARGUMENT = 25;

HANDLE hInstance_;
HANDLE hPrevInstance_;
LPSTR  lpCmdLine_;
int    nCmdShow_;

int    argc_ = 0;
char*  argv_[MAXARGUMENT];

int    argc;
char** argv;

/*
 *  IVMain ist die Main-Funktion der InterViews Anwendung. Sie wird von
 *  WinMain, das selbst in diesem Header vor dem Anwender versteckt ist,
 *  aufgerufen.
 */

extern int IVMain(int, char**);

/*
 *  Die Message Queue der Applikation wird vergr”áert.
 */

void SetMessageQueueLength () {
    int size = MAXSIZE;
    while (SetMessageQueue(size) == 0) {
        size -= 5;
    }
}

/*
 *  Die beiden Parameter argc und argv werden, wie unter C bekannt und
 *  auch unter Interviews erwartet, erzeugt. IV ver„ndert argc und argv
 *  w„hrend des Programmablaufes. Daher ist es notwendig, die Kommando-
 *  zeilenparameter in einem eigenen Puffer abzustellen (argc_, argv_).
 */

void SetCommandLineArguments () {
    char* arg;
    char module[128];

                     /* argv_[0] - Programmname */

    GetModuleFileName(hInstance_, module, 128);
    int len = strlen(module)+1;
    argv_[argc_] = new char(len);
    strcpy(argv_[argc_], module);
    argc_++;

                     /* argv_[i] - Kommandozeilenparameter */

    arg = strtok(lpCmdLine_, " ");
    if (arg) {
         argv_[argc_++] = arg;
         while ((arg = strtok(NULL, " ")) != NULL) {
             argv_[argc_++] = arg;
         }
    }

                     /* argc, argv */

    argc = argc_;
    argv = new char*[argc];
    for (int i = 0; i < argc; i++) {
        argv[i] = argv_[i];
    }
}

void DeleteCommandLineArguments () {
    delete  argv_[0];
}

/*
 *  Diese Funktion ist der Eintrittspunkt von Windows. Sie soll vor dem
 *  InterViews Anwender versteckt bleiben. Der Anwender hat damit das von
 *  X gewohnte Bild seines Hauptprogrammes (einzige Konvention:
 *  IVMain(int, char**) statt main(int, char**).
 */

int PASCAL WinMain(
    HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow
) {
    hInstance_     = hInstance;
    hPrevInstance_ = hPrevInstance;
    lpCmdLine_     = lpCmdLine;
    nCmdShow_      = nCmdShow;

    SetMessageQueueLength();
    SetCommandLineArguments();

    IVMain(argc, argv);

    DeleteCommandLineArguments();
    return(NULL);
}

extern "C" {
long FAR PASCAL IVWindowProc (HWND     hWnd,
			      unsigned message,
			      WORD     wParam,
			      LONG     lParam
) {
    Interactor* i;
    MSG oldMsg;

    switch (message) {

	case WM_SIZE:
	    while (PeekMessage(
		       &oldMsg, hWnd, WM_SIZE, WM_SIZE, PM_REMOVE | PM_NOYIELD
		   ));

	case WM_MOVE:
	    while (PeekMessage(
		       &oldMsg, hWnd, WM_MOVE, WM_MOVE, PM_REMOVE | PM_NOYIELD
		   ));

	case WM_SHOWWINDOW:
	case WM_PAINT:
	case WM_SETFOCUS:
	case WM_KILLFOCUS:
	    PostMessage(hWnd, message, wParam, lParam);
	    break;

	case WM_ERASEBKGND:
	    if (_world->itable()->Find(i, (void*)hWnd)) {
		RECT rect;

		if (GetUpdateRect(hWnd, &rect, false)) {
		    HBRUSH hBrush, holdBrush;

		    hBrush = (HBRUSH)i->GetCanvas()->GetBackground();
		    UnrealizeObject(hBrush);
		    holdBrush = SelectObject(wParam, hBrush);
		    PatBlt(
			wParam,
			rect.left,
			rect.top,
			rect.right - rect.left,
			rect.bottom - rect.top,
			PATCOPY
		    );
		    SelectObject(wParam, holdBrush);
		    return (1L);
		}
	    }
	    break;

	case WM_PALETTECHANGED:
	    if (wParam != hWnd) {
		HDC hDC = GetDC(hWnd);
		HPALETTE holdPal = SelectPalette(hDC, _palette->GetPalette(), 0);
		RealizePalette(hDC);
		UpdateColors(hDC);
		SelectPalette(hDC, holdPal, 0);
		ReleaseDC(hDC, hWnd);
	    }
	    break;

	case WM_DESTROY:
	    if (hWnd == _world->hTimerWindow) {
		PostQuitMessage(0);
	    }
	    break;

	default:
	    return (DefWindowProc(hWnd, message, wParam, lParam));
	    break;
    }
    return (0L);
}
}


void RegisterChildClass () {
     WNDCLASS wndclass;
     do {
	  wndclass.style         = CS_HREDRAW | CS_VREDRAW;
	  wndclass.lpfnWndProc   = IVWindowProc;
	  wndclass.cbClsExtra    = 0;
	  wndclass.cbWndExtra    = 0;
	  wndclass.hInstance     = hInstance_;
	  wndclass.hIcon         = NULL;
	  wndclass.hCursor       = NULL;
	  wndclass.lpszClassName = "IVChild";
	  wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
	  wndclass.lpszMenuName  = NULL;
     }
     while (!RegisterClass(&wndclass));
}

void UnregisterChildClass () {
    UnregisterClass("IVChild", hInstance_);
}

HWND MakeTimerWindow () {
    WNDCLASS wc;

    wc.style = NULL;
    wc.lpfnWndProc = IVWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance_;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "DummyClass";
    wc.hIcon = NULL;
    wc.hCursor = NULL;
    wc.hbrBackground = NULL;

    RegisterClass(&wc);

    return CreateWindow(
	       "DummyClass",
	       "Timer",
	       WS_POPUP,
	       0,
	       0,
	       10,
	       10,
	       NULL,
	       NULL,
	       hInstance_,
	       NULL
	  );
}

WorldRep::WorldRep() {
   _hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
   _max_palette_entry = 0;

   hTimerWindow = MakeTimerWindow();
   SetTimer(hTimerWindow, TIMER_ENTERLEAVE, 200, NULL);

   XBorder = GetSystemMetrics(SM_CXFRAME);
   YBorder = GetSystemMetrics(SM_CYFRAME);
   YCaption = GetSystemMetrics(SM_CYCAPTION);

   RegisterChildClass();
}

WorldRep::~WorldRep() {
   DeleteDC(_hDC);
   KillTimer(hTimerWindow, TIMER_ENTERLEAVE);
   DestroyWindow(hTimerWindow);
   UnregisterClass("DummyClass", hInstance_);
}

HANDLE           WorldRep::hinstance()         { return _hinstance; };
HANDLE           WorldRep::hprevinstance()     { return _hprevinstance; }
char*            WorldRep::hostname()          { return _host; };
HWND             WorldRep::root()              { return _root; };
WORD             WorldRep::max_palette_entry() { return _max_palette_entry; };
InteractorTable* WorldRep::itable()            { return _itable; };
HDC              WorldRep::hdc()               { return _hDC; };
