#include "wx/wxprec.h"  
#ifdef __BORLANDC__     // addition for Borland C++
    #pragma hdrstop
#endif
#ifndef WX_PRECOMP      // for other compilers 
    #include "wx/wx.h"
#endif
#include <iSeeML/fwdPaths>
class ISeeMLDrawingArea : public wxPanel {
  wxFrame *parentFrame; 
  
  const wxBrush *daGP, *cvGP;   
  const wxPen   *bgGP,    
    *configGP,            
    *dubinsGP,   *fscpGP, 
    *dubinsTCGP, *fscpTCGP;  
  wxPoint centerPoint;    
  double  scale;          
  bool    movingConfig;   
  double  maxCurv, maxDCurv;  
public:
  
  ISeeMLDrawingArea(wxFrame& parent, double scl) : 
    wxPanel(&parent),         parentFrame(&parent), 
    scale(scl),               movingConfig(false), 
    maxCurv(1),               maxDCurv(0.8), 
    dubins(start, goal, maxCurv),
    fscp(start, goal, maxCurv, maxDCurv)  {
    daGP       = wxWHITE_BRUSH; 
    cvGP       = wxTRANSPARENT_BRUSH; 
    bgGP       = wxWHITE_PEN; 
    configGP   = wxRED_PEN; 
    dubinsGP   = new wxPen( wxT("BLUE") ); 
    fscpGP     = wxGREEN_PEN; 
    dubinsTCGP = new wxPen( wxT("ORANGE"), 1, wxLONG_DASH ); 
    fscpTCGP   = new wxPen( wxT("GOLD"),   1, wxLONG_DASH ); 
    SetBackgroundColour(*wxWHITE); 
  } 
  
  void OnMouseClick(wxMouseEvent& event); 
  void OnMouseMove (wxMouseEvent& event);
  void OnMouseWheel(wxMouseEvent& event);
  void OnSize      (wxSizeEvent&  event);
  void OnPaint     (wxPaintEvent& event);
  void OnChar      (wxKeyEvent&   event); 
private:
  
    return wxPoint( centerPoint.x + (
int)(p.
xCoord() * scale),
             centerPoint.y + (
int)(p.
yCoord() * scale) ); 
  }
  
  void Draw  (wxDC& dc, const bool erase = false); 
  DECLARE_EVENT_TABLE()
}; 
class ISeeMLFrame : public wxFrame
{
  enum { ID_MENU_EDIT_CONFIG = 1,   ID_MENU_EDIT_MAXIMA, 
     ID_MENU_EDIT_ZOOM,         ID_MENU_VIEW_PATH,       
     ID_MENU_VIEW_PATH_DUBINS,  ID_MENU_VIEW_PATH_FSCP, 
     ID_MENU_VIEW_PATH_BOTH,    ID_MENU_VIEW_TURN_CIRCLES 
  }; 
  wxMenu*                       pathSubMenu; 
  ISeeMLDrawingArea* drawingArea; 
public:
  
  ISeeMLFrame(const wxString& title);
  
  bool DrawDubins() const { 
    return ( pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_DUBINS) ||
         pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_BOTH)   );} 
  bool DrawFSCP() const { 
    return ( pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_FSCP) ||
         pathSubMenu->IsChecked(ID_MENU_VIEW_PATH_BOTH) ); } 
  bool ShowTurnCircles() const {
    wxMenu *editMenu = GetMenuBar()->GetMenu(2); 
    return editMenu->IsChecked(ID_MENU_VIEW_TURN_CIRCLES); }
  
  void enableSave(bool enable) 
  { GetMenuBar()->GetMenu(0)->Enable(wxID_SAVEAS, enable); }
  
  
  void OnSaveAs(wxCommandEvent& event); 
  void OnQuit  (wxCommandEvent& WXUNUSED(event)) { Close(TRUE); }
  
  void OnSetGoal     (wxCommandEvent& event); 
  void OnSetMaxima   (wxCommandEvent& event); 
  void OnSetZoom     (wxCommandEvent& event); 
  
  void OnSelectPath  (wxCommandEvent& event); 
  void OnShowTurnCirc(wxCommandEvent& WXUNUSED(event)) 
  { drawingArea->Refresh(); } 
  
  void OnAbout       (wxCommandEvent& event);
