/////////////////////////////////////////////////////////////////////////
// Name:        App.cpp
// Purpose:     L system breeding program
// Author:      Y. Wong
// Derived from:Penguin.h, wxGLcanvas test program
// Created:     04/06/00
// RCS-ID:      $Id: penguin.cpp,v 1.7 1999/10/14 13:09:01 RR Exp $
// Copyright:   (c) Robert Roebling
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif

#ifdef WIN32
#include <windows.h>
#endif

#include "App.h"
extern "C" {
#include "trackball.h"
}

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/textfile.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

using namespace std;

const string DefaultLstring="5\n45\n10\nAB\nA=[F[+FCB][-FCB]]\nB=[F[^FCA][&PCA]]\nC='(0.7071)\n@\n";
const string AboutString="==== LBREEDER: an Lsystem Parser and Breeder ====\nidea based on Richard Dawkins' \"Biomorphs\" program,\nparsing code based on Laurens Lapre's \"Lparser\" program.\n\nImplemented using openGL and wxWindows\nVersion 0.01\n\n(c) Yan Wong, Oxford University Museum\nLicense: GPL (i.e. may be freely distributed & modified)\nSource code and binaries available from\nhttp://pixie.oum.ox.ac.uk/L-Breeder/";

/* `Main program' equivalent, creating windows and returning main app frame */
bool MyApp::OnInit(void)
{
DEBUGGING_MESSAGE1("Started!\n");

  wxMenuBar *menuBar   = new wxMenuBar;
  wxMenu *fileMenu     = new wxMenu;
  wxMenu *editMenu     = new wxMenu;
  wxMenu *viewMenu     = new wxMenu;
  wxMenu *settingsMenu = new wxMenu;
  wxMenu *helpMenu     = new wxMenu;

  //////////////////////
  fileMenu->Append(ID_OPEN,
		   "&Open", 
		   "Open a new L-system file");

  fileMenu->Append(ID_SAVE, 
		   "&Save", 
		   "Save the selected L-system to a text file");

  fileMenu->Append(ID_EXPORT, 
		   "&Export 3D Form", 
		   "Export the selected L-system form");

  fileMenu->Append(ID_EXIT, 
		   "&Quit", 
		   "You don't really want to select this, do you?");
  ////////////////////////
  editMenu->Append(ID_BREED, 
		      "&Breed",
		      "Breed new mutants from the selected form");
  editMenu->Append(ID_VIEW_TEXTFRAME, 
		      "Genetic &Engineering",
		      "Show the L-system text for the selected individual",
		      TRUE);
  /////////////////////////
  viewMenu->Append(ID_GROW, 
		      "Show &Growing",
		      "Display successive recursions of the selected form");

  viewMenu->Append(ID_UNIFY_VIEW, 
		      "Unify &Viewpoint", 
		      "View all the L-system forms from the point of view of the selected form");

  viewMenu->Append(ID_VIEW_DETAILED, 
		      "View &Detailed", 
		      "Fill the window with the selected form", 
		      TRUE);

  viewMenu->Append(ID_SCALE_TO_FIT,
		      "Zoom to &Fit",
		      "Zoom such that the selected form fits in the window");
  /////////////////////////
  settingsMenu->Append(ID_NO_ANIMATION, 
		      "No Animation", 
		      "Do not show animation when breeding",
		       TRUE);
  settingsMenu->Append(ID_CHG_SEED, 
		   "Change Random Seed");
  ////////////////////////
  helpMenu->Append(ID_ABOUT, 
		   "About L-Breeder", 
		   "Information about the program");

  helpMenu->AppendSeparator();

  helpMenu->Append(ID_HELP_BASIC,
		   "Basic Help", 
		   "How to use the L-Breeder program");
  helpMenu->Append(ID_HELP_MENU,
		   "Help on Menus", 
		   "What the various menu options do");
  helpMenu->Append(ID_HELP_LSYSTEM, 
		   "Help on L-systems", 
		   "Information about L-systems and their use");
  helpMenu->Append(ID_HELP_MUTATIONS,
		   "Help on Mutations", 
		   "How mutations occur in the program");
  helpMenu->Append(ID_HELP_TUTORIAL, 
		   "Genetic Engineering Tutorial", 
		   "A tutorial on how to make your own forms");
  ///////////////////

  editMenu->Check(ID_VIEW_TEXTFRAME, FALSE);
  viewMenu->Check(ID_VIEW_DETAILED, FALSE);
  settingsMenu->Check(ID_NO_ANIMATION, FALSE);


  menuBar->Append(fileMenu,     "&File");
  menuBar->Append(editMenu,     "&Edit");
  menuBar->Append(viewMenu,     "&View");
  menuBar->Append(settingsMenu, "S&ettings");
  menuBar->Append(helpMenu,     "&Help");

  /* Create the main frame window */
  MyFrame *frame = new MyFrame(NULL, "L-System Breeder", wxPoint(50, 50), wxSize(800, 500));
  frame->SetMenuBar(menuBar);
  //  frame->SetSize(wxSize(800,400));

  
  /* Show the frame */
  frame->Show(TRUE);
  frame->ChangeSelected(NULL);
  frame->UnifyView(frame->Lsys[P_COL][P_ROW]);
  frame->ChangeSelected(frame->Lsys[0][0]);
  return TRUE;
}

