#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <plotter.h>

class Lattice{
  int N;                                // size of the lattice is NxN
  std::vector<std::vector<bool> > site; // matrix of positions
  std::pair<int,int> tpos, ppos;        // temporary and previous position of the particle
  static const int MAXRGB = 65535;
public:
  Lattice(int N_);
  void ReleaseNew();
  int MakeStep();
  void Draw(Plotter& plotter, int pixsize);
  void DrawAStep(Plotter& plotter, int pixsize);
};

Lattice::Lattice(int N_): N(N_), site(N)
{
  for (int i=0; i<site.size(); i++){
    site[i].resize(N);
    for (int j=0; j<site[i].size(); j++){
      site[i][j]=false;
    }
  }
  // We put first point in the middle of the system
  site[N/2][N/2]=true;
};

void Lattice::ReleaseNew()
{ // Random walker is released from the boundary
  do{
    int istart = static_cast<int>(drand48()*4*N);
    switch(istart/N){
    case 0: tpos = std::make_pair(istart,0); break;
    case 1: tpos = std::make_pair(N-1,istart%N); break;
    case 2: tpos = std::make_pair(istart%N,N-1); break;
    case 3: tpos = std::make_pair(0,istart%N); break;
    }
  } while (site[tpos.first][tpos.second]);// just make sure that the site is empty
}

int Lattice::MakeStep()
{// Return codes: 1 - particle glued
  //              0 - particle moved
  std::pair<int,int> newpos(tpos);
  int ipos = static_cast<int>(drand48()*4);
  switch(ipos){
  case 0: newpos.first  = (tpos.first+1)%N;   break;
  case 1: newpos.second = (tpos.second+1)%N;  break;
  case 2: newpos.first  = tpos.first>0  ? (tpos.first-1) : N-1;   break;
  case 3: newpos.second = tpos.second>0 ? (tpos.second-1) : N-1;  break;
  }
  // Checking weather particle can be glued!
  // Here we do not take periodic boundary conditions since they put too many particles on the boundary
  bool r_neighbor = newpos.first<N-1   && site[newpos.first+1][newpos.second];
  bool u_neighbor = newpos.second<N-1  && site[newpos.first][newpos.second+1];
  bool l_neighbor = newpos.first>0     && site[newpos.first-1][newpos.second];
  bool d_neighbor = newpos.second>0    && site[newpos.first][newpos.second-1];
    
  if (r_neighbor || u_neighbor || l_neighbor || d_neighbor){
    // Particle just glued
    site[newpos.first][newpos.second]=true;
    return 1;
  }
  ppos = tpos;
  tpos = newpos;
  return 0;
}

void Lattice::Draw(Plotter& plotter, int pixsize)
{
  plotter.filltype(1);
  plotter.fillcolor(0,0,0);
  for (int i=0; i<N; i++){
    for (int j=0; j<N; j++){
      if (site[i][j]){
	plotter.fbox(i*pixsize,j*pixsize,(i+1)*pixsize,(j+1)*pixsize);
      }
    }
  }
}

void Lattice::DrawAStep(Plotter& plotter, int pixsize)
{
  int pi= ppos.first, pj= ppos.second;
  int i = tpos.first, j = tpos.second;
  //  plotter.fillcolor(MAXRGB,MAXRGB,MAXRGB);// white color removes particle from previous position
  //  plotter.box(pi*pixsize,pj*pixsize,(pi+1)*pixsize,(pj+1)*pixsize);
  plotter.fillcolor(0,0,MAXRGB);
  plotter.box(i*pixsize,j*pixsize,(i+1)*pixsize,(j+1)*pixsize);
}

int main(int argc, char *argv[], char *env[])
{
  int N=200;
  int M=3000;
  int pixsize=10;
  bool debug=false;
  int i=0;
  while (++i<argc){
    std::string str(argv[i]);
    if (str=="-N" && i<argc-1)  N = atoi(argv[++i]);
    if (str=="-M" && i<argc-1)  M = atoi(argv[++i]);
    if (str=="-ps" && i<argc-1)  pixsize = atoi(argv[++i]);
    if (str=="-debug") debug=true;
    if (str=="-h" || str=="--help"){
      std::clog<<"************** Dendritic cluster grow **************\n";
      std::clog<<"**                                                **\n";
      std::clog<<"**      Copyright Kristjan Haule, 26.09.2005      **\n";
      std::clog<<"****************************************************\n";
      std::clog<<"\n";
      std::clog<<"dla [-N int] [-h]\n" ;
      std::clog<<"Options:   -h       Displays this help message\n";
      std::clog<<"           -N       Linear dimention of the lattice ("<<N<<")\n";
      std::clog<<"           -M       Number of random walkers ("<<M<<")\n";
      std::clog<<"           -ps      Pixel size ("<<pixsize<<")\n";
      std::clog<<"           -debug   Each step of simulation is plotted ("<<debug<<")\n";
      std::clog<<"*****************************************************\n";
    return 0;
    }
  }

  /************** Initialization of plotter **************************/
  PlotterParams params; // set a Plotter parameter
  params.setplparam( "BITMAPSIZE", (char*)"100x100");
  params.setplparam( "BG_COLOR", (char*)"orange");
  params.setplparam( "TRANSPARENT_COLOR", (char*)"orange");
  params.setplparam( "GIF_ITERATIONS", (char*)"100");
  params.setplparam( "GIF_DELAY", (char*)"5");
  
  GIFPlotter plotter(stdin, stdout, stderr, params);
  if (plotter.openpl () < 0){ // open Plotter      
    cerr << "Couldn't open Plotter\n";
    return 1;
  }
  plotter.fspace (0.0, 0.0, pixsize*N, pixsize*N); // specify user coor system
  plotter.pencolorname("black");    /* use red pen */
  plotter.linewidth(5);           /* set line thickness */
  plotter.filltype(1);            /* objects will be filled */
  plotter.fillcolorname("black"); /* set the fill color */
  
  plotter.erase ();                // erase Plotter's graphics display
  /*************************************************************************/

  /************** Initialization of random number gen. *********************/
  int random_seed = time(0);
  srand48(random_seed);
  /*************************************************************************/
  
  /************** Initialization of main class  ***************************/
  Lattice lattice(N);
  /*************************************************************************/
  
  /************** Simulation  ***************************/
  for (int i=0; i<M; i++){
    lattice.ReleaseNew();
    while (lattice.MakeStep()==0) if (debug) lattice.DrawAStep(plotter, pixsize);
    if (debug) plotter.erase ();
    if (debug || i%20==0) {plotter.erase();lattice.Draw(plotter,pixsize);}
  }
  /******************************************************/

  clog<<"DONE"<<endl;
  if (plotter.closepl () < 0){ // close Plotter
    cerr << "Couldn't close Plotter\n";
    return 1;
  }
  return 0;
}	

