/*   
 *   Copyright 2006 Owen Piette
 *   See license.txt for terms.
 *
 */


#include <stdio.h>
#include <iostream>
#include <string.h>

#include "Sand-4.h"
#include <wx/wx.h>
#include <wx/dcscreen.h>
#include <wx/dcbuffer.h>
#include <wx/datetime.h>
#include <wx/file.h>
#include <wx/image.h>
#include <wx/thread.h>
#include <wx/event.h>
#include <wx/menuitem.h>
#include <wx/treectrl.h>

#define TIMERINTERVAL 33
#define LEFTRIGHTPROB 0.9

#define MAXNUMBEROFELEMENTS 128
#define MAXSIZE 1280*1024

#define found(center, other, cx, cy, ox, oy){\
  char t = data[center];\
  data[center] = data[other];\
  data[other] = t;\
  draw_x[numberToDraw] = cx;\
  draw_y[numberToDraw] = cy;\
  ++numberToDraw;\
  draw_x[numberToDraw] = ox;\
  draw_y[numberToDraw] = oy;\
  ++numberToDraw;\
  calc[center] = true;\
  if (t != 0)\
    calc[other] = true;\
};

BEGIN_EVENT_TABLE(Canvas, wxWindow)
  //EVT_PAINT(Canvas::OnPaint)
  EVT_ERASE_BACKGROUND(Canvas::OnEraseBG)
  EVT_RIGHT_DOWN(Canvas::OnMouseRightDown)
  EVT_LEFT_DOWN(Canvas::OnMouseLeftDown)
  EVT_LEFT_UP(Canvas::OnMouseLeftUp)
  EVT_MOTION(Canvas::OnMouseMove)
  EVT_TIMER(999, Canvas::OnTimer)
  EVT_TIMER(998, Canvas::OnSecondTimer)
  EVT_IDLE(Canvas::OnIdle)
END_EVENT_TABLE()


BEGIN_EVENT_TABLE(MainFrame, wxFrame)
  EVT_SET_FOCUS(MainFrame::OnMove)
  EVT_SIZE(MainFrame::OnSize)
  EVT_TREE_SEL_CHANGED(1000, MainFrame::OnElementChoice)
  EVT_LISTBOX(1001, MainFrame::OnChoice)

  EVT_BUTTON(1025, MainFrame::OnButton)

  EVT_MENU(1002, MainFrame::OnMenu)
  EVT_MENU(1003, MainFrame::OnMenu)
  EVT_MENU(1004, MainFrame::OnMenu)
  EVT_MENU(1005, MainFrame::OnMenu)
  EVT_MENU(1006, MainFrame::OnMenu)
  EVT_MENU(1007, MainFrame::OnMenu)

  EVT_MENU(1050, MainFrame::OnMenu)
  EVT_MENU(1051, MainFrame::OnMenu)
  EVT_MENU(1052, MainFrame::OnMenu)
  EVT_MENU(1053, MainFrame::OnMenu)
  EVT_MENU(1054, MainFrame::OnMenu)
  EVT_MENU(1055, MainFrame::OnMenu)
  EVT_MENU(1056, MainFrame::OnMenu)
END_EVENT_TABLE()

IMPLEMENT_APP(Sand)


char defaultFileContents[] = "#wxSand: Owen Piette's Falling Sand Game\n\
#Version 4, File subversion 2\n\
#element element r g b gravity slip density conductivity visible\n\
element	Empty	0	0	0	0.000000	1.000000	0.000000	0.000000	1\n\
element	Wall	128	128	128	0.000000	0.000000	1.000000	0.500000	1\n\
element	Fire	247	63	63	-1.000000	1.000000	0.000000	1.000000	1\n\
self	0.050000	Fire	1.0000	Empty	\n\
neighbor 0.9	Fire	Water	1.0	Empty		Steam\n\
neighbor 0.75	Fire	Oil	1.0	Fire		Fire\n\
neighbor 1.0	Fire	Plant	1.0	Fire		Fire\n\
neighbor 0.005	Fire	Cera	1.0	MoltenCera	Ember\n\
element	Water	32	32	255	0.700000	1.000000	0.500000	0.000000	1\n\
element	Plant	32	204	32	0.000000	0.000000	1.000000	0.000000	1\n\
neighbor 0.2	Plant	Water	1.0	Plant	Plant\n\
element	Sand	238	204	128	0.900000	0.500000	0.900000	0.000000	1\n\
element	Spout	10	100	10	0.000000	0.000000	1.000000	0.000000	1\n\
neighbor 0.75	Spout	Empty	1.0	Spout		Water\n\
neighbor 0.5	Spout	Sand	1.0	Empty		Empty\n\
element	Cera	238	221	204	0.000000	0.000000	1.000000	0.000000	1\n\
element	???	231	7	231	0.000000	1.000000	1.000000	0.000000	1\n\
self	0.100000	???	1.0000	Leftover???\n\
neighbor 1.0	???	Empty	1.0	???	???\n\
neighbor 1.0	???	Leftover???	1.0	Leftover???	Leftover???\n\
element	Oil	128	64	64	0.700000	1.000000	0.200000	0.000000	1\n\
element	Salt	255	255	255	0.900000	0.400000	0.900000	0.000000	1\n\
element	Ember	200	50	50	0.000000	0.200000	1.000000	0.000000	1\n\
self	0.002000	Ember	1.0000	Fire\n\
neighbor 0.2		Ember	Empty	1.0	Ember	Fire\n\
neighbor 0.9		Ember	Water	1.0	Empty	Steam\n\
neighbor 0.75		Ember	Oil	1.0	Ember	Fire\n\
neighbor 1.0		Ember	Plant	1.0	Ember	Fire\n\
neighbor 0.005		Ember	Cera	1.0	MoltenCera	Ember\n\
element	MoltenCera	255	220	200	0.800000	1.000000	1.000000	0.000000	0\n\
self	0.00001		MoltenCera	1.0	Cera\n\
element	Steam	85	85	255	-1.000000	1.000000	0.010000	0.000000	1\n\
self	0.001000	Steam	1.0000	Water\n\
element	Saltwater	0	0	150	0.700000	0.000000	0.500000	0.000000	1\n\
neighbor 0.004	Water	Salt	1.0	Saltwater		Saltwater\n\
neighbor 0.00001	Saltwater	Saltwater	1.0	Salt		Water\n\
neighbor 0.04		Saltwater	Water		1.0	Water		Saltwater\n\
neighbor 0.9		Saltwater	Fire		1.0	Salt		Steam\n\
element	Torch	100	0	0	0.000000	1.000000	1.000000	1.000000	1\n\
neighbor 0.2	Torch	Empty	1.0	Torch		Fire\n\
neighbor 0.2	Torch	Water	1.0	Torch		Steam\n\
neighbor 0.2	Torch	Saltwater	0.5	Torch		Steam	0.5	Torch	Salt\n\
neighbor 0.2	Torch	Oil	1.0	Torch		Fire\n\
neighbor 0.2	Torch	Plant	1.0	Torch		Fire\n\
neighbor 0.2	Torch	Cera	1.0	Torch		MoltenCera\n\
element	Leftover???	200	0	0	0.000000	0.000000	1.000000	0.000000	0\n\
self	0.005000	Leftover???	1.0000	Empty	\n\
";