IMPLEMENT_APP(MyApp)

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
  EVT_SIZE(                    MyFrame::OnSize)
  EVT_MENU(ID_EXIT,            MyFrame::OnExit)
  EVT_MENU(ID_OPEN,            MyFrame::OnOpen)
  EVT_MENU(ID_SAVE,            MyFrame::OnSave)
  EVT_MENU(ID_EXPORT,          MyFrame::OnExport)
  EVT_MENU(ID_CHG_SEED,        MyFrame::OnChangeSeed)
  EVT_MENU(ID_GROW,            MyFrame::OnGrow)
  EVT_MENU(ID_UNIFY_VIEW,      MyFrame::OnUnifyView)
  EVT_MENU(ID_VIEW_DETAILED,   MyFrame::OnViewDetailed)
  EVT_MENU(ID_SCALE_TO_FIT,    MyFrame::OnScaleToFit)
  EVT_MENU(ID_VIEW_TEXTFRAME,  MyFrame::OnToggleTextFrame)
  EVT_MENU(ID_BREED,           MyFrame::OnBreed)
  EVT_MENU(ID_MUTATE_RAT,      MyFrame::OnMutateRAT)
  EVT_MENU(ID_MUTATE_PARAM,    MyFrame::OnMutateParam)
  EVT_MENU(ID_MUTATE_ADD_R,    MyFrame::OnMutateAddR)
  EVT_MENU(ID_MUTATE_DEL_R,    MyFrame::OnMutateDelR)
  EVT_MENU(ID_MUTATE_ADD_TO,   MyFrame::OnMutateAddTo)
  EVT_MENU(ID_MUTATE_DEL_FROM, MyFrame::OnMutateDelFrom)
  EVT_MENU(ID_ABOUT,           MyFrame::OnAbout)
  EVT_MENU(ID_HELP_BASIC,      MyFrame::OnHelp)
  EVT_MENU(ID_HELP_MENU,       MyFrame::OnHelp)
  EVT_MENU(ID_HELP_LSYSTEM,    MyFrame::OnHelp)
  EVT_MENU(ID_HELP_MUTATIONS,  MyFrame::OnHelp)
  EVT_MENU(ID_HELP_TUTORIAL,   MyFrame::OnHelp)
  EVT_BUTTON(TF_GENERATE,      MyFrame::OnLTextChanged)
  EVT_MOUSE_EVENTS(            MyFrame::OnMouse)
  EVT_KEY_DOWN(                MyFrame::OnKeyDown)
END_EVENT_TABLE()

