#if !defined(WINAPP_H)
/* WINAPP:  An object of this class is defined once for every application. It
    contains the items passed to WinMain, as well as access functions for
    returning them. The constructor initializes them. */
class WinApp { // Windows application class
    static HANDLE hInstance;
    static HANDLE hPrevInstance;
    static LPSTR lpszCmdLine;
    static int nCmdShow;
public:
    WinApp(HANDLE hinst, HANDLE hpinst, LPSTR cmdline, int cmdshow) {
        hInstance = hinst;
        hPrevInstance = hpinst;
        lpszCmdLine = cmdline;
        nCmdShow = cmdshow;
    }
    static HANDLE GetInstance(void) { return hInstance; }
    static HANDLE GetPrevInstance(void) { return hPrevInstance; }
    static LPSTR GetCmdLine(void) { return lpszCmdLine; }
    static int GetCmdShow(void) { return nCmdShow; }
    static int MessageLoop(void) { // default message loop processing
        MSG msg;
        while(GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
};
/* WINCLASS */
const NOSTYLE = 0;
const NOEXTRABYTES = 0;
class WinClass : WNDCLASS {
    BOOL class_registered;
public:
    WinClass(void) {
        style = NOSTYLE;
        lpfnWndProc = DefWindowProc;
        cbClsExtra = NOEXTRABYTES;
        cbWndExtra = NOEXTRABYTES;
        hInstance = NULL;
        hIcon = LoadIcon(NULL, IDI_APPLICATION);
        hCursor = LoadCursor(NULL, IDC_ARROW);
        hbrBackground = GetStockObject(WHITE_BRUSH);
        lpszMenuName = NULL;
        lpszClassName = NULL;
        class_registered = FALSE;
    }
    // since 'this' is derived from WNDCLASS, it's passed as one
    void Register(void) {
        RegisterClass(this);
        class_registered = TRUE;
    }
    BOOL Registered(void)  { return class_registered; }
    friend class Window;     // allow Window to modify members
};

/* WINHANDLE:  An object of this class is created with every instance
    of Window (below). It contains data that controls Window creation
    handling, as well as the window handle. */
class WinHandle {
    HWND hWnd;
    LPSTR classname;
    LPSTR windowname;
    DWORD winstyle;
    int upper_left_x;
    int upper_left_y;
    int winwidth;
    int winheight;
    HWND winParent;
    HMENU menu;
    HANDLE hInstance;
    LPSTR lpParam;
public:
    WinHandle(void) {
        hWnd = NULL;
        classname = (LPSTR)"WinApp";
        windowname = (LPSTR)"WinApp:Window";
        winstyle = 0;
        upper_left_x = upper_left_y = winwidth = winheight = 0;
        winParent = NULL;
        menu = NULL;
        hInstance = 0;
        lpParam = NULL;
    }
    HWND GetHandle(void) { return hWnd; }
    HWND Create(void) {
        if(hWnd)           // if window's already created, return TRUE
            return TRUE;
        hWnd = CreateWindow(classname,windowname,winstyle,
                          upper_left_x,upper_left_y,winwidth,winheight,
                          winParent,menu,hInstance, lpParam);
        return (hWnd ? TRUE : FALSE);
    }
    friend class Window;          // allow Window to modify members
};
/* WINDOW:  Every window is derived from this abstract class.
    Every class derived from this one must have a WinClass object
    (or a pointer to one created by a derived class) and pass it back
    to Window. And each must supply its own WndProc for the class. */
class Window {
    WinHandle WHdl;
    int current_display;
    int previously_visible;
    void Show(void) {
        previously_visible = ShowWindow(GetHandle(),current_display);
    }
protected:
    WinClass *MyWc;
    WinApp *MyApp;
public:
    Window(WinApp *app, WinClass *wc) {
        MyApp = app;
        MyWc = wc;
        DefaultDisplay();
        previously_visible = FALSE;
    };
    // displays window and creates if not already created
    BOOL Display(void) {
        Register();
        if(!Create()) return FALSE;
        Show();
        Update();
        return TRUE;
    }
    BOOL Display(int display_style) {
        current_display = display_style;
        return Display();
    }
    void Register(void) {
        if(!MyWc->Registered()) {            // if class not registered
            SetClassWinXbytes(sizeof(Window *));
            SetClassInstance();         // insert instance handle
            if(!GetClassName())        // if class name not set
                SetClassName("WinApp");
            MyWc->Register();         // register the class
        }
    }
    BOOL Create(void)  {
        Register();
        WHdl.hInstance = MyApp->GetInstance();
        WHdl.classname = MyWc->lpszClassName;
        WHdl.lpParam = (LPSTR)this;
        return WHdl.Create();
    }
    HWND GetHandle(void) { return WHdl.hWnd; }
    void Hide(void) // hides the window
        { Display(SW_HIDE); }
    void Minimize(void) // minimizes the window
        { Display(SW_MINIMIZE); }
    void Maximize(void) // maximizes the window
        { Display(SW_SHOWMAXIMIZED); }
    void Normalize(void) // displays window in original size and position
        { Display(SW_SHOWNORMAL); }
    void DefaultDisplay(void)
        { current_display = MyApp->GetCmdShow(); }
    void Paint(void) {            // paints window
        PAINTSTRUCT ps;
        RECT         rect;
        BeginPaint(GetHandle(), &ps);
        EndPaint(GetHandle(), &ps);
    }
    void Update(void) // updates window
        { UpdateWindow(GetHandle()); }
    // vocab of functions for modifying the registration info
    void SetClassInstance(void)
        { MyWc->hInstance = MyApp->GetInstance(); }
    void SetClassName(LPSTR classname)
        { MyWc->lpszClassName = classname; }
    void SetClassStyle(unsigned newstyle) { MyWc->style = newstyle; }
    void AddClassStyle(unsigned addstyle) { MyWc->style |= addstyle; }
    void SetClassWinProc(long (FAR PASCAL *lpfnWndProc)(HWND, unsigned,
                    WORD, LONG))
        { MyWc->lpfnWndProc = lpfnWndProc; }
    void SetClassWinXbytes(int xtrabytes)
        { MyWc->cbWndExtra = xtrabytes; }
    void SetClassIcon(LPSTR iconname) {
        if(MyWc->hInstance)
            MyWc->hIcon = LoadIcon(MyWc->hInstance,iconname);
    }
    LPSTR GetClassName(void)           { return MyWc->lpszClassName; }
    // vocab of functions for modifying the create info
    void SetWinName(LPSTR winname) { WHdl.windowname = winname; }
    void SetWinStyle(DWORD dword)     { WHdl.winstyle = dword; }
    void AddWinStyle(DWORD dword)    { WHdl.winstyle |= dword; }
    void SetWinX(int x)                            { WHdl.upper_left_x = x; }
    void SetWinY(int y)                            { WHdl.upper_left_y = y; }
    void SetWinWidth(int width)              { WHdl.winwidth = width; }
    void SetWinHeight(int height)           { WHdl.winheight = height; }
    void SetWinInstance(HANDLE hinst) { WHdl.hInstance = hinst; }
};
inline void *GetPointer(HWND hWnd) {
    #if defined(__SMALL__) || defined(__MEDIUM__)
        return (void *)GetWindowWord(hWnd,0);
    #elif defined(__LARGE__) || defined(__COMPACT__)
        return (void *)GetWindowLong(hWnd,0); #else
    #error Must use Small, Medium, Large or Compact models!
    #endif
}
inline void SetPointer(HWND hWnd, void *ptr) {
    #if defined(__SMALL__) || defined(__MEDIUM__)
        SetWindowWord(hWnd,0,(WORD)ptr);
    #elif defined(__LARGE__) || defined(__COMPACT__)
        SetWindowLong(hWnd,0,(LONG)ptr);
    #else
    #error Must use Small, Medium, Large or Compact models!
    #endif
}
#define WINAPP_H
#endif