private:
  DECLARE_EVENT_TABLE()
}; 
class ISeeMLApp : public wxApp
{  
  bool OnInit()
  {
    ISeeMLFrame *frame = 
      new ISeeMLFrame( wxT("ISeeML V1.0 Interface") );
    frame->Show(TRUE);
    SetTopWindow(frame);
    return TRUE;
  } 
}; 
IMPLEMENT_APP(ISeeMLApp)
BEGIN_EVENT_TABLE(ISeeMLDrawingArea, wxPanel)
  EVT_LEFT_DOWN  (ISeeMLDrawingArea::OnMouseClick)
  EVT_MOTION     (ISeeMLDrawingArea::OnMouseMove)
  EVT_MOUSEWHEEL (ISeeMLDrawingArea::OnMouseWheel)
  EVT_SIZE       (ISeeMLDrawingArea::OnSize)
  EVT_PAINT      (ISeeMLDrawingArea::OnPaint)
  EVT_KEY_DOWN   (ISeeMLDrawingArea::OnChar)
END_EVENT_TABLE()
void ISeeMLDrawingArea::OnMouseClick(wxMouseEvent& 
                        event)
{ 
  movingConfig = !movingConfig; 
  OnMouseMove(event); 
  wxString message; 
  if (movingConfig) { 
    ((ISeeMLFrame *) parentFrame)->enableSave(false); 
    message.Printf(wxT("Clic to fix, use arrows or")
           wxT(" page up/down to change orientation") 
           ); } 
  else { 
    ((ISeeMLFrame *) parentFrame)->enableSave(true); 
    message.Printf(wxT("Clic to change again")
           wxT(" goal configuration")
           ); }
  parentFrame->SetStatusText(message);
} 
void ISeeMLDrawingArea::OnMouseMove(wxMouseEvent 
                           &event)
{
  if (movingConfig) {
    
    wxClientDC dc(this);
    PrepareDC(dc);
    parentFrame->PrepareDC(dc);
    
    wxPoint pos = event.GetPosition();
    int i = (int) dc.DeviceToLogicalX(pos.x), 
        j = (int) dc.DeviceToLogicalY(pos.y);
    
            (j - centerPoint.y) / scale, 
            goal.orientation() ); 
    ChangeGoal(newGoal); 
  } 
} 
void ISeeMLDrawingArea::OnMouseWheel(wxMouseEvent 
                        &event)
{
  static const double PI_196 = M_PI / 196, PI_28 = M_PI / 28, 
    PI_4 = M_PI / 4; 
  
  const int change =  goal.sign( event.GetWheelRotation() ); 
  
  double newTh = goal.orientation(); 
  if ( event.ShiftDown() )         
    newTh += change * PI_4; 
  else if ( event.MiddleIsDown() ) 
    newTh += change * PI_196;      
  else newTh += change * PI_28;    
    
  
  ChangeGoal(newGoal); 
} 
void ISeeMLDrawingArea::OnSize(wxSizeEvent &event)
{
  wxSize size   = event.GetSize(); 
  centerPoint.x = size.GetWidth()  / 2; 
  centerPoint.y = size.GetHeight() / 2;
  Refresh(); 
} 
void ISeeMLDrawingArea::OnPaint(wxPaintEvent &event)
{
  
  wxClientDC dc(this);
  PrepareDC(dc);
  parentFrame->PrepareDC(dc);
  
  dc.StartPage(); 
  ClearBackground(); 
  
  dc.SetBrush(*cvGP); 
  Draw(dc); 
  dc.EndPage(); 
} 
void ISeeMLDrawingArea::OnChar(wxKeyEvent &event)
{
  static const double PI_196 = M_PI / 196, PI_28 = M_PI / 28, 
    PI_4 = M_PI / 4; 
  double newTh = goal.orientation(); 
  if (movingConfig) 
    switch ( event.GetKeyCode() ) { 
    case WXK_HOME    : newTh = 0;        break; 
    case WXK_END     : newTh = M_PI;     break; 
    case WXK_PAGEDOWN: newTh += PI_4;    break; 
    case WXK_PAGEUP  : newTh -= PI_4;    break; 
    case WXK_DOWN    : newTh += PI_28;   break; 
    case WXK_UP      : newTh -= PI_28;   break; 
    case WXK_RIGHT   : newTh += PI_196;  break; 
    case WXK_LEFT    : newTh -= PI_196;  break; 
    default          : event.Skip(); 
    } 
  else
    event.Skip(); 
  
  ChangeGoal(newGoal); 
} 
void ISeeMLDrawingArea::Draw(wxDC& dc, 
const OrPtConfig& q)
 {
  
  const static int configSize = 20; 
  wxPoint startP = dblPt2intPt( q.
position() ); 
      iF = startP.x + di, jF = startP.y + dj; 
  
  dc.DrawLine(startP.x, startP.y, iF, jF); 
  dc.DrawLine(iF, jF, iF - (di + dj) / 4, jF + (di - dj) / 4);
  dc.DrawLine(iF, jF, iF - (di - dj) / 4, jF - (di + dj) / 4);
} 
{
  int i = 0; 
    wxPoint endP = dblPt2intPt( linPiece.
end().
position() ); 
    
    
    
    if (linPiece.
length() > 1.5 / scale) 
     
      
      dc.DrawLine(startP.x, startP.y, endP.x, endP.y); 
    else { 
      
      wxPoint cntr = dblPt2intPt
        dc.DrawArc(endP.x, endP.y, startP.x, startP.y,  
               cntr.x, cntr.y); 
      else
        dc.DrawArc(startP.x, startP.y, endP.x, endP.y, 
               cntr.x, cntr.y); } 
      else {
    
    double step = 5. / scale, arcLngth = step; 
    while (arcLngth < linPiece.
length()) { 
       wxPoint midP = 
        dblPt2intPt( linPiece[arcLngth].position() ); 
      dc.DrawLine(startP.x, startP.y, midP.x, midP.y); 
      startP = midP; 
      arcLngth += step; }
    dc.DrawLine(startP.x, startP.y, endP.x, endP.y); } 
    else 
      dc.DrawLine(startP.x, startP.y, endP.x, endP.y); 
    startP = endP; 
  } 
} 
{
  
    dc.DrawCircle(dblPt2intPt(centers[i]), radius); 
} 
void ISeeMLDrawingArea::Draw(wxDC& dc, const bool clean)
{
  
  if (clean)    dc.SetPen(*bgGP); 
  else          dc.SetPen(*configGP); 
  dc.SetBrush(*cvGP); 
  
  Draw(dc, start); 
  Draw(dc, goal); 
  ISeeMLFrame *parent = 
    (ISeeMLFrame *) parentFrame; 
  
  if ( clean || parent->DrawDubins() ) { 
    if (!clean) dc.SetPen(*dubinsGP); 
    Draw(dc, dubins); 
    if ( clean || parent->ShowTurnCircles() ) { 
      if (!clean) dc.SetPen(*dubinsTCGP); 
      DrawTC(dc, dubins); } } 
  if ( clean || parent->DrawFSCP() ) { 
    if (!clean) dc.SetPen(*fscpGP); 
    Draw(dc, fscp); 
    if ( clean || parent->ShowTurnCircles() ) { 
      if (!clean) dc.SetPen(*fscpTCGP); 
      DrawTC(dc, fscp); } } 
} 
void ISeeMLDrawingArea::ChangeGoal(
const OrPtConfig& 
                           q) 
{ 
  
  wxClientDC dc(this);
  PrepareDC(dc);
  parentFrame->PrepareDC(dc);
  
  dc.StartPage(); 
  
  Draw(dc, true); 
  
  goal   = q; 
  dubins.connect(start, goal); 
  fscp.connect(start, goal);
  
  Draw(dc); 
  dc.EndPage(); 
} 
ISeeMLFrame::ISeeMLFrame(const wxString& title)
  : wxFrame((wxFrame *) NULL, -1, title, wxDefaultPosition, 
        wxSize(750, 800))
{
  
  
  wxMenuBar *menuBar = new wxMenuBar;
  wxMenu    *menu    = new wxMenu;
  menu->Append( wxID_SAVEAS, wxT("&Save/Export as...\tS"), 
        wxT("Save drawing into various formats") );
  menu->AppendSeparator();
  menu->Append( wxID_EXIT,   wxT("E&xit\tX"), 
        wxT("Close GUI and quit") );
  menuBar->Append( menu, wxT("File") );
  
  menu = new wxMenu;
  menu->Append( ID_MENU_EDIT_CONFIG, 
        wxT("Set Goal &Configuration...\tC"), 
        wxT("Not yet available!") );
  menu->AppendSeparator();
  menu->Append( ID_MENU_EDIT_MAXIMA, 
        wxT("Modify Paths' &Maxima...\tM"), 
        wxT("Not yet available!") );
  menu->AppendSeparator();
  menu->Append( ID_MENU_EDIT_ZOOM, wxT("Change &Zoom/Scale...\tZ"), 
        wxT("Not yet available!") );
  menuBar->Append( menu, wxT("Edit") );
  
  menu        = new wxMenu;
  pathSubMenu = new wxMenu;
  pathSubMenu->AppendCheckItem( ID_MENU_VIEW_PATH_DUBINS, 
                wxT("&Dubins'\tD"),
                wxT("Only draw Dubin's path") ); 
  pathSubMenu->AppendCheckItem( ID_MENU_VIEW_PATH_FSCP,   
                wxT("&Linear Curvature (FSCP)\tL"),
                wxT("Only draw FSC path") ); 
  pathSubMenu->AppendCheckItem( ID_MENU_VIEW_PATH_BOTH, 
                    wxT("&Both\tB"),
                wxT("Draw both Dubin's and FSC paths") ); 
  pathSubMenu->Check(ID_MENU_VIEW_PATH_DUBINS, true);
  menu->Append(ID_MENU_VIEW_PATH, wxT("Draw Paths"), pathSubMenu);
  menu->AppendCheckItem( ID_MENU_VIEW_TURN_CIRCLES, 
             wxT("Show &Turning Circles...\tT"),
             wxT("Display turning circles")
             wxT(" of drawn path(s)") );
  menuBar->Append( menu, wxT("View") );
  
  menu = new wxMenu;
  menu->Append( wxID_ABOUT, wxT("&About...\tA"),
        wxT("Display information about this application") );
  menuBar->Append( menu, wxT("Help") );
  SetMenuBar(menuBar);
  CreateStatusBar();
  wxString message; 
  message.Printf(wxT("Clic in the drawing array to change")
         wxT(" goal configuration") 
         );
  SetStatusText(message);
  drawingArea = new ISeeMLDrawingArea(*this, 50); 
} 
BEGIN_EVENT_TABLE(ISeeMLFrame, wxFrame)
  
  EVT_MENU (wxID_SAVEAS, ISeeMLFrame::OnSaveAs)
  EVT_MENU (wxID_EXIT,   ISeeMLFrame::OnQuit)
  
  EVT_MENU (ID_MENU_EDIT_CONFIG, 
        ISeeMLFrame::OnSetGoal)
  EVT_MENU (ID_MENU_EDIT_MAXIMA, 
        ISeeMLFrame::OnSetMaxima)
  EVT_MENU (ID_MENU_EDIT_ZOOM, 
        ISeeMLFrame::OnSetZoom)
  
  EVT_MENU (ID_MENU_VIEW_PATH_DUBINS, 
        ISeeMLFrame::OnSelectPath)
  EVT_MENU (ID_MENU_VIEW_PATH_FSCP, 
        ISeeMLFrame::OnSelectPath)
  EVT_MENU (ID_MENU_VIEW_PATH_BOTH, 
        ISeeMLFrame::OnSelectPath)
  EVT_MENU (ID_MENU_VIEW_TURN_CIRCLES, 
        ISeeMLFrame::OnShowTurnCirc)
  
  EVT_MENU (wxID_ABOUT, ISeeMLFrame::OnAbout)