/* My frame constructor */
MyFrame::MyFrame(wxFrame *frame, const wxString& title, const wxPoint& pos,
    const wxSize& size, long style):
  wxFrame(frame, -1, title, pos, size, style),
  PixBuffer(NULL),
  min_thick(0.2f),
  maximised_view(NULL),
  Cols(COLS),
  Rows(ROWS),
  Selected(NULL),
  ParentBGColour(0.4f,0.9f,0.4f),
  PMutantBGColour(0.5f,0.5f,0.9f),
  AMutantBGColour(0.9f,0.9f,0.5f)
{
DEBUGGING_MESSAGE2("application called with %i arguments\n", wxGetApp().argc);

  switch (wxGetApp().argc) {
  default:
DEBUGGING_MESSAGE1("caution: all parameter arguments ignored\n");
  case 1:
  case 2:
    MutParam = new MutationParameters();
    break;
  case 3:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]));
    break;
  case 4:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3]);
    break;
  case 5:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]));
    break;
  case 6:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]));
    break;
  case 7:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]));
    break;
  case 8:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]),
				      atof(wxGetApp().argv[7]));
    break;
  case 9:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]),
				      atof(wxGetApp().argv[7]),
				      atof(wxGetApp().argv[8]));
    break;
  case 10:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]),
				      atof(wxGetApp().argv[7]),
				      atof(wxGetApp().argv[8]),
				      atof(wxGetApp().argv[9]));
    break;
  case 11:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]),
				      atof(wxGetApp().argv[7]),
				      atof(wxGetApp().argv[8]),
				      atof(wxGetApp().argv[9]),
				      atof(wxGetApp().argv[10]));
    break;
  case 12:
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]),
				      atof(wxGetApp().argv[7]),
				      atof(wxGetApp().argv[8]),
				      atof(wxGetApp().argv[9]),
				      atof(wxGetApp().argv[10]),
				      atof(wxGetApp().argv[11]));
    break;
  case 33:
DEBUGGING_MESSAGE1("Using all parameters\n");
    MutParam = new MutationParameters(atoi(wxGetApp().argv[2]),
				      wxGetApp().argv[3],
				      atof(wxGetApp().argv[4]),
				      atof(wxGetApp().argv[5]),
				      atof(wxGetApp().argv[6]),
				      atof(wxGetApp().argv[7]),
				      atof(wxGetApp().argv[8]),
				      atof(wxGetApp().argv[9]),
				      atof(wxGetApp().argv[10]),
				      atof(wxGetApp().argv[11]),
				      atof(wxGetApp().argv[12]),
				      atof(wxGetApp().argv[13]),
				      atof(wxGetApp().argv[14]),
				      atof(wxGetApp().argv[15]),
				      atof(wxGetApp().argv[16]),
				      atof(wxGetApp().argv[17]),
				      atof(wxGetApp().argv[18]),
				      atof(wxGetApp().argv[19]),
				      atof(wxGetApp().argv[20]),
				      atof(wxGetApp().argv[21]),
				      atof(wxGetApp().argv[22]),
				      atof(wxGetApp().argv[23]),
				      atof(wxGetApp().argv[24]),
				      atof(wxGetApp().argv[25]),
				      atof(wxGetApp().argv[26]),
				      atof(wxGetApp().argv[27]),
				      atof(wxGetApp().argv[28]),
				      atof(wxGetApp().argv[29]),
				      atof(wxGetApp().argv[30]),
				      atof(wxGetApp().argv[31]),
				      atof(wxGetApp().argv[32]));
    break;
};

  LightClass temp;
  temp.set_position(-50.0f, 50.0f, 0.0f, 0.0f);
  temp.set_colour(.6f, .6f, .6f, 1.0f); // white light 
  Lights.push_back(temp);
  temp.set_position( 50.0f, 50.0f, 0.0f, 0.0f);
  temp.set_colour(.4f, .4f, 1, 1.0f);   // cold blue light
  Lights.push_back(temp);

  colours.push_back(vect3<GLfloat>(0.7f,0.3f,0.9f));
  colours.push_back(vect3<GLfloat>(0.1f,0.8f,0.9f));
  colours.push_back(vect3<GLfloat>(0.2f,0.9f,0.1f));
  colours.push_back(vect3<GLfloat>(0.2f,0.3f,0.1f));
  
  col_red=new vect3<GLfloat>(1.0f,0.0f,0.0f);
  col_black=new vect3<GLfloat>(0.0f,0.0f,0.0f);

  TextFrame = new MyTextFrame(this, "L system description", wxPoint(0,0), wxSize(300,100));
  TextFrame->Show(FALSE);

  AnimationCanvas=new AnimateGLCanvas(this, &Cols, &Rows, col_black, col_red, wxPoint(0,0), wxSize(1, 1));
  AnimationCanvas->Show(FALSE);

  for (unsigned int i=0;i<Cols;i++)
    for (unsigned int j=0;j<Rows;j++) {
    if ((i==P_COL) && (j==P_ROW))      //Parent
	Lsys[i][j]=
	new LsysGLCanvas(this, &min_thick, &colours, &ParentBGColour,
			 &Lights, MutParam, &Selected, col_black, col_red, 
			 wxPoint(0,0),
			 wxSize(1,1));
    else if ((i>P_COL) || ((i==P_COL) && (j>=P_ROW))) //parameter mutant
	Lsys[i][j]= 
	new LsysGLCanvas(this, &min_thick, &colours, &PMutantBGColour,
			 &Lights, MutParam, &Selected, col_black, col_red, 
			 wxPoint(0,0),
			 wxSize(1, 1));
    else                               //axiom mutant
    Lsys[i][j]= 
	new LsysGLCanvas(this, &min_thick, &colours, &AMutantBGColour,
			 &Lights, MutParam, &Selected, col_black, col_red, 
			 wxPoint(0,0),
			 wxSize(1, 1));
  };

  size_up();
  