wxString names[MAXNUMBEROFELEMENTS];
wxColor colors[MAXNUMBEROFELEMENTS];
wxPen pens[MAXNUMBEROFELEMENTS];

double gravity[MAXNUMBEROFELEMENTS];
double slip[MAXNUMBEROFELEMENTS];
double density[MAXNUMBEROFELEMENTS];
double conductivity[MAXNUMBEROFELEMENTS];
bool visible[MAXNUMBEROFELEMENTS];

double death_prob[MAXNUMBEROFELEMENTS];
char death_center[MAXNUMBEROFELEMENTS][100];

double trans_prob[MAXNUMBEROFELEMENTS][MAXNUMBEROFELEMENTS];        //Probability that something will happen.
char trans_center[MAXNUMBEROFELEMENTS][MAXNUMBEROFELEMENTS][100];   //Probability that that something will be this.
char trans_neighbor[MAXNUMBEROFELEMENTS][MAXNUMBEROFELEMENTS][100];

wxString groupNames[MAXNUMBEROFELEMENTS];
int groups[MAXNUMBEROFELEMENTS];
int numberOfGroups;

int numberOfElements;

MainFrame* g_mainFrame;
Canvas* g_canvas;
char data[MAXSIZE];
char energy[MAXSIZE];
unsigned char calc[MAXSIZE];
unsigned char bitmapdata[MAXSIZE*4];

unsigned int numberToDraw;
int draw_x[MAXSIZE];
int draw_y[MAXSIZE];

wxMenuItem* wallsCB;
wxMenuItem* sourcesCB;
wxMenuItem* drawCB;
wxMenuItem* gravityCB;
wxMenuItem* interactionsCB;
wxMenuItem* limitCB;

bool doWalls;
bool doSources;
bool doDraw;
bool doGravity;
bool doInteractions;
bool doLimit;

bool drawAll;
wxString sandboxFilename;
wxString physicsFilename;
wxTreeCtrl* penSelections;
bool pauseDrawing;
wxTimer* g_timer;
wxTimer* g_timer_second;
bool mouseIsDown;
int mousex, mousey;
char sand_type;
int pen_width;
wxStatusBar* statusbar;
double previous_now;
int g_width;
int g_height;


int frameCount;
double lastCalculateTime;


Canvas::Canvas(wxWindow* parent, wxWindowID id = -1, wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize) : wxWindow( parent, id, pos, size, wxCLIP_CHILDREN ){
  g_canvas = this;

  this->SetSizeHints(g_width,g_height,g_width,g_height);
  this->SetBackgroundColour(wxColor(_("BLACK")));
  sand_type = 1;
  pen_width = 32;
  lastCalculateTime = 0;
  numberToDraw = 0;
  drawAll = true;

  memset(data, 0, g_width*g_height*sizeof(char));
  memset(calc, false, g_width*g_height*sizeof(char));

  for(int i=0;i<MAXNUMBEROFELEMENTS;++i){
    for(int j=0;j<MAXNUMBEROFELEMENTS;++j){
      trans_prob[i][j] = 0;
      for(int k=0;k<100;++k){
	trans_center[i][j][k] = 0;
	trans_neighbor[i][j][k] = 0;
      }
    }
  }
}



bool inline checkForTransformation(int center, int other, int cx, int cy, int ox, int oy){
  char t1 = data[center];
  char t2 = data[other];
  if (trans_prob[t1][t2] == 0) return false;
  if (doWalls && (ox == 0 || ox == g_width-1 || oy == 0 || oy == g_height-1))
    return false;
  if (rand() < trans_prob[t1][t2]*RAND_MAX){
    int r = (int)round(100*(double(rand())/double(RAND_MAX)));
    data[center] = trans_center[t1][t2][r];
    data[other] = trans_neighbor[t1][t2][r];
    draw_x[numberToDraw] = cx;
    draw_y[numberToDraw] = cy;
    ++numberToDraw;
    draw_x[numberToDraw] = ox;
    draw_y[numberToDraw] = oy;
    ++numberToDraw;
    calc[center] = true;
    calc[other] = true;
    return true;
  }
  return false;
}



