#ifndef _Windows
# error Can't include this file without windows
#endif

#include "gr-draw.h"

#ifndef _WIN_GRAPHIC_DRAW_H
#define _WIN_GRAPHIC_DRAW_H

#include "winabout.h"
#include "keysym.h"
#include <windows.h>
#include <owl\window.h>
#include <owl\framewin.h>
#include <owl\dialog.h>
#include <owl\checkbox.h>
#include <owl\edit.h>
#include <owl\applicat.h>

#define main	OwlMain

class TWinGraphDraw : public TBaseGraphDraw<TWinGraphDraw>
{
  typedef TBaseGraphDraw<TWinGraphDraw> TGraphDraw;
public:
  TWinGraphDraw(const char *s = 0);
  ~TWinGraphDraw();
public:
  static const int CM_ABOUT;
  static unsigned long GetKeySym(unsigned int key);
protected:
  class TDrwWindow : public TWindow
  {
  public:
    TDrwWindow(TWinGraphDraw *drw) : TWindow(), win_draw(drw)
    {
      Init((TWindow*)NULL, 0, 0);
    }

    TColor GetBkgndColor() const {return BkgndColor;}
  protected:
    void SetupWindow();
    bool CanClose();
    void Paint(TDC &dc, bool erase, TRect &rect);
    void EvSize(uint sizeType, TSize &size) {Invalidate();}
    void EvLButtonDblClk(uint modKeys, TPoint& point) {EvLButtonDown(modKeys, point);}
    void EvLButtonDown(uint modKeys, TPoint& point);
    void EvLButtonUp(uint modKeys, TPoint& point);
    void EvRButtonDblClk(uint modKeys, TPoint& point) {EvRButtonDown(modKeys, point);}
    void EvRButtonDown(uint modKeys, TPoint& point);
    void EvRButtonUp(uint modKeys, TPoint& point);
    void EvMButtonDblClk(uint modKeys, TPoint& point) {EvMButtonDown(modKeys, point);}
    void EvMButtonDown(uint modKeys, TPoint& point);
    void EvMButtonUp(uint modKeys, TPoint& point);
    void EvMouseMove(uint modKeys, TPoint& point);
    void EvKeyDown(uint key, uint repeatCount, uint flags);
    void EvKeyUp(uint key, uint repeatCount, uint flags);
  public:
    TWinGraphDraw *win_draw;

    DECLARE_RESPONSE_TABLE(TDrwWindow);
  };

  class TDrwFrameWindow : public TFrameWindow
  {
  public:
    TDrwFrameWindow(TWindow *parent, TDrwWindow *client);
  protected:
    virtual void SetupWindow();
    void EvSysCommand(uint cmdType, TPoint& point);
  public:
    TWinGraphDraw *win_draw;

    DECLARE_RESPONSE_TABLE(TDrwFrameWindow);
  };

  class TDrwApplication : public TApplication
  {
  public:
    TDrwApplication(TWinGraphDraw *drw, int w = 0, int h = 0)
                  : def_w(w), def_h(h), win_draw(drw) {}

    void InitMainWindow()
    {
      win_draw->win = new TDrwWindow(win_draw);
      SetMainWindow(new TDrwFrameWindow(0, win_draw->win));
      GetMainWindow()->SetIcon(this, "WinDrawIcon");
      GetMainWindow()->SetIconSm(this, "WinDrawIcon");
      if (def_w > 0 && def_h > 0)
      {
        GetMainWindow()->Attr.W = def_w + 2*GetSystemMetrics(SM_CXFRAME);
        GetMainWindow()->Attr.H = def_h +
           GetSystemMetrics(SM_CYCAPTION) + 2*GetSystemMetrics(SM_CYFRAME);
      }
    }
  public:
    int def_w, def_h;
    TWinGraphDraw *win_draw;
  };

  friend class TDrwWindow;
  friend class TDrwApplication;
protected:
  TDrwApplication app;
  TDrwWindow *win;
  TDC *dc;
  int kind_dc;
  TColor bgcolor;
  int evmask, quit;
public:
  unsigned long GetBgColor();
  void SetBgColor(unsigned long c);
  void SetTitle(const char *s);