DEBUGGING_MESSAGE1("Finished building LsysGLCanvases\n");

//Input a new Lsystem.

  if ((wxGetApp().argc>1) &&
      (open(wxGetApp().argv[1], Lsys[P_COL][P_ROW]))) {
    ParentToChildren();
  } else {
    *(Lsys[P_COL][P_ROW]->the_Lsystem) << DefaultLstring;
    ParentToChildren();
  };

  Selected=NULL;
}

MyFrame::~MyFrame()
{
  if (AnimationCanvas) delete AnimationCanvas;
  for (unsigned int i=0;i<Cols;i++)
    for (unsigned int j=0;j<Rows;j++)
      if (Lsys[i][j]) delete Lsys[i][j];
  delete MutParam;
  delete col_red;
  delete col_black;
  delete PixBuffer;
}

void MyFrame::OnSize(wxSizeEvent &event)
{
  size_up();
}

void MyFrame::size_up()
{
//Desc: set the size of the subwindows etc.
  int w, h;
  GetClientSize(&w,&h);
  p_width =w/Cols;
  p_height=h/Rows;

  if (PixBuffer!=NULL)
    delete []PixBuffer;
  PixBuffer=new GLuint[p_width*p_height*3];
  
  AnimationCanvas->SetSize(0, 0, w, h);
  for (unsigned int i=0;i<Cols;i++)
    for (unsigned int j=0;j<Rows;j++)
      {
	Lsys[i][j]->SetNormalSize(i*p_width, (Rows-j-1)*p_height,p_width,p_height);
	//check if we are viewing in detail
	if (Lsys[i][j]==maximised_view)
	  Lsys[i][j]->SetSize(0,0,w,h);
	else
	  Lsys[i][j]->SetToNormalSize();
      };
}

void MyFrame::OnExit(wxCommandEvent& event)
{
  Exit();
}

void MyFrame::Exit()
{
  Destroy();
}

