#include "wxinc.h"

#define XTWIN_CPP 1
#include "xvt.h"
#include "xvtart.h"
#include "xvtwin.h"

#include <wx/artprov.h>
#include <wx/aui/aui.h>
//#include <wx/dcbuffer.h>
#include <wx/process.h>
#include <wx/taskbar.h>
#include <wx/treectrl.h>
#include <wx/vlbox.h>

///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////

wxHashTable   _nice_windows;
wxFrame*      _task_win = NULL;
EVENT_HANDLER _task_win_handler = NULL;

wxRect RCT2Rect(const RCT* prct)
{
	wxRect rct;
	if (prct != NULL)
  {
	  rct.x = min(prct->left, prct->right);
	  rct.y = min(prct->top, prct->bottom);
	  rct.width = abs(prct->right - prct->left);
	  rct.height = abs(prct->bottom - prct->top);
  }
	return rct;
}

void Rect2RCT(const wxRect& r, RCT* rct)
{ xvt_rect_set(rct, r.x, r.y, r.GetRight()+1, r.GetBottom()+1); }

///////////////////////////////////////////////////////////
// Caret emulation
///////////////////////////////////////////////////////////

class TwxCaret : private wxTimer
{
	WINDOW _owner;
  PNT _pos;
	wxSize _size;
  bool _visible;
	bool _drawn;

protected:
	void Toggle();
  virtual void Notify() { Toggle(); }

public:
	void SetPos(int x, int y);
	void SetSize(int x, int y) { _size.x = x; _size.y = y; }
	void Show(WINDOW w, bool on = true);
	void Hide() { Show(NULL_WIN, false); }
	bool IsVisible() const { return _visible; }
	WINDOW Owner() const { return _owner; }
	void Kill();

	TwxCaret() :  _owner(NULL_WIN), _visible(false) { }
	virtual ~TwxCaret() { Kill(); }
} _TheCaret;

void TwxCaret::Kill()
{ _owner = NULL_WIN; }

void TwxCaret::SetPos(int x, int y) 
{ 
  if (_visible && _drawn) // Lo cancella se necessario
	  Toggle();  
  _pos.h = x; _pos.v = y; 
}

void TwxCaret::Show(WINDOW w, bool on)
{
  if (_visible && _drawn)
		Toggle();  // Lo cancella

	_visible = on;
	if (on)
	{
		_owner = w;
		Toggle();
		wxTimer::Start(500); // Lampeggia ogni mezzo secondo
	}
	else
	{
    if (w == _owner || w == NULL_WIN)
			Kill();
	}
}

void TwxCaret::Toggle()
{
	if (!_visible || _owner == NULL_WIN)
		return;
		
	_drawn = !_drawn;

	DRAW_CTOOLS dct;
	xvt_dwin_get_draw_ctools(_owner, &dct);

	CPEN pen;
	pen.width = _size.x;
	pen.pat = PAT_SOLID;
	pen.style = P_SOLID;
  pen.color = dct.fore_color;
	xvt_dwin_set_draw_mode(_owner, M_NOT_XOR);
	
  xvt_dwin_set_cpen(_owner, &pen);
	xvt_dwin_draw_set_pos(_owner, _pos);
	PNT p = _pos;	p.v -= _size.y-1;

	xvt_dwin_set_clip(_owner, NULL);  // Non si sa mai!
	xvt_dwin_draw_line(_owner, p);
	xvt_dwin_set_draw_ctools(_owner, &dct);
}

void xvt_win_set_caret_size(WINDOW win, int width, int height)
{
  if (win != NULL_WIN)
	  _TheCaret.SetSize(width, height);
}

void xvt_win_set_caret_pos(WINDOW win, PNT p)
{
  if (win != NULL_WIN)
	  _TheCaret.SetPos(p.h, p.v-1);
}

void xvt_win_set_caret_visible(WINDOW win, BOOLEAN on)
{
	_TheCaret.Show(win, on != 0);
}

///////////////////////////////////////////////////////////
// Generic Display context
///////////////////////////////////////////////////////////

TDC::TDC(wxWindow* owner) : _dc(NULL)
{
  _owner = owner;

	memset(&_dct, 0, sizeof(_dct));
	_dct.pen.width = 0;
	_dct.pen.pat = PAT_SOLID;
  _dct.pen.style = P_SOLID;
  _dct.pen.color = COLOR_BLACK;
	_dct.brush.pat = PAT_HOLLOW;
  _dct.brush.color = COLOR_WHITE;
	_dct.mode = M_COPY;
	_dct.fore_color = COLOR_BLACK;
	_dct.back_color = COLOR_WHITE;
	_dct.opaque_text = FALSE;

  _font.SetPointSize(9);  // Default font
  _deltaf = 0;

  // Reset clip area
  SetClippingBox(NULL);
  _real_clip = _clip;

	_dirty = -1;  // Absolutely force setting
}

TDC::~TDC()
{
	KillDC();
}

void TDC::SetDirty(int d)
{
	if (_dirty >= 0)
		_dirty = d;
}