  int GetStatus() {return win ? 1 : 0;}
  int Init() {return 0;}
  int Run(int evmask = 0, int w = INT_MIN, int h = INT_MIN);

  void GetSize(int &w, int &h);
  int OpenDraw();
  int IsDraw() {return win && dc;}
  void CloseDraw();

  int SetColor(unsigned long c);
  int DrawLine(int x0, int y0, int x1, int y1);
  int DrawText(int x0, int y0, char *text);
  int DrawClear();
  int GetTextH(const char *s);
  int GetTextW(const char *s);
  void Quit(int q = 1);
};

const int TWinGraphDraw::CM_ABOUT = 7128;

unsigned long TWinGraphDraw::GetKeySym(uint key)
{
  switch(key)
  {
    case 37:  return XK_Left;
    case 39:  return XK_Right;
    case 38:  return XK_Up;
    case 40:  return XK_Down;
    case 13:  return XK_Return;
    case 32:  return XK_space;
    case 27:  return XK_Escape;
    case 188: return XK_comma;
    case 190: return XK_period;
    case 189: return XK_minus;
    case 187: return XK_equal;
    case 46:  return XK_Delete;
    case 191: return XK_slash;
    case 112: return XK_F1;
    case 113: return XK_F2;
    case 114: return XK_F3;
    case 115: return XK_F4;
    case 116: return XK_F5;
    case 117: return XK_F6;
    case 118: return XK_F7;
    case 119: return XK_F8;
    case 120: return XK_F9;
    case 121: return XK_F10;
    case 122: return XK_F11;
    case 123: return XK_F12;
    case 65:  return XK_a;
    case 66:  return XK_b;
    case 67:  return XK_c;
    case 68:  return XK_d;
    case 69:  return XK_e;
    case 70:  return XK_f;
    case 71:  return XK_g;
    case 72:  return XK_h;
    case 73:  return XK_i;
    case 74:  return XK_j;
    case 75:  return XK_k;
    case 76:  return XK_l;
    case 77:  return XK_m;
    case 78:  return XK_n;
    case 79:  return XK_o;
    case 80:  return XK_p;
    case 81:  return XK_q;
    case 82:  return XK_r;
    case 83:  return XK_s;
    case 84:  return XK_t;
    case 85:  return XK_u;
    case 86:  return XK_v;
    case 87:  return XK_w;
    case 88:  return XK_x;
    case 89:  return XK_y;
    case 90:  return XK_z;
    default:  return XK_VoidSymbol;
  }
}

TWinGraphDraw::TWinGraphDraw(const char *s)
             : TGraphDraw(s), app(this), dc(0), kind_dc(0)
{
  bgcolor = GetWhiteColor();
}

TWinGraphDraw::~TWinGraphDraw()
{
  CloseDraw();
  dc = 0; kind_dc = 0; win = 0;
}

unsigned long TWinGraphDraw::GetBgColor()
{
  if (win) bgcolor = win->GetBkgndColor();
  return bgcolor.GetValue();
}

void TWinGraphDraw::SetBgColor(unsigned long c)
{
  bgcolor.SetValue(c);
  if (win) win->SetBkgndColor(bgcolor);
}

void TWinGraphDraw::SetTitle(const char *s)
{
  TGraphDraw::SetTitle(s);
  if (win) win->SetCaption(s);
}

int TWinGraphDraw::Run(int evmask, int w, int h)
{
  if (!evfunc) return -2;
  this->evmask = evmask;
  app.def_w = w; app.def_h = h;
  quit = 0;
  app.Run();
  return quit;
}

void TWinGraphDraw::GetSize(int &w, int &h)
{
  if (win)
  {
    TRect rect;
    win->GetWindowRect(rect);
    w = rect.Width(); h = rect.Height();
  }
  else TGraphDraw::GetSize(w, h);
}