void MyFrame::OnOpen(wxCommandEvent& event)
{
  wxFileDialog dlg(this, "Chose an Lstring file to open","", "", "*.ls", wxOPEN);
  if (dlg.ShowModal()==wxID_OK)
    if (Selected==NULL)
      open(dlg.GetPath(), Lsys[P_COL][P_ROW]);
    else
      open(dlg.GetPath(), Selected);
}

void MyFrame::OnSave(wxCommandEvent& event)
{
  if (Selected==NULL)
    ErrorUnselected();
  else 
    {
      wxFileDialog dlg(this, "Save as","", "", "*.ls", wxSAVE|wxOVERWRITE_PROMPT);
      if (dlg.ShowModal()==wxID_OK)
	save(dlg.GetPath(), Selected);
    };
}

void MyFrame::OnExport(wxCommandEvent& event)
{
  wxMessageDialog td(this, 
		     "Sorry, not implemented yet",
		     "Temporary Dialog",  
		     wxOK);
  td.ShowModal();
}

void MyFrame::OnChangeSeed(wxCommandEvent& event)
{
  wxTextEntryDialog td(this, "Seed", "Please enter seed number", "",  wxOK|wxCANCEL);
  if (td.ShowModal()==wxID_OK)
    {
      int s=atoi(td.GetValue());
    };
}

void MyFrame::OnGrow(wxCommandEvent& event)
{
  if (Selected==NULL)
    ErrorUnselected();
  else
    Selected->display_Lstring_growing();  
}

void MyFrame::OnBreed(wxCommandEvent& event)
{
  breed_from(Selected);
}

void MyFrame::breed_from(unsigned int i, unsigned int j) 
{
  ChangeSelected(NULL);
  if ((this->GetMenuBar()->IsChecked(ID_NO_ANIMATION))==FALSE)
    {
      Lsys[i][j]->take_Picture(0,0,p_width, p_height, PixBuffer);
      for (unsigned int c=0;c<Cols;c++)
	for (unsigned int r=0;r<Rows;r++)
	  Lsys[c][r]->Show(FALSE);
      AnimationCanvas->Show(TRUE);
      AnimationCanvas->Refresh();
      AnimationCanvas->ShowBreeding(i,j,P_COL,P_ROW,i*p_width, j*p_height+1, P_COL*p_width, P_ROW*p_height+1, p_width, p_height, GL_RGB, GL_UNSIGNED_INT, &(PixBuffer[0]),25);
    };
  Lsys[P_COL][P_ROW]->copy(*(Lsys[i][j]));

  ParentToChildren();
  AnimationCanvas->Show(FALSE);
};

void MyFrame::breed_from(LsysGLCanvas *the_Selected) 
{
  unsigned int i, j;
  if (findSelected(i,j, the_Selected)==false)
    ErrorUnselected();
  else 
    breed_from(i,j);
}

void MyFrame::OnMutateRAT(wxCommandEvent& event)
{
  mutate(MUT_RAT);
}

void MyFrame::OnMutateParam(wxCommandEvent& event)
{
  mutate(MUT_PARAM);
}

void MyFrame::OnMutateAddR(wxCommandEvent& event)
{
  mutate(MUT_ADD_RULE);
}

void MyFrame::OnMutateDelR(wxCommandEvent& event)
{
  mutate(MUT_DEL_RULE);
}

void MyFrame::OnMutateAddTo(wxCommandEvent& event)
{
  mutate(MUT_ADD_TO);
}

void MyFrame::OnMutateDelFrom(wxCommandEvent& event)
{
  mutate(MUT_DEL_FROM);
}

void MyFrame::OnToggleTextFrame(wxCommandEvent &event)
{
  ToggleTextFrame();
}

void MyFrame::ToggleTextFrame() 
{
  if (TextFrame->IsShown()) {
    //change menu name
    this->GetMenuBar()->Check(ID_VIEW_TEXTFRAME,FALSE);
    TextFrame->Show(FALSE);
  } else {
    this->GetMenuBar()->Check(ID_VIEW_TEXTFRAME,TRUE);
    TextFrame->Show(TRUE);
  };
}

