import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class SqueezePanel extends EventPanel
	{

		private final int MAX_POP_SIZE = 500;  // nmaximum number of pieces
		private final int DISPLAY_WIDTH=300;
	    private final int DISPLAY_HEIGHT=300;
	    private final double PROB_MUT = 0.01;
	    private final int NUM_SKIP_GENS = 1;
	    private int substrate_width;
	    private int substrate_height;
	    private int west_boundary;
	    private int east_boundary;
	    private int north_boundary;
	    private int south_boundary;

        private Color theColor = Color.green;
        private int x;
        private int y;

        private Shape population[];
        private Shape temp_pop[];

        private int pop_size;
        private int updated_temp_pop_size;
        private int niche_sample_size;
        private int tourn_size;
        private double prob_mut;
        private int gen_num;
        private int num_skip_gens;
        private int piece_size;
        private int magnification=4;

        private GlobalOptStats globalStats;
        private AllSpeciesStats allSpeciesStats;

        public static Random rand = new Random(1234567890);


        public SqueezePanel()
            {

			                // initialization
			   pop_size = MAX_POP_SIZE;
			   population = new Shape[pop_size];
			   temp_pop = new Shape[pop_size];

			   tourn_size = 2;
			   prob_mut = PROB_MUT;
			   niche_sample_size = 200;
			   num_skip_gens = NUM_SKIP_GENS;

			   piece_size=10;
			   substrate_width = 40;
			   substrate_height = 40;

			   west_boundary = 30;
			   east_boundary = west_boundary+substrate_width;
			   north_boundary = 30;
			   south_boundary = north_boundary + substrate_height;

			   globalStats = new GlobalOptStats(west_boundary, north_boundary,
			   								    substrate_width,substrate_height,
			   								    piece_size);
			   allSpeciesStats = new AllSpeciesStats(west_boundary, north_boundary,
			   			   							 substrate_width,substrate_height,
			   								    	 piece_size);

			   int random_x;
			   int random_y;

			   for(int i =0; i < pop_size; i++)
			      {
					random_x = SqueezePanel.rand.nextInt(substrate_width) + west_boundary;
					random_y = SqueezePanel.rand.nextInt(substrate_height) + north_boundary;
			    	population[i] = new Square(random_x, random_y, piece_size, magnification);

			    //	population[i] = new Square(random_x, 40, piece_size, magnification);

				  }
			   gen_num = 0;

			  globalStats.update(population);
			  globalStats.print(gen_num);

			}
	         //
	    public void paint(Graphics g)
	          {
				    Color oldColor = g.getColor();
					g.setColor(Color.white);

					g.fillRect(0,0,800,800);

					g.setColor(Color.red);
					g.drawString("generation:  " + gen_num,
					             (substrate_width*magnification)/2 + 30,
					              30*magnification*3/4);
	//				             substrate_height*magnification + 150);
					g.setColor(oldColor);

	                paintSubstrate(g);

	              }

	    //
	    private void paintSubstrate(Graphics gu)
	              {
	               //
	              for(int i=0; i<pop_size;i++)
	                   population[i].paint(gu);

				  Color oldColor = gu.getColor();
				  gu.setColor(Color.blue);
				  gu.drawRect(30*magnification,30*magnification,
				    		  substrate_width*magnification,substrate_height*magnification);

	              gu.setColor(oldColor);
	              }

	    private boolean over_edge(Shape sh)
	        {
				return  sh.right_of(east_boundary) ||
				        sh.left_of(west_boundary) ||
				        sh.below(south_boundary) ||
				        sh.above(north_boundary);
		    }

		private double sampled_shared_fitness(Shape sh)
	        {
//			  System.out.println(updated_temp_pop_size);
			  double fitness;
			  if(updated_temp_pop_size <= niche_sample_size)
						     return shared_fitness(sh);

			  if(over_edge(sh))
			     fitness = 0;
			     else {
						  fitness = 1;
						  int niche_count=1;

						  for(int i = 0; i < niche_sample_size; i++)
						     {
							   int random_indiv = SqueezePanel.rand.nextInt(updated_temp_pop_size);
			                   niche_count += sh.overlap_amount(temp_pop[random_indiv]);
						     }
			              fitness /= niche_count;
				      }

			  return fitness;
		    }

	    private double shared_fitness(Shape sh)
	        {
//			  System.out.println(updated_temp_pop_size);
			  double fitness;
			  if(over_edge(sh))
			     fitness = 0;
			     else {
						  fitness = 1;
						  int niche_count=1;
						  for(int i = 0; i < updated_temp_pop_size; i++)
			                 niche_count += sh.overlap_amount(temp_pop[i]);
			              fitness /= niche_count;
				      }

			  return fitness;
		    }

		private Shape tournament(int size)
		   {
			   Shape [] competitors = new Shape[size];
			   for(int i = 0; i < size; i++)
			       competitors[i] = population[SqueezePanel.rand.nextInt(pop_size)];
			   Shape winner_so_far = competitors[0];
			   for(int i = 1; i < size; i++)
			       if(sampled_shared_fitness(winner_so_far) < sampled_shared_fitness(competitors[i]) )
			          winner_so_far = competitors[i];
			   return winner_so_far;
		   }

		private void selection()
		  {
			  updated_temp_pop_size = 0;
			  while(updated_temp_pop_size < pop_size)
			     {
			  	  temp_pop[updated_temp_pop_size] = tournament(tourn_size).offspring();
			  	  updated_temp_pop_size++;
			     }

			  for(int i =0; i < pop_size; i++)
			  	  population[i] = temp_pop[i];
	      }


	    private void mutation()
	      {
			  for(int i = 0; i < pop_size; i++)
			      if( SqueezePanel.rand.nextDouble() < prob_mut)
			          population[i].mutate();
	      }

	    private void generation()
	      {
			gen_num++;
			System.out.println("Now processing generation " + gen_num +
								"  Number of overlap comp.s last gen: " + Square.num_comps);
			Square.num_comps = 0;
			globalStats.update(population);
			globalStats.print(gen_num);

			allSpeciesStats.update(population);
			if(gen_num % num_skip_gens == 0) allSpeciesStats.print(gen_num);

			selection();
			if(gen_num % num_skip_gens == 0) mutation();
			repaint();
//			try {Thread.sleep(10);}  catch (Exception e) {};
	      }

	    private void generations(int n)
	      {
			  for(int i=0; i<n; i++)
			     generation();
	  	  }


    public void mouseDragged(MouseEvent e)
       {
		  x = e.getX();
		  y = e.getY();

          repaint();
        }


    public void mouseReleased(MouseEvent ev)
       {

	   }

	public void mousePressed(MouseEvent e)
		{
		x = e.getX();
		y = e.getY();

	  	repaint();
		}

	public void keyPressed(KeyEvent e)
		{
		   char c = e.getKeyChar();

		   System.out.println(c);

		   if(c == '1' )
		      generations(1);
		      else if(c == '2')
		      generations(2);
		      else if(c == '3')
		      generations(3);
		      else if(c == '4')
		      generations(4);
		      else if(c == '5')
		      generations(5);
		      else if(c == '9')
		      generations(9);
		      else if(c == 'c')
		      generations(100);
		      else if(c == 'x')
		      generations(700);
		      else if(c == 'q')
		      {globalStats.close(); allSpeciesStats.close();}
		}
	}