int TWinGraphDraw::OpenDraw()
{
  if (!win) return 0;
  if (!dc) {dc = new TWindowDC(*win); kind_dc = 1;}
  return 1;
}

void TWinGraphDraw::CloseDraw()
{
  if (dc && kind_dc == 1) delete dc;
  if (kind_dc <= 2) {dc = 0; kind_dc = 0;}
}

int TWinGraphDraw::SetColor(unsigned long c)
{
  if (!win || !dc) return 0;
  TColor color(c);
  dc->SelectObject((TPen)color);
  dc->SetTextColor(color);
  return 1;
}

int TWinGraphDraw::DrawLine(int x0, int y0, int x1, int y1)
{
  if (!win || !dc) return 0;
  dc->MoveTo(x0, y0);
  dc->LineTo(x1, y1);
  return 1;
}

int TWinGraphDraw::DrawText(int x0, int y0, char *text)
{
  if (!win || !dc) return 0;
  dc->TextOut(x0, y0, text);
  return 1;
}

int TWinGraphDraw::DrawClear()
{
  if (!win || !dc) return 0;
  dc->FillRect(TRect(TPoint(0, 0), win->GetWindowRect().Size()), bgcolor);
  return 1;
}

int TWinGraphDraw::GetTextH(const char *s)
{
  if (win)
  {
    TFont fnt = win->GetWindowFont();
    return fnt.GetHeight();
  }
  else return TGraphDraw::GetTextH(s);
}

int TWinGraphDraw::GetTextW(const char *s)
{
  if (win)
  {
    TFont fnt = win->GetWindowFont();
    return fnt.GetMaxWidth() * strlen(s);
  }
  else return TGraphDraw::GetTextW(s);
}

void TWinGraphDraw::Quit(int q)
{
  quit = q;
  if (quit <= 0) quit = 0;
  else if (win) win->PostMessage(WM_CLOSE);
}

DEFINE_RESPONSE_TABLE1(TWinGraphDraw::TDrwFrameWindow, TFrameWindow)
  EV_WM_SYSCOMMAND,
END_RESPONSE_TABLE;

TWinGraphDraw::TDrwFrameWindow::TDrwFrameWindow(TWindow *parent, TDrwWindow *client)
             : TFrameWindow(parent, client->win_draw->GetTitle(), client)
{
  win_draw = client->win_draw;
}

void TWinGraphDraw::TDrwFrameWindow::SetupWindow()
{
  TFrameWindow::SetupWindow();
  if (win_draw->GetAboutInfo() > 0)
  {
    TMenu menu = GetSystemMenu(false);
    menu.AppendMenu(MF_SEPARATOR);
    menu.AppendMenu(MF_STRING, CM_ABOUT, "&About");
  }
}

void TWinGraphDraw::TDrwFrameWindow::EvSysCommand(uint cmdType, TPoint& point)
{
  if (cmdType == CM_ABOUT)
  {
    char dia_name[50] = "IDD_ABOUT";
    int dia_num = win_draw->GetAboutInfo();
    if (dia_num > 0)
    {
      itoa(dia_num, dia_name + strlen(dia_name), 10);
      TAboutDialog(this, dia_name).Execute();
    }
  }
  else TFrameWindow::EvSysCommand(cmdType, point);
}

DEFINE_RESPONSE_TABLE1(TWinGraphDraw::TDrwWindow, TWindow)
  EV_WM_MOUSEMOVE,
  EV_WM_LBUTTONDBLCLK,
  EV_WM_LBUTTONDOWN,
  EV_WM_LBUTTONUP,
  EV_WM_RBUTTONDBLCLK,
  EV_WM_RBUTTONDOWN,
  EV_WM_RBUTTONUP,
  EV_WM_MBUTTONDBLCLK,
  EV_WM_MBUTTONDOWN,
  EV_WM_MBUTTONUP,
  EV_WM_SIZE,
  EV_WM_KEYDOWN,
  EV_WM_KEYUP,
END_RESPONSE_TABLE;