void calculate(){
  lastCalculateTime = wxDateTime::UNow().GetMinute()*60*1000 + wxDateTime::UNow().GetSecond()*1000 + wxDateTime::UNow().GetMillisecond();


  if (pauseDrawing == true)
    return;

  pauseDrawing = true;

  ++frameCount;

  /*
  if (doSources){
    int offset = g_width*50;
    int space = g_width/5;

    for(int i=0;i<10;++i){
      if (rand() < RAND_MAX/4){
	data[offset+space+i] = SAND;
	data[offset+2*space+i] = WATER;
	data[offset+3*space+i] = SALT;
	data[offset+4*space+i] = OIL;
      }
    }
  }
  */

  
  if (mouseIsDown){
    for(int dx=-pen_width/2;dx<pen_width/2;++dx){
      int x = mousex+dx;
      int dy = (int)round(sqrt(pen_width/2 - dx*dx));
      for(int y=mousey-dy;y<mousey+dy;++y){
	if (x > 0 && x < g_width && y > 0 && y < g_height){
	  data[(g_width*y)+x] = sand_type;
	  draw_x[numberToDraw] = x;
	  draw_y[numberToDraw] = y;
	  ++numberToDraw;
	}
      }
    }
  }
  


  for(int y=g_height-2;y>0;--y){
    for(int x=1;x<g_width-1;++x){

      int center = (g_width*y)+x;

      if (data[center] == 0)
	continue;
      if (calc[center])
	continue;
    
    
      if (death_prob[data[center]] > 0){
	if (rand() < death_prob[data[center]] * RAND_MAX){
	  int r = (int)round(100*(double(rand())/double(RAND_MAX)));
	  data[center] = death_center[data[center]][r];
	  draw_x[numberToDraw] = x;
	  draw_y[numberToDraw] = y;
	  ++numberToDraw;
	  continue;
	}
      }
    
      int left = center-1;
      int right = center+1;
      int up = center - g_width;
      int down = center + g_width;
      int downleft = center + g_width - 1;
      int downright = center + g_width + 1;
      int upleft = center - g_width - 1;
      int upright = center - g_width + 1;
    
      if (doInteractions){
	//Check for specific transformations.
	//Randomly check neighbors.
	if (rand() < 0.5*RAND_MAX){
	
	  if (rand() < 0.5*RAND_MAX){

	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, left, x, y, x-1, y)) continue;
	      if(checkForTransformation(center, right, x, y, x+1, y)) continue;
	    }
	    else{
	      if(checkForTransformation(center, right, x, y, x+1, y)) continue;
	      if(checkForTransformation(center, left, x, y, x-1, y)) continue;
	    }
	  
	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, down, x, y, x, y+1)) continue;
	      if(checkForTransformation(center, up, x, y, x, y-1)) continue;
	    }
	    else{
	      if(checkForTransformation(center, up, x, y, x, y-1)) continue;
	      if(checkForTransformation(center, down, x, y, x, y+1)) continue;
	    }

	  }
	  else{

	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, down, x, y, x, y+1)) continue;
	      if(checkForTransformation(center, up, x, y, x, y-1)) continue;
	    }
	    else{
	      if(checkForTransformation(center, up, x, y, x, y-1)) continue;
	      if(checkForTransformation(center, down, x, y, x, y+1)) continue;
	    }
	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, left, x, y, x-1, y)) continue;
	      if(checkForTransformation(center, right, x, y, x+1, y)) continue;
	    }
	    else{
	      if(checkForTransformation(center, right, x, y, x+1, y)) continue;
	      if(checkForTransformation(center, left, x, y, x-1, y)) continue;
	    }

	  }

	}
	else{

	  if (rand() < 0.5*RAND_MAX){

	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, downleft, x, y, x-1, y+1)) continue;
	      if(checkForTransformation(center, upright, x, y, x+1, y-1)) continue;
	    }
	    else{
	      if(checkForTransformation(center, upright, x, y, x+1, y-1)) continue;
	      if(checkForTransformation(center, downleft, x, y, x-1, y+1)) continue;
	    }
	  
	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, upleft, x, y, x-1, y-1)) continue;
	      if(checkForTransformation(center, downright, x, y, x+1, y+1)) continue;
	    }
	    else{
	      if(checkForTransformation(center, downright, x, y, x+1, y+1)) continue;
	      if(checkForTransformation(center, upleft, x, y, x-1, y-1)) continue;
	    }

	  }
	  else{

	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, upleft, x, y, x-1, y-1)) continue;
	      if(checkForTransformation(center, downright, x, y, x+1, y+1)) continue;
	    }
	    else{
	      if(checkForTransformation(center, downright, x, y, x+1, y+1)) continue;
	      if(checkForTransformation(center, upleft, x, y, x-1, y-1)) continue;
	    }

	    if (rand() < 0.5*RAND_MAX){
	      if(checkForTransformation(center, downleft, x, y, x-1, y+1)) continue;
	      if(checkForTransformation(center, upright, x, y, x+1, y-1)) continue;
	    }
	    else{
	      if(checkForTransformation(center, upright, x, y, x+1, y-1)) continue;
	      if(checkForTransformation(center, downleft, x, y, x-1, y+1)) continue;
	    }

	  }

	}
      }
    
    
    
      if (doGravity){
      
	if (gravity[data[center]] == 0)
	  continue;
      
	if (gravity[data[center]] > 0){
	  //Check down
	  //falling
	  if (!doWalls || y < g_height-2){
	    if (data[down] == 0){
	      if (rand() < gravity[data[center]]*RAND_MAX){
		found(center, down, x, y, x, y+1);
		continue;
	      }
	    }
	    else{
	      //density
	      if (density[data[center]] > density[data[down]]){
		if (rand() < gravity[data[center]]*0.5*RAND_MAX ){
		  found(center, down, x, y, x, y+1);
		  continue;
		}
	      }
	    }
	  }

	  if (rand() < 0.5*RAND_MAX){ //left versus right.
	    if (!doWalls || (y < g_height-2 && x > 1)){
	      //Check down and left
	      //falling
	      if (data[downleft] == 0){
		if (data[down] == 0){//no friction.
		  if (rand() < LEFTRIGHTPROB*gravity[data[center]]*RAND_MAX){
		    found(center, downleft, x, y, x-1, y+1);
		    continue;
		  }
		}
		else{
		  if (rand() < LEFTRIGHTPROB*gravity[data[center]]*slip[data[center]]*RAND_MAX){
		    found(center, downleft, x, y, x-1, y+1);
		    continue;
		  }
		}
	      }
	      else{
		//density
		if (density[data[center]] > density[data[downleft]]){
		  if ( rand() < LEFTRIGHTPROB*gravity[data[center]]*0.5*RAND_MAX ){
		    found(center, downleft, x, y, x-1, y+1);
		    continue;
		  }
		}
	      }
	    }
	  }
	  else{//left versus right.
	    if (!doWalls || (y < g_height-2 && x < g_width-2)){
	      //Check down and right
	      //falling
	      if (data[downright] == 0){
		if (data[down] == 0){//no friction.
		  if (rand() < LEFTRIGHTPROB*gravity[data[center]]*RAND_MAX){
		    found(center, downright, x, y, x+1, y+1);
		    continue;
		  }
		}
		else{
		  if (rand() < LEFTRIGHTPROB*slip[data[center]]*gravity[data[center]]*RAND_MAX){
		    found(center, downright, x, y, x+1, y+1);
		    continue;
		  }
		}
	      }
	      else{
		//density
		if (density[data[center]] > density[data[downright]]){
		  if ( rand() < LEFTRIGHTPROB*gravity[data[center]]*0.5*RAND_MAX ){
		    found(center, downright, x, y, x+1, y+1);
		    continue;
		  }
		}
	      }
	    }
	  }
	}
	else{
	  //gravity < 0

	  //up or to the side?
	  if (rand() < 0.7*RAND_MAX){
	    if (!doWalls || y > 1){
	      //Check up
	      if (data[up] == 0){
		if (rand() < -gravity[data[center]]*RAND_MAX){
		  found(center, up, x, y, x, y-1);
		  continue;
		}
	      }
	    }
	  }
	  else{
	    //Check up and left.
	    if (rand() < 0.5*RAND_MAX){ //left versus right.
	      if (!doWalls || (y > 1 && x > 1)){
		//floating
		if (data[upleft] == 0){
		  if (rand() < -gravity[data[center]]*RAND_MAX){
		    found(center, upleft, x, y, x-1, y-1);
		    continue;
		  }
		}
	      }
	    }
	    else{	   //left versus right.
	      if (!doWalls || (y > 1 && x < g_width-2)){
		//Check up and right.
		if (data[upright] == 0){
		  if (rand() < -gravity[data[center]]*RAND_MAX){
		    found(center, upright, x, y, x+1, y-1);
		    continue;
		  }
		}
	      }
	    }
	  }
	}

    
	if (rand() < 0.5*RAND_MAX){ //left versus right.
	  //Check left.
	  if (!doWalls || x > 1){
	    //Random opening.
	    if (data[left] == 0){
	      if (rand() < LEFTRIGHTPROB*gravity[data[center]]*RAND_MAX){
		found(center, left, x, y, x-1, y);
		continue;
	      }
	      
	      //Check for being pushed to the side.
	      if (data[right] > 0){
		if (rand() < LEFTRIGHTPROB*slip[data[center]]*RAND_MAX){
		  found(center, left, x, y, x-1, y);
		  continue;
		}
		continue;
	      }
	    }
	    
	    //density
	    if (density[data[center]] > density[data[left]]){
	      if ( rand() < 0.001*0.5*RAND_MAX ){
		found(center, left, x, y, x-1, y);
		continue;
	      }
	    }
	  }
	}
	else{
	  //Check right.
	  if (!doWalls || x < g_width-2){
	    //Random opening.
	    if (data[right] == 0){
	      if (rand() < LEFTRIGHTPROB*gravity[data[center]]*RAND_MAX){
		found(center, right, x, y, x+1, y);
		continue;
	      }
	      
	      //Check for being pushed to the side.
	      if (data[left] > 0){
		if (rand() < LEFTRIGHTPROB*slip[data[center]]*RAND_MAX){
		  found(center, right, x, y, x+1, y);
		  continue;
		}
		continue;
	      }
	    }
	    
	    //density
	    if (density[data[center]] > density[data[right]]){
	      if ( rand() < 0.001*0.5*RAND_MAX ){
		found(center, right, x, y, x+1, y);
		continue;
	      }
	    }
	  }
	}
      }
    }
  }

  pauseDrawing = false;
  g_canvas->Refresh();
}