END_EVENT_TABLE()
void ISeeMLFrame::OnSaveAs(wxCommandEvent& 
                      WXUNUSED(event))
{
  SetStatusText( wxT("Sorry, exporting drawing ")
         wxT("is not yet implemented!") );
} 
void ISeeMLFrame::OnSetGoal(wxCommandEvent& 
                       WXUNUSED(event))
{
  SetStatusText( wxT("Sorry, goal definition's dialog box ")
         wxT("is not yet implemented!") );
} 
void ISeeMLFrame::OnSetMaxima(wxCommandEvent& 
                     WXUNUSED(event))
{
  SetStatusText( wxT("Sorry, maxima changing is not yet implemented!") );
} 
void ISeeMLFrame::OnSetZoom(wxCommandEvent& 
                       WXUNUSED(event))
{
  SetStatusText( wxT("Sorry, zoom/scale changing ")
         wxT("is not yet implemented!") );
} 
void ISeeMLFrame::OnSelectPath(wxCommandEvent& event)
{
  pathSubMenu->Check(ID_MENU_VIEW_PATH_DUBINS, false); 
  pathSubMenu->Check(ID_MENU_VIEW_PATH_FSCP,   false); 
  pathSubMenu->Check(ID_MENU_VIEW_PATH_BOTH,   false); 
  pathSubMenu->Check(event.GetId(),            true); 
  drawingArea->Refresh(); 
} 
void ISeeMLFrame::OnAbout(wxCommandEvent& 
                     WXUNUSED(event))
{
  wxString message; 
  message.Printf(wxT("This is ISeeML V1.0 GUI\n\n")
         wxT("It uses wxWidgets (www.wxWidgets.org),\n") 
         wxT("which should become\n")
         wxT("ISeeML V1.1 base GUI toolkit.\n\n")
         wxT("Written by A. Scheuer\n")
         wxT("(Alexis.Scheuer@inria.fr)")
         ); 
  wxMessageBox(message,
           _T("About ISeeML V1.0 Interface"), 
           wxOK | wxICON_INFORMATION, this);
}