static int PatternToStyle(PAT_STYLE pat)
{
	int style = wxSOLID;
	switch (pat)
	{
  case PAT_NONE:
  case PAT_HOLLOW:    style = wxTRANSPARENT; break;
  case PAT_SOLID:	    style = wxSOLID; break;
  case PAT_HORZ:      style = wxHORIZONTAL_HATCH; break;
  case PAT_VERT:      style = wxVERTICAL_HATCH; break;
  case PAT_FDIAG:	    style = wxFDIAGONAL_HATCH; break;
  case PAT_BDIAG:     style = wxBDIAGONAL_HATCH; break;
  case PAT_CROSS:     style = wxCROSS_HATCH; break;
  case PAT_DIAGCROSS: style = wxCROSSDIAG_HATCH; break;
  case PAT_SPECIAL:		style = wxSOLID; break; //  Used for gradient
  case PAT_RUBBER:	
  default: style = wxSOLID; SORRY_BOX(); break;
	}
	return style;
}

static int PenStyleToStyle(PEN_STYLE s, PAT_STYLE p)
{
	int style = wxSOLID;
	if (p != PAT_HOLLOW)
	{
		switch (s)
		{
		case P_DOT : style = wxDOT;        break;
		case P_DASH: style = wxSHORT_DASH; break;
		default: break;
		}
	}
	else
		style = wxTRANSPARENT;

	return style;
}

bool TDC::PenChanged() const
{
	const int diff = memcmp(&_dct.pen, &_real_dct.pen, sizeof(_dct.pen));
  return diff != 0;
}

bool TDC::BrushChanged() const
{
	const int diff = memcmp(&_dct.brush, &_real_dct.brush, sizeof(_dct.brush));
  return diff != 0;
}

bool TDC::FontChanged() const
{
  return _font != _real_font;
}

bool TDC::ClipChanged() const
{
	const int diff = memcmp(&_clip, &_real_clip, sizeof(_clip));
  return diff != 0;
}

#ifdef LINUX
bool is_printer_dc(wxDC * dc)
{
	return wxDynamicCast(dc, wxPostScriptDC) != NULL;
}
#endif

wxDC& TDC::GetDC(bool bPaint)
{
  if (bPaint)
	{
		KillDC();
    //_dc = new wxAutoBufferedPaintDC(_owner); // Funziona ma si vedono cose strane temporanee
	  _dc = new wxPaintDC(_owner);
  	_dirty = -1;
	}
	else
	{
		if (_dc == NULL)
		{
      if (_owner == NULL || (unsigned long)_owner == SCREEN_WIN)
        _dc = new wxScreenDC();
      else  
		    _dc = new wxClientDC(_owner);
    	_dirty = -1;
		}
	}

	if (_dirty)
	{
		if (_dirty < 0 || PenChanged())
		{
		  CAST_COLOR(_dct.pen.color, pen_color);
  		wxPen* pen = wxThePenList->FindOrCreatePen(pen_color, _dct.pen.width, PenStyleToStyle(_dct.pen.style, _dct.pen.pat));
	  	_dc->SetPen(*pen);
			_real_dct.pen = _dct.pen;
		}

		if (_dirty < 0 || BrushChanged())
		{
 		  CAST_COLOR(_dct.brush.color, brush_color);
		  wxBrush* brush = wxTheBrushList->FindOrCreateBrush(brush_color, PatternToStyle(_dct.brush.pat));
		  _dc->SetBrush(*brush);
			_real_dct.brush = _dct.brush;
		}

		if (_dirty < 0 || _dct.mode != _real_dct.mode)
		{
#ifdef LINUX
			if(!is_printer_dc(_dc))
			{ 
#endif
			switch(_dct.mode)
			{
			case M_COPY:     _dc->SetLogicalFunction(wxCOPY); break;
			case M_OR:       _dc->SetLogicalFunction(wxOR); break;
			case M_XOR:      _dc->SetLogicalFunction(wxXOR); break;
			case M_CLEAR:    _dc->SetLogicalFunction(wxCLEAR); break;
			case M_NOT_COPY: _dc->SetLogicalFunction(wxSRC_INVERT); break;
			case M_NOT_OR:   _dc->SetLogicalFunction(wxNOR); break;
			case M_NOT_XOR:  _dc->SetLogicalFunction(wxEQUIV); break;
			case M_NOT_CLEAR:_dc->SetLogicalFunction(wxSET); break;
			default: SORRY_BOX();
			}
#ifdef LINUX
			}
#endif
			_real_dct.mode = _dct.mode;
		}

		if (_dirty < 0 || _dct.fore_color != _real_dct.fore_color)
		{
 		  CAST_COLOR(_dct.fore_color, fore_color);
		  _dc->SetTextForeground(fore_color);
			_real_dct.fore_color = _dct.fore_color;
		}

		if (_dirty < 0 || _dct.back_color != _real_dct.back_color)
		{
 		  CAST_COLOR(_dct.back_color, back_color);
		  _dc->SetTextBackground(back_color);
			_real_dct.back_color = _dct.back_color;
		}

		if (_dirty < 0 || _dct.opaque_text != _real_dct.opaque_text)
		{
		  _dc->SetBackgroundMode(_dct.opaque_text ? wxSOLID : wxTRANSPARENT);
			_real_dct.opaque_text = _dct.opaque_text;
		}

		if (_dirty < 0 || FontChanged())
		{
      const wxFont& f = _font.Font(_dc, (WINDOW)_owner);
 		  _dc->SetFont(f);
			_real_font = _font;

      int height, desc, lead;
      _dc->GetTextExtent("Kpfx", NULL, &height, &desc, &lead);
		  _deltaf = height-desc; // Baseline offset from top
		}

    if (_dirty < 0 || ClipChanged())
    {
      _dc->DestroyClippingRegion();
      if (_clip.bottom < 4096)
        _dc->SetClippingRegion(RCT2Rect(&_clip));
      _real_clip = _clip;
    }
		
		_dirty = false;
	}
	return *_dc;
}