void Canvas::Refresh(){
  ::wxYield();
  if (pauseDrawing == true)
    return;
  pauseDrawing = true;

  for(int x=0;x<g_width;++x){
    data[(0*g_width)+x] = 0;
    data[((g_height-1)*g_width)+x] = 0;
    draw_x[numberToDraw] = x;
    draw_y[numberToDraw] = 0;
    ++numberToDraw;
    draw_x[numberToDraw] = x;
    draw_y[numberToDraw] = g_height-1;
    ++numberToDraw;
  }
  for(int y=0;y<g_height;++y){
    data[(y*g_width)] = 0;
    data[(y*g_width)+g_width-1] = 0;
    draw_x[numberToDraw] = 0;
    draw_y[numberToDraw] = y;
    ++numberToDraw;
    draw_x[numberToDraw] = g_width-1;
    draw_y[numberToDraw] = y;
    ++numberToDraw;
  }


  if (doDraw){
    wxClientDC dc(this);
    dc.BeginDrawing();
    
    if (drawAll){
      for(int center=0;center<g_width*g_height;++center){
	bitmapdata[(center*3)+0] = colors[data[center]].Red();
	bitmapdata[(center*3)+1] = colors[data[center]].Green();
	bitmapdata[(center*3)+2] = colors[data[center]].Blue();
      }
      drawAll = false;
    }
    else{
      for(int i=0;i<numberToDraw;++i){
	int center = (draw_y[i]*g_width)+draw_x[i];
	bitmapdata[(center*3)+0] = colors[data[center]].Red();
	bitmapdata[(center*3)+1] = colors[data[center]].Green();
	bitmapdata[(center*3)+2] = colors[data[center]].Blue();
      }
    }
    

    wxMemoryDC memdc;
    wxImage image(g_width, g_height, bitmapdata, true);
    wxBitmap bmp(image);
    memdc.SelectObject(bmp);
    dc.Blit(0,0,g_width, g_height, &memdc, 0, 0);

    
    dc.EndDrawing();
  }

  numberToDraw = 0;
  memset(calc, false, g_width*g_height*sizeof(unsigned char));

  pauseDrawing = false;
}

void Canvas::OnIdle(wxIdleEvent& e){
  if (doLimit == false){
    calculate();
    e.RequestMore(true);
    ::wxWakeUpIdle();
  }
}

void Canvas::OnTimer(wxTimerEvent& event){
  if (doLimit == true){
    calculate();
  }
}

void Canvas::OnSecondTimer(wxTimerEvent& event){
  wxString str = _("");
  str.Printf("%d fps", frameCount);
  frameCount = 0;
  statusbar->SetStatusText(str);
}

void Canvas::OnEraseBG(wxEraseEvent& e){
  //Do absolutely nothing, thereby halting this event.
  //memset(draw, true, g_width*g_height*sizeof(unsigned char));
  drawAll = true;
  this->Refresh();
}