void TWinGraphDraw::TDrwWindow::SetupWindow()
{
  TWindow::SetupWindow();
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0) return;
  event ev;
  ev.type = event::start;
  ev.any.drw = win_draw;
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

bool TWinGraphDraw::TDrwWindow::CanClose()
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 1) return true;
  event ev;
  ev.type = event::close;
  ev.any.drw = win_draw;
  win_draw->quit = win_draw->evfunc(ev);
  if (win_draw->quit <= 0) win_draw->quit = 0;
  win_draw->CloseDraw();
  return win_draw->quit != 0;
}

void TWinGraphDraw::TDrwWindow::Paint(TDC &dc, bool, TRect&)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0) return;
  win_draw->CloseDraw();
  win_draw->dc = &dc; win_draw->kind_dc = 2;
  event ev;
  ev.type = event::draw;
  ev.any.drw = win_draw;
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvLButtonDown(uint /*modKeys*/, TPoint& point)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & button_down_mask)) return;
  event ev;
  ev.type = event::button_down;
  ev.button.drw = win_draw;
  ev.button.n = 1;
  ev.button.x = point.x;
  ev.button.y = point.y;
  int r = win_draw->evfunc(ev);
  if (r >= 0 && (r & ret_setcapture)) SetCapture();
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvLButtonUp(uint /*modKeys*/, TPoint& point)
{
  ReleaseCapture();
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & button_up_mask)) return;
  event ev;
  ev.type = event::button_up;
  ev.button.drw = win_draw;
  ev.button.n = 1;
  ev.button.x = point.x;
  ev.button.y = point.y;
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvRButtonDown(uint /*modKeys*/, TPoint& point)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & button_down_mask)) return;
  event ev;
  ev.type = event::button_down;
  ev.button.drw = win_draw;
  ev.button.n = 2;
  ev.button.x = point.x;
  ev.button.y = point.y;
  int r = win_draw->evfunc(ev);
  if (r >= 0 && (r & ret_setcapture)) SetCapture();
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvRButtonUp(uint /*modKeys*/, TPoint& point)
{
  ReleaseCapture();
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & button_up_mask)) return;
  event ev;
  ev.type = event::button_up;
  ev.button.drw = win_draw;
  ev.button.n = 2;
  ev.button.x = point.x;
  ev.button.y = point.y;
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvMButtonDown(uint /*modKeys*/, TPoint& point)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & button_down_mask)) return;
  event ev;
  ev.type = event::button_down;
  ev.button.drw = win_draw;
  ev.button.n = 3;
  ev.button.x = point.x;
  ev.button.y = point.y;
  int r = win_draw->evfunc(ev);
  if (r >= 0 && (r & ret_setcapture)) SetCapture();
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvMButtonUp(uint /*modKeys*/, TPoint& point)
{
  ReleaseCapture();
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & button_up_mask)) return;
  event ev;
  ev.type = event::button_up;
  ev.button.drw = win_draw;
  ev.button.n = 3;
  ev.button.x = point.x;
  ev.button.y = point.y;
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvMouseMove(uint /*modKeys*/, TPoint& point)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & (mouse_move_mask | mouse_drag_mask))) return;
  event ev;
  ev.type = event::mouse_move;
  ev.button.drw = win_draw;
  ev.button.n = -1;
  ev.button.x = point.x;
  ev.button.y = point.y;
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvKeyDown(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & key_down_mask)) return;
  event ev;
  ev.type = event::key_down;
  ev.key.drw = win_draw;
  ev.key.k = GetKeySym(key);
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

void TWinGraphDraw::TDrwWindow::EvKeyUp(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  if (!win_draw || !win_draw->evfunc || win_draw->quit > 0 ||
      !(win_draw->evmask & key_up_mask)) return;
  event ev;
  ev.type = event::key_up;
  ev.key.drw = win_draw;
  ev.key.k = GetKeySym(key);
  win_draw->evfunc(ev);
  win_draw->CloseDraw();
}

#endif  //_WIN_GRAPHIC_DRAW_H