void TDC::KillDC()
{
	if (_dc != NULL)
	{
  	SetClippingBox(NULL);
    _real_clip = _clip;
		delete _dc;
  	_dc = NULL;
	}
}

void TDC::SetClippingBox(const RCT* pRct)
{
	if (pRct != NULL)
	{
    // Normalizza posizione e dimensioni invece di limitarsi a fare _clip=*pRct
		const wxRect rct = RCT2Rect(pRct); 
    Rect2RCT(rct, &_clip);
	}
	else
	{
		_clip.left = _clip.top = 0;
		_clip.right = _clip.bottom = 32000;  // 32000 serve per i TDC di stampa (aumentare quando possibile) per il video basterebbe 4096
	}
}

bool TDC::GetClippingBox(RCT* pRct) const
{
	if (pRct != NULL) 
    *pRct = _clip;
	return _clip.right > _clip.left;
}

TDCMapper& GetTDCMapper()
{
  static TDCMapper* _dc_map = NULL;
  if (_dc_map == NULL)
    _dc_map = new TDCMapper;
  return *_dc_map;
}

void TDCMapper::DestroyDC(WINDOW owner)
{
	if (owner)
	{
	  TDC* pTDC = (*this)[owner];
	  if (pTDC)
		  pTDC->KillDC();
	}
	else
	{
		for (TDCMapper::iterator it = begin(); it != end(); ++it)
		{
  	  TDC* pTDC = it->second;
	  	if (pTDC)
   		  pTDC->KillDC();
		}
  }
}

void TDCMapper::DestroyTDC(WINDOW owner)
{
	if (owner != NULL_WIN)
	{
	  TDC* pTDC = (*this)[owner];
	  if (pTDC)
		  delete pTDC;
    erase(owner);
	}
	else
	{
		TDCMapper::iterator it;
		for (it = begin(); it != end(); ++it)
		{
  	  TDC* pTDC = it->second;
	  	if (pTDC)
				delete pTDC;
		}
		clear();
	}
	_pLastOwner = NULL_WIN;
}

TDC& TDCMapper::GetTDC(WINDOW owner)
{
	if (owner != _pLastOwner)
  {
    wxASSERT(owner != NULL_WIN); 
	  TDC* pTDC = (*this)[owner];
	  if (pTDC == NULL)
	  {
		  if (owner == PRINTER_WIN)
			  pTDC = new TPrintDC((wxWindow*)owner);
		  else
		    pTDC = new TDC((wxWindow*)owner);
		  (*this)[owner] = pTDC;
	  }
	  _pLastOwner = owner;
	  _pLastTDC = pTDC;
  }
	return *_pLastTDC;
}

bool TDCMapper::HasValidDC(WINDOW owner) const
{
  if (owner == NULL_WIN)
		return false;

	if (owner == (WINDOW)_pLastOwner)
		return true;
	
	TDC* pTDC = (*((TDCMapper *) this))[owner];
	return pTDC != NULL;
}

///////////////////////////////////////////////////////////
// Generic window class
///////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS(TwxWindowBase, wxWindow)

#ifdef WIN32
WXLRESULT TwxWindowBase::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
  WXLRESULT rc = 0;
  bool processed = false;

  switch (nMsg)
  {
  case WM_CLOSE: 
    processed = !Close(); 
    break;
  default: 
    break;
  }

  if ( !processed )
    rc = wxWindow::MSWWindowProc(nMsg, wParam, lParam);

  return rc;
}
#endif

bool TwxWindowBase::CreateBase(wxWindow *parent, wxWindowID id, const wxString &title,
		    								       const wxPoint &pos, const wxSize &size, long style)
{
  wxWindowBase::Show(false);  // Evita inutili sfarfallamenti
  return Create(parent, id, pos, size, style, title);
}

TwxWindowBase::TwxWindowBase(wxWindow *parent, wxWindowID id, const wxString &title,
													   const wxPoint &pos, const wxSize &size, long style)
{
  CreateBase(parent, id, title, pos, size, style);
}

IMPLEMENT_DYNAMIC_CLASS(TwxWindow, TwxWindowBase)