void drawCircle(int centerx, int centery){
  for(int dx=-pen_width/2;dx<pen_width/2;++dx){
    int x = centerx+dx;
    int dy = (int)round(sqrt(pen_width/2 - dx*dx));
    for(int y=centery-dy;y<centery+dy;++y){
      if (x > 0 && x < g_width && y > 0 && y < g_height){
	data[(g_width*y)+x] = sand_type;
	draw_x[numberToDraw] = x;
	draw_y[numberToDraw] = y;
	++numberToDraw;
      }
    }
  }
}

void drawLine(int x2, int y2, int x1, int y1){
  int dx = x1 - x2;
  int dy = y1 - y2;

  int t_max = abs(dx);
  if (abs(dy) > abs(dx))
    t_max = abs(dy);

  if (t_max == 0){
    drawCircle(x1, y1);
  }
  else{
    double ddx = double(dx)/double(t_max);
    double ddy = double(dy)/double(t_max);
    
    double x=x2;
    double y=y2;
    for(int t=0;t<=t_max;++t){
      drawCircle((int)round(x),(int)round(y));
      x += ddx;
      y += ddy;
    }
  }
}

void Canvas::OnMouseLeftDown(wxMouseEvent& event){
  mouseIsDown = true;
  mousex = event.GetX();
  mousey = event.GetY();

  drawCircle(mousex, mousey);
}

void Canvas::OnMouseLeftUp(wxMouseEvent& event){
  mouseIsDown = false;
}


void Canvas::OnMouseRightDown(wxMouseEvent& event){
  int oldmousex = mousex;
  int oldmousey = mousey;

  mousex = event.GetX();
  mousey = event.GetY();

  drawLine(oldmousex, oldmousey, mousex, mousey);

}

void Canvas::OnMouseMove(wxMouseEvent& event){
  if (event.LeftIsDown()){
    int oldmousex = mousex;
    int oldmousey = mousey;

    mousex = event.GetX();
    mousey = event.GetY();


    drawLine(oldmousex, oldmousey, mousex, mousey);
  }
  else
    mouseIsDown = false;
}

void Canvas::OnPaint(wxPaintEvent& event){
  //memset(draw, true, g_width*g_height*sizeof(unsigned char));
  drawAll = true;
  this->Refresh();
}


bool OnNew(wxWindow* window){
  //New
  wxString choices[] = {_("320x240 (fastest)"), _("640x480"), _("800x600"), _("1024x768"), _("1280x1024 (slowest)")};
  wxSingleChoiceDialog dialog(NULL, _("Select an area size"), _("New"), 5, choices, NULL);
  
  if (dialog.ShowModal() == wxID_OK){
    if (dialog.GetSelection() == 0){
      g_width = 320;
      g_height = 240;
    }
    else if (dialog.GetSelection() == 1){
      g_width = 640;
      g_height = 480;
    }
    else if (dialog.GetSelection() == 2){
      g_width = 800;
      g_height = 600;
    }
    else if (dialog.GetSelection() == 3){
      g_width = 1024;
      g_height = 768;
    }
    else if (dialog.GetSelection() == 4){
      g_width = 1280;
      g_height = 1024;
    }
    return true;
  }

  return false;
}


void RefreshPenList(){

  penSelections->DeleteAllItems();
  wxTreeItemId id1 = penSelections->AddRoot(_("Elements"));

  for(int i=0;i<numberOfElements;++i){
    std::cout << penSelections->AppendItem(id1, names[i]) << "\n";
  }

  //penSelections->SelectItem();
}


MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, 
		     long style) : wxFrame(NULL, -1, title, pos, size, style){
  g_mainFrame = this;

  wxMenu* fileMenu = new wxMenu;
  wxMenu* optionsMenu = new wxMenu;
  wxMenu* helpMenu = new wxMenu;
  
  fileMenu->Append(1050, _("New.."), _(""));
  fileMenu->AppendSeparator();
  fileMenu->Append(1051, _("Load sandbox.."), _(""));
  fileMenu->Append(1052, _("Save sandbox.."), _(""));
  fileMenu->AppendSeparator();
  fileMenu->Append(1055, _("Load physics.."), _(""));
  fileMenu->Append(1056, _("Save physics.."), _(""));
  fileMenu->AppendSeparator();
  fileMenu->Append(1053, _("Exit"), _(""));

  wallsCB = new wxMenuItem(optionsMenu, 1002, _("Boundary Walls"), _(""), wxITEM_CHECK);
  sourcesCB = new wxMenuItem(optionsMenu, 1003, _("Sources"), _(""), wxITEM_CHECK);
  drawCB = new wxMenuItem(optionsMenu, 1004, _("Paint Updates"), _(""), wxITEM_CHECK);
  gravityCB = new wxMenuItem(optionsMenu, 1005, _("Gravity"), _(""), wxITEM_CHECK);
  interactionsCB = new wxMenuItem(optionsMenu, 1006, _("Element Interactions"), _(""), wxITEM_CHECK);
  limitCB = new wxMenuItem(optionsMenu, 1007, _("Limit FPS"), _(""), wxITEM_CHECK);

  optionsMenu->Append(wallsCB);
  optionsMenu->Append(sourcesCB);
  optionsMenu->Append(drawCB);
  optionsMenu->Append(gravityCB);
  optionsMenu->Append(interactionsCB);
  optionsMenu->Append(limitCB);

  helpMenu->Append(1054, _("About"), _(""));

  wxMenuBar* menuBar = new wxMenuBar();
  menuBar->Append(fileMenu, _("File"));
  menuBar->Append(optionsMenu, _("Options"));
  menuBar->Append(helpMenu, _("Help"));

  SetMenuBar(menuBar);

  wallsCB->Check();
  sourcesCB->Check();
  drawCB->Check();
  gravityCB->Check();
  interactionsCB->Check();
  limitCB->Check();

  doWalls = wallsCB->IsChecked();
  doSources = sourcesCB->IsChecked();
  doDraw = drawCB->IsChecked();
  doGravity = gravityCB->IsChecked();
  doInteractions = interactionsCB->IsChecked();
  doLimit = limitCB->IsChecked();

  statusbar = this->CreateStatusBar();
  statusbar->Show(true);

  g_canvas = new Canvas(this, -1, wxDefaultPosition, wxSize(g_width, g_height));
  
  wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
  {
    wxFlexGridSizer* RHSizer = new wxFlexGridSizer(2);
    {
      
      const wxString labels2[10] = {_("1"), _("2"), _("4"), _("8"), _("16"), _("32"), _("64"), _("128"), _("256"), _("512")};

      penSelections = new wxTreeCtrl(this, 1000, wxDefaultPosition, wxDefaultSize, wxTR_SINGLE);
      wxListBox* lb2= new wxListBox(this, 1001, wxDefaultPosition, wxDefaultSize, 10, labels2, wxLB_SINGLE);

      lb2->SetSelection(4);
      
      RHSizer->Add(new wxStaticText(this, -1, _("Element")));
      RHSizer->Add(new wxStaticText(this, -1, _("Pen Size")));
      RHSizer->Add(penSelections, 0, wxEXPAND);
      RHSizer->Add(lb2, 0, wxEXPAND);

      RHSizer->Add(new wxButton(this, 1025, _("Refresh\nReload"), wxDefaultPosition, wxSize(75,75)), 0, wxCENTER | wxALL, 25);
    }

    sizer->Add(g_canvas, 0, wxADJUST_MINSIZE|wxALL, 10);
    sizer->Add(RHSizer, 0);
  }
  this->SetSizer(sizer);
  
  this->Layout();
  sizer->Fit(this);

  pauseDrawing = false;

  g_timer = new wxTimer(g_canvas, 999);
  g_timer->Start(TIMERINTERVAL);

  g_timer_second = new wxTimer(g_canvas, 998);
  g_timer_second->Start(1000);

}