void MyFrame::OnLTextChanged(wxCommandEvent& event)
{
  if (Selected) {
    string   sStr=TextFrame->GetText().c_str();
    *(Selected->the_Lsystem) << sStr;
    sStr="";
    *(Selected->the_Lsystem) >> sStr;
    TextFrame->PutText(sStr.c_str());
    Selected->Refresh(FALSE);
  };
}


void MyFrame::OnKeyDown( wxKeyEvent& event )
{
  switch (event.KeyCode()){
  case WXK_DOWN:
    if (event.ShiftDown())
      zoom(50);
    else
      zoom(5);
    return;
  case WXK_UP:
    if (event.ShiftDown())
      zoom(-50);
    else
      zoom(-5);
    return;
  case 'Q':
  case 'q':
    if (event.ControlDown())
      Exit();
    return;
  case 'r':
  case 'R':
    mutate(MUT_RAT);
    return;
  case 'm':
  case 'M':
    mutate(MUT_PARAM);
    return;
  case '+':
    mutate(MUT_ADD_RULE);
    return;
  case '-':
    mutate(MUT_DEL_RULE);
    return;
  case '<':
    mutate(MUT_ADD_TO);
    return;
  case '>':
    mutate(MUT_DEL_FROM);
    return;
  case 'g':
  case 'G':
    display_Lstring_growing();
    return;
  };
}

void MyFrame::OnMouse(wxMouseEvent &event)
{
  static int beginx, beginy;
  unsigned int i,j;
  //check if event is in an Lsys canvas
  for (i=0;i<Cols;i++)
    for (j=0;j<Rows;j++)
      if (Lsys[i][j]==event.GetEventObject()) {
	//this is an event passed up from an Lsys canvas
	//single left click selects, 
	//left drag rotates,
	//shift + single left click selects and unifies the viewpoint,
	//shift + left drag rotates all
	//double left click selects and breeds from
	//single right click brings up text frame
	if (event.LeftDown()) {
	  ChangeSelected(i,j);
	  if (event.ShiftDown())
	    UnifyView(i,j);
	  beginx=event.GetX();
	  beginy=event.GetY();
	};
	
	if (event.LeftDClick())
	  breed_from(i,j);
	
	if ((event.Dragging()) && (Selected==Lsys[i][j])) {
	  // drag in progress, simulate trackball
	  wxSize sz(Lsys[i][j]->GetClientSize());
	  float spin_quat[4];
	  trackball(spin_quat,
		    (2.0*beginx -       sz.x) / sz.x,
		    (     sz.y - 2.0*beginy ) / sz.y,
		    (     2.0*event.GetX() - sz.x) / sz.x,
		    (    sz.y - 2.0*event.GetY() ) / sz.y);
	  beginx=event.GetX();
	  beginy=event.GetY();
	  if (event.ShiftDown())
	    for (int a=0;a<Cols;a++)
	      for (int b=0;b<Rows;b++)
		Lsys[a][b]->spin(spin_quat);
	  else
	    Lsys[i][j]->spin(spin_quat);
	};

	if (event.RightUp())
	  ToggleTextFrame();
	
	return;
      };
  
  //not a mouse event that has ben passed up from an Lsys canvas
  return;
}

void MyFrame::OnViewDetailed(wxCommandEvent &event)
{
  if (this->GetMenuBar()->IsChecked(ID_VIEW_DETAILED))
    view_detailed(Selected);
  else
    view_detailed(NULL);
}

void MyFrame::OnScaleToFit(wxCommandEvent &event)
{
  scale_to_fit(Selected);
}


void MyFrame::scale_to_fit(LsysGLCanvas *the_Selected) 
{
  if (the_Selected==NULL)
    ErrorUnselected();
  else 
    the_Selected->scale_to_fit();
}

void MyFrame::ChangeSelected(unsigned int i, unsigned int j)
{
  if ((i<COLS) && (j<ROWS))
    ChangeSelected(Lsys[i][j]);
}