BEGIN_EVENT_TABLE(TwxWindow, TwxWindowBase)
  EVT_CHAR(TwxWindow::OnChar)
  EVT_KEY_DOWN(TwxWindow::OnKeyDown)
	EVT_CLOSE(TwxWindow::OnClose)
	EVT_KILL_FOCUS(TwxWindow::OnKillFocus)
  EVT_LEFT_DCLICK(TwxWindow::OnMouseDouble)
  EVT_LEFT_DOWN(TwxWindow::OnMouseDown)
  EVT_LEFT_UP(TwxWindow::OnMouseUp)
  EVT_MENU_RANGE(1000, 32766, TwxWindow::OnMenu)
  EVT_MIDDLE_DOWN(TwxWindow::OnMouseDown)
  EVT_MIDDLE_UP(TwxWindow::OnMouseUp)
	EVT_MOTION(TwxWindow::OnMouseMove)
  EVT_MOUSE_CAPTURE_LOST(TwxWindow::OnMouseCaptureLost)
  EVT_MOUSEWHEEL(TwxWindow::OnMouseWheel)
  EVT_PAINT(TwxWindow::OnPaint)
  EVT_RIGHT_DOWN(TwxWindow::OnMouseDown)
  EVT_RIGHT_UP(TwxWindow::OnMouseUp)
	EVT_SCROLL(TwxWindow::OnScroll)
	EVT_SCROLLWIN(TwxWindow::OnScrollWin)
	EVT_SET_FOCUS(TwxWindow::OnSetFocus)
	EVT_SIZE(TwxWindow::OnSize)
	EVT_TIMER(TIMER_ID, TwxWindow::OnTimer)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, TwxWindow::OnButton)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, TwxWindow::OnCheckBox)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, TwxWindow::OnRadioButton)
END_EVENT_TABLE()

long TwxWindow::DoXvtEvent(EVENT& e)
{
  long ret = 0;
	if (this != NULL && _eh != NULL)
  	ret = _eh((WINDOW)this, &e);
  return ret;
}

void TwxWindow::OnChar(wxKeyEvent& evt)
{
  static int nSkipNextDotKey = -883; // Valore indefinito
  if (nSkipNextDotKey == -883)       // Devo stabilire se attivare la gestione o no
  {
    const char* campoini = xvt_fsys_get_campo_ini();
    char str[2];
    xvt_sys_get_profile_string(campoini, "Main", "Point2Comma", "1", str, sizeof(str));
    nSkipNextDotKey = strchr("1XY", *str) != NULL ? 0 : -1; // Dis/Abilita conversione punto in virgola
  }

  XVT_EVENT e(E_CHAR);
	int k = evt.GetKeyCode();

	if (nSkipNextDotKey == 1)
	{
    nSkipNextDotKey = 0;
		if (k == '.')
		{
		  evt.Skip();
		  return;
		}
	}

	switch (k)
	{
	case WXK_ALT:
	case WXK_MENU:
  case WXK_NUMPAD0:
  case WXK_NUMPAD1:
  case WXK_NUMPAD2:
  case WXK_NUMPAD3:
  case WXK_NUMPAD4:
  case WXK_NUMPAD5:
  case WXK_NUMPAD6:
  case WXK_NUMPAD7:
  case WXK_NUMPAD8:
  case WXK_NUMPAD9:
		evt.Skip();
		return;
  case WXK_NUMPAD_DECIMAL:   // Arriva solo dalla 2.6.3 in poi
	case WXK_DECIMAL:          // ??? Arriva sia '.' sia WXK_DECIMAL=340
    if (nSkipNextDotKey == 0)
    {
		  k = ','; // Trasformo il punto in virgola
		  nSkipNextDotKey = 1;
    }
		break;
  case WXK_NUMPAD_ADD: k = '+';break;
	case WXK_DOWN : k = K_DOWN;  break;
  case WXK_END  : k = K_LEND;  break;
  case WXK_HOME : k = K_LHOME; break;
	case WXK_LEFT : k = K_LEFT;  break;
	case WXK_NEXT : k = K_NEXT;  break;
	case WXK_PRIOR: k = K_PREV;  break;
	case WXK_RIGHT: k = K_RIGHT; break;
	case WXK_UP   : k = K_UP;	   break;
	case WXK_TAB:
    if (evt.ShiftDown())
			k = K_BTAB;
		break;
	default:
	  if (k >= WXK_F1 && k <= WXK_F24)
		  k = K_F1 + k - WXK_F1;
		break;
	}
	
	e.v.chr.shift = evt.ShiftDown();
	e.v.chr.control = evt.ControlDown();
	if (evt.AltDown())
	{
		e.v.chr.control = TRUE;
    if (xvt_chr_is_alnum(k))
			k = toupper(k);
		else
		{
      if (strchr("+-", k) == NULL) // Aggiungere qui vari testi eventuali
      {
			  evt.Skip();
			  return;
      }
		}
	}
	e.v.chr.ch = k;
	
	DoXvtEvent(e);
}

void TwxWindow::OnKeyDown(wxKeyEvent& e)
{
#ifdef WIN32
  // Triste necessita' per gestire corretamente Alt+'+' del tasterino
	const int k = e.GetKeyCode();
  if (k == WXK_NUMPAD_ADD)
  {
    if (e.AltDown())
    {
      OnChar(e);
      return;
    }
  }
#else
  if (e.AltDown() || e.ControlDown())
  {
    OnChar(event);
    return;
  }
#endif
  e.Skip();
}

void TwxWindow::OnClose(wxCloseEvent& WXUNUSED(e))
{
  XVT_EVENT e(E_CLOSE);
	DoXvtEvent(e);
}