void loadSandbox(wxString filename){
  wxImage loadBitmap;
  loadBitmap.LoadFile(filename);
  g_width = loadBitmap.GetWidth();
  g_height = loadBitmap.GetHeight();
  drawAll = true;

  for(int y=0;y<g_height;++y){
    for(int x=0;x<g_width;++x){
      int i = (y*g_width)+x;
      
      data[i] = 0;
      
      //Find the closest.
      unsigned int smallestDistance = 256+256+256;
      
      unsigned char red = loadBitmap.GetRed(x,y);
      unsigned char green = loadBitmap.GetGreen(x,y);
      unsigned char blue = loadBitmap.GetBlue(x,y);
      
      if (red == 0 && green == 0 && blue == 0)
	continue;
      
      for(int j=0;j<numberOfElements;++j){
	if (j == 8)
	  continue;
	unsigned int dist = 
	  int(abs(red - colors[j].Red())) + 
	  int(abs(green - colors[j].Green())) + 
	  int(abs(blue - colors[j].Blue()));
	if (dist < smallestDistance){
	  smallestDistance = dist;
	  data[i] = j;
	}
      }
    }
  }
  
  g_canvas->SetSizeHints(g_width,g_height,g_width,g_height);
  //g_canvas->SetClientSize(g_width,g_height);
  g_mainFrame->Layout();
  g_mainFrame->GetSizer()->Fit(g_mainFrame);

  //g_canvas->Refresh();
}