void MyFrame::ChangeSelected(LsysGLCanvas *new_Selected)
{
  if (new_Selected!=Selected) {
    string tempStr("");
    LsysGLCanvas *old_Selected = Selected;
    Selected = new_Selected;
    
    if (Selected!=NULL) {
      Selected->Refresh(FALSE);
      *(Selected->the_Lsystem) >> tempStr;
    };
    
    if (old_Selected!=NULL)     // cancel the other selection
      old_Selected->Refresh(FALSE);
   
     TextFrame->PutText(tempStr.c_str());
    
  };
}

void MyFrame::OnUnifyView(wxCommandEvent &event)
{
  UnifyView();
}

void MyFrame::UnifyView(LsysGLCanvas *the_Selected)
{
  if (the_Selected!=NULL) {
    for (unsigned int i=0;i<Cols;i++)
      for (unsigned int j=0;j<Rows;j++)
	if (Lsys[i][j]!=the_Selected)
	  Lsys[i][j]->set_viewpoint_to(*the_Selected);
  } else
    ErrorUnselected();
}

void MyFrame::UnifyView(unsigned int i, unsigned int j)
{
  if ((i<COLS) && (j<ROWS))
    UnifyView(Lsys[i][j]);
}

void MyFrame::UnifyView()
{
  UnifyView(Selected);
}

/* Private methods - used internally */

bool MyFrame::open(wxString name, LsysGLCanvas *the_Selected)
{
  wxTextFile f;
  if (f.Open(name)) {
    string   s="";
    for (wxString str = f.GetFirstLine(); !f.Eof(); str = f.GetNextLine() )
      {
	s+=str.c_str();
	s+="\n";
      };
    *(the_Selected->the_Lsystem) << s;
    the_Selected->Refresh();
    return true;
  } else {
    wxString msg;
    msg =  "Could not open the file ";
    msg += name;
    wxMessageDialog td(this, msg, "Error", wxOK);
    td.ShowModal();
    return false;
  };
}

void MyFrame::save(wxString name, LsysGLCanvas *the_Selected)
{
  wxFile     f;
  wxTextFile tf;
  
  if ((f.Create(name,TRUE)) && (tf.Open(name)))
    { 
      string s="", subs="";
      size_t pos1=0, pos2=0;
      *(the_Selected->the_Lsystem) >> s;
DEBUGGING_MESSAGE2("%s\n",s.c_str());
      while (pos2!=string::npos)
	{
	  pos2=s.find('\n',pos1);
	  if (pos2!=string::npos) {
	    tf.AddLine( (s.substr(pos1,pos2-pos1)).c_str());
	    pos1=pos2+1;
	  } else
	    tf.AddLine( (s.substr(pos1)).c_str());
	};
      tf.Write();
    } else {
      wxString msg;
      msg =  "Could not save the file ";
      msg += name;
      wxMessageDialog td(this, msg, "Error", wxOK);
      td.ShowModal();
    };
}

bool MyFrame::findSelected(unsigned int &i, unsigned int &j, LsysGLCanvas *the_Selected)
{
  for (i=0; i<Cols;i++)
    for (j=0; j<Rows;j++)
      if (the_Selected==Lsys[i][j])
	return true;
  
  return false;
}

bool MyFrame::findSelected(unsigned int &i, unsigned int &j)
{
  return findSelected(i,j,Selected);
}


void MyFrame::mutate(LsysGLCanvas *the_Selected, mutations mut)
{
  if (the_Selected==NULL)
    ErrorUnselected();
  else {
    string tempStr;
    the_Selected->mutate(mut);
    *(the_Selected->the_Lsystem) >> tempStr; 
    TextFrame->PutText(tempStr.c_str());
  };
} 

bool MyFrame::mutate(mutations mut)
{
  if (Selected==NULL)
    return false;
  
  mutate(Selected,mut);
  return true;
}