void TwxWindow::OnKillFocus(wxFocusEvent& WXUNUSED(e))
{
  if (_TheCaret.Owner() == (WINDOW)this)
    _TheCaret.Hide();

  XVT_EVENT e(E_FOCUS);
	e.v.active = 0;
	DoXvtEvent(e);
}

void TwxWindow::OnMenu(wxCommandEvent& evt)
{
  XVT_EVENT e(E_COMMAND);
	e.v.cmd.control = 0; e.v.cmd.shift = 0;
	e.v.cmd.tag = evt.GetId();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(e))
{
  xvt_win_release_pointer();
}

void TwxWindow::OnMouseDouble(wxMouseEvent& evt)
{
  XVT_EVENT e(E_MOUSE_DBL);
	e.v.mouse.button = (evt.RightDown() ? 1 : 0) + (evt.MiddleDown() ? 2 : 0);
	e.v.mouse.control = evt.ControlDown();
	e.v.mouse.shift = evt.ShiftDown();
	e.v.mouse.where.h = evt.GetX();
	e.v.mouse.where.v = evt.GetY();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseDown(wxMouseEvent& evt)
{
  XVT_EVENT e(E_MOUSE_DOWN);
  e.v.mouse.button = (evt.RightDown() ? 1 : 0) + (evt.MiddleDown() ? 2 : 0);
  e.v.mouse.control = evt.ControlDown();
  e.v.mouse.shift = evt.ShiftDown();
  e.v.mouse.where.h = evt.GetX();
  e.v.mouse.where.v = evt.GetY();
  DoXvtEvent(e);
  SetFocus();   // Triste necessita'
}

void TwxWindow::OnMouseMove(wxMouseEvent& evt)
{
  XVT_EVENT e(E_MOUSE_MOVE);
	e.v.mouse.button = (evt.RightIsDown() ? 1 : 0) + (evt.MiddleIsDown() ? 2 : 0);
	e.v.mouse.control = evt.ControlDown();
	e.v.mouse.shift = evt.m_shiftDown;
	e.v.mouse.where.h = evt.GetX();
	e.v.mouse.where.v = evt.GetY();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseUp(wxMouseEvent& evt)
{
  XVT_EVENT e(E_MOUSE_UP);
	e.v.mouse.button = (evt.RightUp() ? 1 : 0) + (evt.MiddleUp() ? 2 : 0);
	e.v.mouse.control = evt.ControlDown();
	e.v.mouse.shift = evt.ShiftDown();
	e.v.mouse.where.h = evt.GetX();
	e.v.mouse.where.v = evt.GetY();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseWheel(wxMouseEvent& evt)
{
  const int nRot = evt.GetWheelRotation();
  if (nRot != 0)
  {
    XVT_EVENT e(E_VSCROLL);
	  e.v.scroll.pos = evt.GetY();
    e.v.scroll.what = nRot > 0 ? SC_LINE_UP : SC_LINE_DOWN;
	  DoXvtEvent(e);
  }
}

void TwxWindow::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
  const wxRect rctDamaged = GetUpdateRegion().GetBox();

	XVT_EVENT e(E_UPDATE);
  Rect2RCT(rctDamaged, &e.v.update.rct);

  TDC& tdc = GetTDCMapper().GetTDC((WINDOW)this);
  tdc.GetDC(true); // Forza la creazione di un wxPaintDC
  DoXvtEvent(e);
  tdc.KillDC();    // Distrugge il wxPaintDC
  GetTDCMapper().DestroyDC(NULL_WIN); // Distrugge davvero tutti i wxClientDC residui (risolve molte "porcate" del video)
}

static SCROLL_CONTROL ConvertScrollToXVT(wxEventType et)
{
  if (et == wxEVT_SCROLL_TOP)
		return SC_THUMB; // Meglio di niente
  if (et == wxEVT_SCROLL_BOTTOM)
		return SC_THUMB; // Meglio di niente
  if (et == wxEVT_SCROLL_LINEUP)
		return SC_LINE_UP;
  if (et == wxEVT_SCROLL_LINEDOWN)
		return SC_LINE_DOWN;
  if (et == wxEVT_SCROLL_PAGEUP)
		return SC_PAGE_UP;
  if (et == wxEVT_SCROLL_PAGEDOWN)
		return SC_PAGE_DOWN;
  if (et == wxEVT_SCROLL_THUMBTRACK)
		return SC_THUMBTRACK;
  if (et == wxEVT_SCROLL_THUMBRELEASE)
		return SC_THUMB;
	return SC_NONE;
}

void TwxWindow::OnScroll(wxScrollEvent& evt)
{
  SCROLL_CONTROL sc = ConvertScrollToXVT(evt.GetEventType());
  if (sc != SC_NONE)
  {
    XVT_EVENT e(E_CONTROL);
	  e.v.ctl.id = evt.GetId();
	  e.v.ctl.ci.type = evt.GetOrientation()==wxHORIZONTAL ? WC_HSCROLL : WC_VSCROLL;
	  e.v.ctl.ci.win = (WINDOW)evt.GetEventObject();
	  e.v.ctl.ci.v.scroll.pos = evt.GetPosition();
    e.v.ctl.ci.v.scroll.what = sc;
	  DoXvtEvent(e);
  }
}

void TwxWindow::OnScrollWin(wxScrollWinEvent& evt)
{
	wxEventType et = evt.GetEventType();
  et -= (wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP);
  const SCROLL_CONTROL sc = ConvertScrollToXVT(et);
  if (sc != SC_NONE)
  {
    XVT_EVENT e(evt.GetOrientation() == wxHORIZONTAL ? E_HSCROLL : E_VSCROLL);
	  e.v.scroll.pos = evt.GetPosition();
	  e.v.scroll.what = sc;
	  DoXvtEvent(e);
  }
}

void TwxWindow::OnSetFocus(wxFocusEvent& WXUNUSED(e))
{
  XVT_EVENT e(E_FOCUS);
	e.v.active = TRUE;
	DoXvtEvent(e);
}

void TwxWindow::OnSize(wxSizeEvent& evt)
{
  XVT_EVENT e(E_SIZE);
	e.v.size.width = evt.GetSize().x;
	e.v.size.height = evt.GetSize().y;
	DoXvtEvent(e);
}

void TwxWindow::OnTimer(wxTimerEvent& WXUNUSED(evt))
{
  XVT_EVENT e(E_TIMER);
	e.v.timer.id = (WINDOW)this;
	DoXvtEvent(e);
}

void TwxWindow::OnButton(wxCommandEvent& evt)
{
  XVT_EVENT e(E_CONTROL);
	e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_PUSHBUTTON;
	DoXvtEvent(e);
}

void TwxWindow::OnCheckBox(wxCommandEvent& evt)
{
  XVT_EVENT e(E_CONTROL);
	e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_CHECKBOX;
	DoXvtEvent(e);
}

void TwxWindow::OnRadioButton(wxCommandEvent& evt)
{
  XVT_EVENT e(E_CONTROL);
	e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_RADIOBUTTON;
	DoXvtEvent(e);
}

void TwxWindow::SetMenuTree(const MENU_ITEM* tree)
{
	wxASSERT(tree != NULL);
	if (tree != NULL)
	{
		if (m_menu)
			xvt_res_free_menu_tree(m_menu);
		m_menu = xvt_menu_duplicate_tree(tree);
    TTaskWin* tw = wxStaticCast(_task_win, TTaskWin);
		tw->PushMenuTree(tree, this);
	}
}

BOOLEAN TwxWindow::AddPane(wxWindow* wnd, const char* caption, int nDock, int nFlags)
{
  BOOLEAN ok = wnd != NULL;
  if (ok)
  {
    if (m_pManager == NULL)
      m_pManager = new wxAuiManager(this);
    wxAuiPaneInfo pane;
    pane.DefaultPane(); pane.Dockable(false); 
    const wxSize sz = wnd->GetSize();
    switch (nDock)
    {
    case  1: // Left
      pane.Left().Floatable(true).LeftDockable().RightDockable();
      pane.MinSize(sz.x/2, -1).BestSize(sz.x, -1).MaxSize(3*sz.x/2, -1); 
      break;
    case  2: // Top
      pane.Top().Floatable(true).TopDockable().BottomDockable().MinSize(-1, sz.y/2); 
      break;
    case  3: // Right
      pane.Right().Floatable(true).LeftDockable().RightDockable();
      pane.MinSize(sz.x/2, -1).BestSize(sz.x, -1).MaxSize(3*sz.x/2, -1); 
      break;
    case  4: // Bottom
      pane.Bottom().Floatable(true).TopDockable().BottomDockable().MinSize(-1, sz.y/2); 
      break;
    case 52: // Center Top
      pane.CentrePane().CaptionVisible(true).TopDockable(); 
      break;
    case 54: // Center Bottom
      pane.CentrePane().CaptionVisible(true).BottomDockable(); 
      break;
    case 62: // Top toolbar
      pane.ToolbarPane().Top().MinSize(wxSize(-1,sz.y)).Gripper(false); 
      break;
    default: // Center
      pane.CentrePane().Floatable(false); 
      break;
    }
    pane.CloseButton(false);
    if (caption && *caption)
    {
      pane.Caption(caption);
      pane.Name(caption);
    }
    if (nFlags)
      pane.SetFlag(nFlags, true);

    ok = m_pManager->AddPane(wnd, pane);
    if (ok)
      m_pManager->Update();
  }
  return ok;
}

TwxWindow::TwxWindow() 
         : m_menu(NULL), _type(W_DOC), _eh(NULL), _app_data(0L), 
           _timer(NULL), m_pManager(NULL)
{ }

TwxWindow::TwxWindow(wxWindow *parent, wxWindowID id, const wxString& title,
										 const wxPoint& pos, const wxSize& size, long style)
  			 : TwxWindowBase(parent, id, title, pos, size, style),
           m_menu(NULL), _eh(NULL), _app_data(0L), _timer(NULL), 
           m_pManager(NULL), m_bInDestroy(false)
{
  _nice_windows.Put((WINDOW)this, this);
}

TwxWindow::~TwxWindow()
{
  if (!m_bInDestroy) // Controllo di non essere RIchiamato dalla gestione di E_DESTROY
  {
    m_bInDestroy = true;               
    XVT_EVENT e(E_DESTROY);
    DoXvtEvent(e);

    // Rendo praticamente impossibile risalire a questo oggetto d'ora in poi
    _nice_windows.Delete((WINDOW)this);
    _eh = NULL;
    _app_data = 0L;

    GetTDCMapper().DestroyTDC((WINDOW)this); // Elimina dalla lista dei display context

    if (HasCapture())
    {
		  ReleaseMouse();
      xvt_win_release_pointer(); // Paranoid?
    }
    
    if (_timer != NULL)
		  delete _timer;

    if (m_pManager != NULL)
    {
      m_pManager->UnInit(); // Obbligatorio ma, chissa' perche', non gestito dal distruttore!
      delete m_pManager;
    }

	  if (m_menu)
	  {
		  xvt_res_free_menu_tree(m_menu);
      m_menu = NULL;
		  ((TTaskWin*)_task_win)->PopMenuTree();
	  }
  }
}

///////////////////////////////////////////////////////////
// Main application = TASK_WIN functions
///////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS(TTaskWin, wxFrame)

BEGIN_EVENT_TABLE(TTaskWin, wxFrame)
  EVT_CLOSE(TTaskWin::OnClose)
  EVT_MENU_RANGE(1000, 32766, TTaskWin::OnMenu)
	EVT_PAINT(TTaskWin::OnPaint)
	EVT_SIZE(TTaskWin::OnSize)
  EVT_END_SESSION(TTaskWin::OnClose)
  EVT_END_PROCESS(wxID_ANY, TTaskWin::OnEndProcess)
END_EVENT_TABLE()

void TTaskWin::OnClose(wxCloseEvent& evt)
{
	if (evt.CanVeto())
	{
	  XVT_EVENT e(E_CLOSE);
	  const int veto = _task_win_handler((WINDOW)this, &e);
		evt.Veto(veto != 0);
	}
	else
		evt.Skip();
}

void TTaskWin::OnMenu(wxCommandEvent& evt)
{
	XVT_EVENT e(E_COMMAND);
	e.v.cmd.control = 0; e.v.cmd.shift = 0;
	e.v.cmd.tag = evt.GetId();

	if (m_MenuOwner == NULL || m_MenuOwner == this)
  	_task_win_handler((WINDOW)this, &e);
	else
  {
    TwxWindow* w = wxDynamicCast(m_MenuOwner, TwxWindow);
    if (w != NULL)
		  w->_eh((WINDOW)m_MenuOwner, &e);
  }
}

void TTaskWin::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
  const wxRect rctDamaged = GetUpdateRegion().GetBox();

	XVT_EVENT e(E_UPDATE);
  Rect2RCT(rctDamaged, &e.v.update.rct);
  
  TDC& dc = GetTDCMapper().GetTDC((WINDOW)this);
  dc.GetDC(true); // Forza la creazione di un wxPaintDC
  _task_win_handler((WINDOW)this, &e);
  dc.KillDC();
}

void TTaskWin::OnSize(wxSizeEvent& evt)
{
  XVT_EVENT e(E_SIZE);
	e.v.size.width  = evt.GetSize().x;
	e.v.size.height = evt.GetSize().y;
	_task_win_handler((WINDOW)this, &e);
}

void TTaskWin::OnEndProcess(wxProcessEvent& evt)
{
  if (_task_win_handler != NULL)
  {
    XVT_EVENT e(E_PROCESS);
    e.v.process.msg_id = E_DESTROY;
    e.v.process.pid = evt.GetPid();
    e.v.process.exit_code = evt.GetExitCode();
  	_task_win_handler((WINDOW)this, &e);
    delete evt.GetEventObject(); // delete wxProcess
  }
}

void TTaskWin::SetMenuTree(const MENU_ITEM* tree)
{
	wxMenuBar* bar = GetMenuBar();
  if (bar != NULL && tree != NULL)
  {
	  if (m_menu)
		  xvt_res_free_menu_tree(m_menu);
	  m_menu = xvt_menu_duplicate_tree(tree);

	  for ( ; tree != NULL && tree->tag != 0; tree++)
	  {
		  wxMenu* pMenu = new wxMenu;
		  for (MENU_ITEM* mi = tree->child; mi != NULL && mi->tag != 0; mi++)
		  {
			  wxMenuItem* item = NULL;
			  if (mi->separator)
				  item = new wxMenuItem(pMenu, wxID_SEPARATOR);
			  else
				  item = new wxMenuItem(pMenu, mi->tag, mi->text, wxEmptyString, mi->checkable);
        pMenu->Append(item);
		  }
		  const int nLast = bar->GetMenuCount()-1;
		  int m;
		  for (m = 2; m < nLast; m++)
		  {
			  wxMenu* pMenu = bar->GetMenu(m);
			  if (pMenu->FindItem(tree->child->tag))
			  {
				  bar->Remove(m);
  //				delete pMenu;
				  break;
			  }
		  }
		  bar->Insert(m, pMenu, tree->text);
	  }
  }
}

void TTaskWin::PushMenuTree(const MENU_ITEM* tree, wxWindow* owner)
{
	if(m_pOldBar != NULL)
    PopMenuTree();
  m_pOldBar = GetMenuBar();

	wxMenuBar* pBar = new wxMenuBar;
	for (; tree && tree->tag != 0; tree++)
	{
		wxMenu* pMenu = new wxMenu;
		for (MENU_ITEM* mi = tree->child; mi != NULL && mi->tag != 0; mi++)
		{
			wxMenuItem* item = NULL;
			if (mi->separator)
				item = new wxMenuItem(pMenu, wxID_SEPARATOR);
			else
				item = new wxMenuItem(pMenu, mi->tag, mi->text, wxEmptyString, mi->checkable);
      pMenu->Append(item);
		}
		pBar->Append(pMenu, tree->text);
	}
	SetMenuBar(pBar);
	m_MenuOwner = owner;
}

void TTaskWin::PopMenuTree()
{
	wxASSERT(m_pOldBar != NULL);
  wxMenuBar* pBar = GetMenuBar();
	SetMenuBar(m_pOldBar);
	delete pBar;
	m_pOldBar = NULL;
	m_MenuOwner = NULL;  // = this;
}

const XVT_COLOR_COMPONENT* TTaskWin::GetCtlColors() const
{
  if (m_xcc == NULL)
    ((TTaskWin*)this)->m_xcc = (XVT_COLOR_COMPONENT*)xvt_vobj_get_attr(NULL_WIN, ATTR_APP_CTL_COLORS);
  return m_xcc;
}

COLOR TTaskWin::GetCtlColor(XVT_COLOR_TYPE ct) const
{
  COLOR croma = COLOR_INVALID;
  const XVT_COLOR_COMPONENT* xcc = GetCtlColors();
  for (int i = 0; i < 16 && xcc[i].type != XVT_COLOR_NULL; i++)
  {
    if (xcc[i].type == ct)
    {
      croma = xcc[i].color;
      break;
    }
  }
  return croma;
}

void TTaskWin::SetCtlColors(const XVT_COLOR_COMPONENT* colors)
{
  GetCtlColors(); // Ensure m_xcc is not NULL
  for (int c = 0; colors[c].type != XVT_COLOR_NULL; c++)
  {
    int k = -1;
    for (k = 0; m_xcc[k].type != colors[c].type; k++);
    if (k < 15)
    {
      m_xcc[k].type  = colors[c].type;
      m_xcc[k].color = colors[c].color;
    }
  }
}

TTaskWin::TTaskWin(wxWindowID id, const wxString& title, 
                   const wxPoint& pos, const wxSize& size, long style)
				: wxFrame(NULL, id, title, pos, size, style), m_menu(NULL), m_pOldBar(NULL), m_MenuOwner(NULL), m_xcc(NULL)
{
	SetIcon(xvtart_GetIconResource(ICON_RSRC));
  _nice_windows.Put((WINDOW)this, this);
}

TTaskWin::~TTaskWin()
{
  _task_win = NULL;
  _nice_windows.Delete((WINDOW)this);
	if (m_menu)
	{
		xvt_res_free_menu_tree(m_menu);
		m_menu = NULL;
	}
  if (m_xcc)
  {
    xvt_mem_free((DATA_PTR)m_xcc);
    m_xcc = NULL;
  }
  wxExit(); // Exits main loop in the "rare" case it's still running
}

///////////////////////////////////////////////////////////
// TwxTaskBarIcon
///////////////////////////////////////////////////////////

class TwxTaskBarIcon : public wxTaskBarIcon
{
  wxWindow* _owned;
  DECLARE_EVENT_TABLE();

protected:  
  void OnClick(wxTaskBarIconEvent& e);

public:
  TwxTaskBarIcon(wxWindow* owned, short icon, wxString strTip);
};

BEGIN_EVENT_TABLE(TwxTaskBarIcon, wxTaskBarIcon)
  EVT_TASKBAR_LEFT_DOWN(OnClick)
  EVT_TASKBAR_RIGHT_DOWN(OnClick)
END_EVENT_TABLE()

void TwxTaskBarIcon::OnClick(wxTaskBarIconEvent& WXUNUSED(e))
{
  if (_owned != NULL)
  {
    if (_owned->IsShown())
      _owned->Show(false);
    else
    {
      _owned->Show();
      _owned->Raise();
    }
  }
}

TwxTaskBarIcon::TwxTaskBarIcon(wxWindow* owned, short icon, wxString strTip) 
              : _owned(owned) 
{ 
  wxIcon ico;
  if (icon <= 0 && _owned != NULL)
  {
    const wxFrame* pFrame = wxDynamicCast(_owned, wxFrame);
    if (pFrame != NULL)
      ico = pFrame->GetIcon();
  }
  else
    ico = xvtart_GetIconResource(icon);

  if (strTip.IsEmpty())
    strTip = _owned->GetLabel();

  SetIcon(ico, strTip);
}

WINDOW xvt_trayicon_create(WINDOW owned, short icon, const char* tooltip)
{
  WINDOW ti = NULL_WIN;
  if (owned != NULL_WIN)
    ti = (WINDOW)new TwxTaskBarIcon((wxWindow*)owned, icon, tooltip);
  return ti;
}

void xvt_trayicon_destroy(WINDOW tray)
{
  wxTaskBarIcon* pTray = wxDynamicCast((wxObject*)tray, wxTaskBarIcon);
  if (pTray != NULL)
    delete pTray;
}