void loadPhysics(wxString filename){
  for(int i=0;i<MAXNUMBEROFELEMENTS;++i){
    for(int j=0;j<MAXNUMBEROFELEMENTS;++j){
      trans_prob[i][j] = 0;
      for(int k=0;k<100;++k){
	trans_center[i][j][k] = 0;
	trans_neighbor[i][j][k] = 0;
      }
    }
  }

  FILE* file = fopen(filename, "r");
  char line[200];

  //First, count the number of elements and get their names.
  int groupNumber = -1;
  int elementNumber = -1;
  while(fgets(line, 200, file) != NULL){
    if (line[0] == '#' || line[0] == ' ' || line[0] == '\t' || line[0] == '\n' || line[0] == '\r'){
      //skip.
    }
    else{
      char* type = strtok(line, " \t\n\r");
      if (strcmp(type, "element") == 0){
	++elementNumber;
	char* name = strtok(NULL, " \t\n\r");
	names[elementNumber] = wxString(name);
      }
      else if (strcmp(type, "group") == 0){
	++groupNumber;
	char name[128];
	sscanf(line, "%s%s", type, name);
	groupNames[groupNumber] = wxString(name);
      }
    }
  }

  numberOfElements = elementNumber + 1;
  numberOfGroups = groupNumber + 1;
  rewind(file);


  //Now fill in the elements.
  elementNumber = -1;
  while(fgets(line, 200, file) != NULL){
    if (line[0] == '#' || line[0] == ' ' || line[0] == '\t' || line[0] == '\n' || line[0] == '\r'){
      //skip.
    }
    else{
      char type[128];
      sscanf(line, "%s", type);

      if (strcmp(type, "element") == 0){
	++elementNumber;
	char name[128];
	int red;
	int green;
	int blue;
	float g,s,d,c;
	int v;
	
	sscanf(line, "%s%s%d%d%d%f%f%f%f%d", 
	       &type, &name, &red, &green, &blue, 
	       &g, &s, &d, &c, &v);
	
	colors[elementNumber] = wxColour(red, green, blue);
	pens[elementNumber] = wxPen(colors[elementNumber], 1, wxSOLID);
	
	gravity[elementNumber] = (double)g;
	slip[elementNumber] = (double)s;
	density[elementNumber] = (double)d;
	conductivity[elementNumber] = (double)c;
	visible[elementNumber] = (bool)v;
      }
      else if (strcmp(type, "group") == 0){
	char* gar = strtok(line, " \t\n\r");
	char* group = strtok(NULL, " \t\n\r");
	
	int n_group = -1;
	for(int i=0;i<numberOfGroups;++i){
	  if (groupNames[i] == wxString(group))
	    n_group = i;
	}
	
	
	char* element = strtok(NULL, " \t\n\r");
	while(element != NULL){
	  int n_element = -1;
	  for(int i=0;i<numberOfElements;++i){
	    if (names[i] == wxString(element))
	      n_element = i;
	  }
	  
	  if (n_element != -1 && n_group != -1)
	    groups[n_element] = n_group;
	
	  element = strtok(NULL, " \t\n\r");
	}

      }
      else if (strcmp(type, "self") == 0){
	//deathrate. "self [prob] [element] [prob][element] ..."
	char* gar = strtok(line, " \t\n\r");
	char* thisprob = strtok(NULL, " \t\n\r");
	char* center = strtok(NULL, " \t\n\r");

	float n_thisprob = 0;
	sscanf(thisprob, "%f", &n_thisprob);

	int n_center = -1;
	for(int i=0;i<numberOfElements;++i){
	  if (names[i] == wxString(center))
	    n_center = i;
	}
	
	char* prob = strtok(NULL, " \t\n\r");

	int currentProbIndex = 0;
	while(prob != NULL){
	  char* s_trans_center = strtok(NULL, " \t\n\r");
	  
	  float n_prob;
	  sscanf(prob, "%f", &n_prob);

	  int n_trans_center = -1;
	  for(int i=0;i<numberOfElements;++i){
	    if (names[i] == wxString(s_trans_center))
	      n_trans_center = i;
	  }
	  
	  if (n_center != -1 && n_trans_center != -1){
	    //Fill the probability array.
	    for(int i=currentProbIndex; i < 100 && i < currentProbIndex + int(round(100.0*n_prob)); ++i){
	      death_center[n_center][i] = n_trans_center;
	    }
	    currentProbIndex = currentProbIndex + int(round(100.0*n_prob));

	    death_prob[n_center] = (double)n_thisprob; //Just stuck here for simplicity.
	  }

	  prob = strtok(NULL, " \t\n\r");
	}

	for(int i=currentProbIndex; i < 100;++i){
	  death_center[n_center][i] = n_center;
	}
      }
      else if (strcmp(type, "neighbor") == 0){
	//neighbor [prob] [element] [element] [prob][element][element] ...
	//Reading element interactions.
	char* gar = strtok(line, " \t\n\r");
	char* thisprob = strtok(NULL, " \t\n\r");
	char* center = strtok(NULL, " \t\n\r");
	char* neighbor = strtok(NULL, " \t\n\r");
	
	float n_thisprob = 0;
	sscanf(thisprob, "%f", &n_thisprob);
	

	int n_center = -1;
	int n_neighbor = -1;
	for(int i=0;i<numberOfElements;++i){
	  if (names[i] == wxString(center))
	    n_center = i;
	  if (names[i] == wxString(neighbor))
	    n_neighbor = i;
	}

	char* prob = strtok(NULL, " \t\n\r");

	int currentProbIndex = 0;
	while(prob != NULL){
	  char* s_trans_center = strtok(NULL, " \t\n\r");
	  char* s_trans_neighbor = strtok(NULL, " \t\n\r");

	  float n_prob;
	  sscanf(prob, "%f", &n_prob);
	
	  int n_trans_center = -1;
	  int n_trans_neighbor = -1;
	  for(int i=0;i<numberOfElements;++i){
	    if (names[i] == wxString(s_trans_center))
	      n_trans_center = i;
	    if (names[i] == wxString(s_trans_neighbor))
	      n_trans_neighbor = i;
	  }

	  if (n_neighbor != -1 && n_center != -1 && n_trans_center != -1 && n_trans_neighbor){
	    //Fill the probability array.
	    for(int i=currentProbIndex; i < 100 && i < currentProbIndex + int(round(100.0*n_prob)); ++i){
	      trans_center[n_center][n_neighbor][i] = n_trans_center;
	      trans_neighbor[n_center][n_neighbor][i] = n_trans_neighbor;
	    }
	    currentProbIndex = currentProbIndex + int(round(100.0*n_prob));

	    trans_prob[n_center][n_neighbor] = (double)n_thisprob; //Just stuck here for simplicity.
	  }
	  
	  prob = strtok(NULL, " \t\n\r");
	}
      }
    }
  }
  
  
  
  //Update selections.
  RefreshPenList();
  
  fclose(file);

}