bool MyFrame::zoom(float amount)
{
  if (Selected==NULL)
    return false;
  
  Selected->zoom(amount);
  return true;
}

bool MyFrame::display_Lstring_growing()
{
  if (Selected==NULL)
    return false;
  
  Selected->display_Lstring_growing();
  return true;
}

void MyFrame::view_detailed(LsysGLCanvas *the_Selected)
{    
//Desc: if the_Selected is NULL, shows all canvases at the
//      same size, otherwise make the_Selected canvas fill the
//      whole screen.
  int           h,w;

  if (the_Selected==NULL) {
    
    //set "normal view"
    if (maximised_view==NULL) {
      ErrorUnselected();
    } else {
      //we are in "detailed view"
      maximised_view->SetToNormalSize();
      for (unsigned int i=0;i<Cols;i++)
	for (unsigned int j=0;j<Rows;j++)
	  Lsys[i][j]->Show(TRUE);
      maximised_view=NULL;
      this->GetMenuBar()->Check(ID_VIEW_DETAILED,FALSE);
    };
  } else {
    
    //set "detailed view"
    if (maximised_view==the_Selected)
      return; //already viewing the_Selected in detail
    if (maximised_view!=NULL)
      //we are already in "detailed view", but viewing another one
      maximised_view->SetToNormalSize();
    
    this->GetClientSize(&w, &h);
    for (unsigned int i=0;i<Cols;i++)
      for (unsigned int j=0;j<Rows;j++)
	Lsys[i][j]->Show(FALSE);
    the_Selected->SetSize(0,0,w,h);
    the_Selected->Show(TRUE);
    maximised_view=Selected;
    this->GetMenuBar()->Check(ID_VIEW_DETAILED,TRUE);
  };
}

void MyFrame::ParentToChildren()
{
  //force paint so that zoom factor is obtained;
  Lsys[P_COL][P_ROW]->Show(TRUE);
  Lsys[P_COL][P_ROW]->Paint();
  
  //Breed from the rest
  for (unsigned int i=0;i<Cols;i++)
	for (unsigned int j=0;j<Rows;j++)
	  if (!((i==P_COL) && (j==P_ROW))) {
	    if ((i>P_COL) || ((i==P_COL) && (j>=P_ROW)))
		  Lsys[i][j]->copy_with_mutation(*Lsys[P_COL][P_ROW],MUT_GENERAL_PARAMETER);
	    else
		  Lsys[i][j]->copy_with_mutation(*Lsys[P_COL][P_ROW],MUT_GENERAL_ALGORITHM);
        Lsys[i][j]->Show(TRUE);
        Lsys[i][j]->Refresh(FALSE);
    };
}

void MyFrame::ErrorUnselected()
{
  wxMessageDialog td(this, 
		     "You have not selected an Lsystem", 
		     "Error",  
		     wxOK);
  td.ShowModal();
}

void MyFrame::OnAbout(wxCommandEvent& event)
{
  wxMessageDialog td(this, AboutString.c_str(), "About L-Breeder",wxOK);
  td.ShowModal();
}

void MyFrame::OnHelp(wxCommandEvent& event)
{
  wxString str;
  switch (event.GetId()){
  case ID_HELP_BASIC:
    str="Please view the file \"basic.html\" which should come with the program";
    break;
  case ID_HELP_MENU:
    str="Please view the file \"menus.html\" which should come with the program";
    break;
  case ID_HELP_LSYSTEM:
    str="Please view the file \"l-systems.html\" which should come with the program";
    break;
  case ID_HELP_MUTATIONS:
    str="Please view the file \"mutations.html\" which should come with the program";
    break;
  case ID_HELP_TUTORIAL:
    str="Please view the file \"tutorial.html\" which should come with the program";
    break;
  default:
    str="Oops, error in MyFrame::OnHelp";
  };
  
  
  wxMessageDialog td(this, 
		     str,
		     "Temporary Help Solution",  
		     wxOK);
  td.ShowModal();
}