void MainFrame::OnMenu(wxCommandEvent& event){
  pauseDrawing = true;

  if (event.GetId() == 1050){
    OnNew(this);

    drawAll = true;
    //memset(draw, true, g_width*g_height*sizeof(unsigned char));
    memset(data, 0, g_width*g_height*sizeof(char));

    g_canvas->SetSizeHints(g_width,g_height,g_width,g_height);
    //g_canvas->SetClientSize(g_width,g_height);
    g_mainFrame->Layout();
    g_mainFrame->GetSizer()->Fit(g_mainFrame);
    
    g_canvas->Refresh();
  }
  else if(event.GetId() == 1051){
    //Load
    wxFileDialog dialog(this, _("Load from a file"), _(""), _(""), _("Any Image Files (*.*)|*.*"), wxOPEN);
    
    if (dialog.ShowModal() == wxID_OK){
      sandboxFilename = dialog.GetPath();
      loadSandbox(sandboxFilename);
    }
  }
  else if(event.GetId() == 1052){
    //Save
    wxFileDialog dialog(this, _("Save to a file"), _(""), _(""), _("PNG files (*.png)|*.png|BMP files (*.bmp)|*.bmp"), wxSAVE);
    
    if (dialog.ShowModal() == wxID_OK){
      wxString filename = dialog.GetPath();
      
      for(int i=0;i<g_width*g_height;++i){
	bitmapdata[(i*3)] = colors[data[i]].Red();
	bitmapdata[(i*3)+1] = colors[data[i]].Green();
	bitmapdata[(i*3)+2] = colors[data[i]].Blue();
      }
      wxImage saveBitmap(g_width, g_height, bitmapdata, true);
      wxBitmap bmp(saveBitmap);

      if (dialog.GetFilterIndex() == 0 || filename.find(_(".png")) != -1 || filename.find(_(".PNG")) != -1){
	bmp.SaveFile(filename, wxBITMAP_TYPE_PNG);
      }
      else{
	bmp.SaveFile(filename, wxBITMAP_TYPE_BMP);
      }
    }
  }
  else if(event.GetId() == 1053){
    //Exit
    g_mainFrame->Close();
  }
  else if(event.GetId() == 1054){
    //About
    wxString str = "Owen Piette's Falling Sand Game\nCopyright Owen Piette 2006.\nThank you to Troy Larson for the refresh button idea.\n\n";
    str << "Version " << VERSION << "-" << CVERSION << "\n" << "Check for updates at:\nhttp://www.piettes.com/fallingsandgame/";
    wxMessageDialog dlg(this, str, _("About"));
    dlg.ShowModal();
  }
  else if(event.GetId() == 1055){
    //Load physics

    wxFileDialog dialog(this, _("Load from a file"), _(""), _(""), _("Physics Files (*.txt)|*.txt"), wxOPEN);
    if (dialog.ShowModal() == wxID_OK){
      physicsFilename = dialog.GetPath();
      loadPhysics(physicsFilename);
    }
  }
  else if(event.GetId() == 1056){
    //Save physics

    wxFileDialog dialog(this, _("Save to a file"), _(""), _(""), _("Physics files (*.txt)|*.txt"), wxSAVE);
    
    if (dialog.ShowModal() == wxID_OK){
      wxString filename = dialog.GetPath();
      FILE* file = fopen(filename, "w");

      fprintf(file, "#wxSand: Owen Piette's Falling Sand Game\n#Version %s-%s, File subversion 2\n", VERSION, CVERSION);
      
      //groups
      for(int i=0;i<numberOfGroups; ++i){
	fprintf(file, "group\t%s\t", groupNames[i].c_str());
	for(int j=0;j<numberOfElements;++j){
	  if (groups[j] == i){
	    fprintf(file, "%s\t", names[j].c_str());
	  }
	}
	fprintf(file, "\n");
      }

      for(int i=0;i<numberOfElements;++i){
	//element
	fprintf(file, "element\t%s\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\n", 
		names[i].c_str(), colors[i].Red(), colors[i].Green(), colors[i].Blue(), 
		gravity[i], slip[i], density[i], conductivity[i], (int)visible[i]);

	//deathrate. "self [prob] [element] [prob][element] ..."
	if (death_prob[i] > 0){
	  fprintf(file, "self\t%f\t%s\t", death_prob[i], names[i].c_str());
	  int sum = 1;
	  int prev_index = death_center[i][0];
	  for(int k=1;k<100;++k){
	    if (prev_index == death_center[i][k] && k != 99){
	      ++sum;
	    }
	    else{
	      fprintf(file, "%f\t%s\t", double(sum)/100.0, names[death_center[i][k-1]].c_str());
	      sum = 1;
	    }
	    prev_index = death_center[i][k];
	  }
	  fprintf(file, "\n");
	}

	//Neighbors
	for(int j=0;j<numberOfElements;++j){
	  //neighbor [prob] [element] [element] [prob][element][element] ...
	  if (trans_prob[i][j] != 0){
	    fprintf(file, "neighbor\t%f\t%s\t%s\t", trans_prob[i][j], names[i].c_str(), names[j].c_str());
	    int sum = 1;
	    int prev_index = trans_center[i][j][0];
	    for(int k=1;k<100;++k){
	      if (prev_index == trans_center[i][j][k] && k != 99){
		++sum;
	      }
	      else{
		fprintf(file, "%f\t%s\t%s\t", double(sum)/100.0, names[trans_center[i][j][k-1]].c_str(), 
			names[trans_neighbor[i][j][k-1]].c_str());
		sum = 1;
	      }
	      prev_index = trans_center[i][j][k];
	    }
	    fprintf(file, "\n");
	  }
	}
	
      }
      
      fprintf(file, "\n");
      fclose(file);
      
    }

  }

  doWalls = wallsCB->IsChecked();
  doSources = sourcesCB->IsChecked();
  doDraw = drawCB->IsChecked();
  doGravity = gravityCB->IsChecked();
  doInteractions = interactionsCB->IsChecked();
  doLimit = limitCB->IsChecked();

  pauseDrawing = false;

}

void MainFrame::OnButton(wxCommandEvent& event){
  if (event.GetId() == 1025){
    if(sandboxFilename == _("")){
      //Refresh
      drawAll = true;
      memset(data, 0, g_width*g_height*sizeof(unsigned char));
      //memset(draw, true, g_width*g_height*sizeof(unsigned char));
    }
    else{
      //reload
      loadSandbox(sandboxFilename);
    }
    
    if (physicsFilename != _("")){
      loadPhysics(physicsFilename);
    }
  }
}

void MainFrame::OnElementChoice(wxTreeEvent& event){
  wxTreeItemId id = event.GetItem();


  
  //TODO: Do something
}

void MainFrame::OnChoice(wxCommandEvent& event){
  pen_width = 2*(int)pow(2,(event.GetSelection()));
}

void MainFrame::OnSize(wxSizeEvent& event){
  drawAll = true;
  //memset(draw, true, g_width*g_height*sizeof(unsigned char));
  g_mainFrame->Layout();
}

void MainFrame::OnMove(wxFocusEvent& event){
  drawAll = true;
  //memset(draw, true, g_width*g_height*sizeof(unsigned char));
  if (g_canvas){
    g_canvas->Refresh();
  }
}


bool DropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames){
  
  if (filenames[0].First(_(".txt")) != -1 || filenames[0].First(_(".TXT")) != -1 ){
    loadPhysics(filenames[0]);
  }
  else{
    loadSandbox(filenames[0]);
  }

  if (filenames.GetCount() == 2){
    if (filenames[1].First(_(".txt")) != -1 || filenames[1].First(_(".TXT")) != -1 ){
      loadPhysics(filenames[1]);
    }
    else{
      loadSandbox(filenames[1]);
    }
  }

  return true;
}


/* this is executed upon startup, like 'main()' in non-wxWidgets programs */
bool Sand::OnInit()
{
  g_mainFrame = NULL;
  g_canvas = NULL;
  mouseIsDown = false;
  statusbar = NULL;
  g_width = 640;
  g_height = 480;
  sandboxFilename = _("");
  physicsFilename = _("");

  numberOfGroups = 0;
  numberOfElements=17;

  ::wxInitAllImageHandlers();

  wxString str = _("Owen Piette's Falling Sand Game");
  wxFrame *frame = new MainFrame(str, wxDefaultPosition, wxDefaultSize);//, wxCLOSE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN);
  frame->Show(TRUE);
  //SetTopWindow(frame);


  FILE* file = fopen("default.txt", "w");
  fprintf(file, defaultFileContents);
  fclose(file);

  if (this->argc > 1){
    sandboxFilename = wxString(argv[1]);
    loadSandbox(sandboxFilename);
    if (this->argc > 2){
      physicsFilename = wxString(argv[2]);
      loadPhysics(physicsFilename);
    }
  }
  else{
    physicsFilename = _("default.txt");
    loadPhysics(physicsFilename);
  }
  
  g_mainFrame->SetDropTarget(new DropTarget());

  return true;
}
