package CookieCutter.g2;

import java.util.*;

/**
 * original author:
 * @author mayz
 * modified author:
 * Yaniv
 */
public class optimumCutter{



  public Cookie[] placeCookies(Cookie[] iCookies) {
    //array that hold bes configuration for that many cookies
    //index is one off the number of cookies
    List[] bestcfgs= new List[iCookies.length];
    double[] bestscrs=new double[iCookies.length];

    Cookie best = null;
    double bestScore;

    initPosition(iCookies);

    Random rng = new Random();
    List placed = new ArrayList();
    Cookie c = null;
    Cookie[] bestArrangement ;

    //    System.out.println("[placeCookies] start loop through Cookies");
    for (int i = 0; i < iCookies.length; i++) {
      bestScore = Double.MAX_VALUE;
      best = null;
      int NUMGUESSES=1000;
      int j = 0;
      while ((j < NUMGUESSES) || (best == null)) {
	  //      for (int j = 0; j < NUMGUESSES; j++) {
	  //for the first 180 degrees, rotate one degree at a time
	  //after 180 degrees, rotate randomly
	  if(j>180){
	      c = iCookies[i].transform(iCookies[i].getCentroid(),
					rng.nextDouble() * Math.PI * 2).flushToTopObstacle(placed);

	  }
	  else
	      c = iCookies[i].transform(iCookies[i].getCentroid(),Math.PI*(double)(j/90.0))
		  .flushToTopObstacle(placed);
	  //first third, flush to the left and then up (flush to bottom
	  //is mis-named)
	  if (j<NUMGUESSES/3.0) {// /3.0 =180||rng.nextBoolean()
          c = c.flushToLeftObstacle(placed).flushToBottomObstacle(placed);
	  c=c.shift(0,-(c.getBounds().getBottom()-Cookie.kCollideEpsilon)).flushToLeftObstacle(placed);
        } 
	//move up and to the left for the first 2/3 of NUMGUESSES
	else if(j<NUMGUESSES*(2.0/3.0)){//rng.nextBoolean()
          c = c.flushToBottomObstacle(placed).flushToLeftObstacle(placed);
        } 
	//for the last 1/3 move up a random amount, and try to 
	//snuggle the shape in
	else{
          c = c.flushToRandomY(placed,rng.nextDouble())
              .flushToLeftObstacle(placed).flushToBottomObstacle(placed)
              .flushToLeftObstacle(placed).flushToBottomObstacle(placed);
        }

	  //System.out.println("[placeCookies] Cookie "+i+", Guess "+j+": getting rightmost point");
        double score = c.getLargestX();
	if( !c.intersects(placed)) {
	    if (score < bestScore){
		bestScore = score;
		best = c;
	    }
	}
	j++;
      }
   
      //      System.out.println("[placeCookies] Cookie "+i+": done guessing positions");
      //      if (best==null) {
      //	  System.out.println("[placeCookies] COOKIE INTERSECTS! BEST=NULL!");
      //      }

      //go through all combinations

      double minCombScore=Double.MAX_VALUE;
      int minCombo=0;
      for(int mc=placed.size();mc>0;mc--){//for each combinatoral size
        double combScore=0;
        int rem=i+1,div=0;
        int s=mc;
        while(rem>0){
          div=(int)(rem/s);
          if(div>0){
            combScore+=(div*bestscrs[s-1]);//-1 for index fixing...
            rem-=div*s;
          }
          s--;
        }
        if(combScore<minCombScore){
          minCombScore = combScore;
          minCombo=mc;
        }
      }
      //      System.out.println("[placeCookie] checking if current is better");
      if(i==0||bestScore<minCombScore)
      {
	  //	  System.out.println("[placeCookie] it is.");
	  placed.add(best);
	  bestscrs[i]=bestScore;
	  bestcfgs[i]=new ArrayList(placed);
	  //	  System.out.println("[placeCookie] i: "+i);
	  //	  System.out.println("[placeCookie] placed: "+placed.size());
	  //	  System.out.println("[placeCookie] bestcfgs: "+bestcfgs.length);
	  //	  System.out.println("[placeCookies] placed "+i+": "+placed.get(i));
	  //	  System.out.println("[placeCookies] bestcfgs "+i+": "+bestcfgs[i]);
			     
      }
      else{
	  //	  System.out.println("[placeCookie] it's not.");
        //use Combination
        //using combination stating with: minCombo
        int rem=i+1,div=0;
        int s=minCombo;
        double left=0;
        int ccnt=0;
        Cookie[] tempArrangement =new Cookie[i+1];
        List somePlaced=new ArrayList();
        while(rem>0){
          div = (int) (rem / s);
          if (div > 0) {
            //use 'div' * this config of size 's'-1
            //use bestcfgs[s-1]
            for(int q=0;q<div;q++){//for each time
              Cookie[] veryTempArrangement =new Cookie[s];
              for(int r=0;r<(s);r++){//for each cookie in that config
                Cookie tC=(Cookie)(bestcfgs[s-1].toArray()[r]);
                tempArrangement[ccnt]=tC.transform(new Point(tC.getCentroid().xpos+left+(left>0?Cookie.kCollideEpsilon:0),tC.getCentroid().ypos),0) ;
                veryTempArrangement[r]=tempArrangement[ccnt];
                ccnt++;
              }
	      //	      System.out.println("[placesCookies] dumping all cookies:");
	      //	      for (int z=0; z < veryTempArrangement.length; z++) {
	      //		  System.out.println("\t\t"+veryTempArrangement[z]);
	      //	      }
              //now smush the box...
              if(left>0)
                veryTempArrangement=flushToLeftObstacle(somePlaced,veryTempArrangement);
              left=Cookie.computeRightBoundary(veryTempArrangement);
              //now after the smush, add the column (verytplaced) to the rest of the stable ones (someplaced)
              for(int k=0;k<veryTempArrangement.length;k++)
                somePlaced.add(veryTempArrangement[k]);//k.next()
            }
            rem -= div * s;
          }
          s--;
        }
        bestscrs[i]=minCombScore;
        bestcfgs[i]=new ArrayList(somePlaced);
        placed=null;
        placed=somePlaced;

      }
    }
    //    System.out.println("[placeCookies] bestcfgs dump: ");
    //    for (int z=0;z<bestcfgs.length;z++) {
    //	System.out.println("\t"+z+": "+bestcfgs[z]);
    //	for (int q=0;q<bestcfgs[z].size();q++) {
    //	    System.out.println("\t\t"+q+": "+bestcfgs[z].get(q));
    //	}
    //    }
    bestArrangement = (Cookie[]) bestcfgs[iCookies.length-1].toArray(new Cookie[0]);
    //    System.out.println("[placeCookies] bestArragement dump: ");
    //    for (int z=0;z<bestArrangement.length;z++) {
    //	System.out.println("\t"+z+": "+bestArrangement[z]);
    //    }
    System.arraycopy(bestArrangement, 0, iCookies, 0, bestArrangement.length);
    //    System.out.println("[placeCookies] iCookies dump: ");
    //    for (int z=0;z<iCookies.length;z++) {
    //	System.out.println("\t"+z+": "+iCookies[z]);
    //    }
    return bestArrangement;
  }

  private Cookie[] copy(Cookie[] iCookies) {
    Cookie[] c = new Cookie[iCookies.length];
    System.arraycopy(iCookies, 0, c, 0, c.length);
    return c;
  }

  private void initPosition(Cookie[] iCookies) {
    for (int i = 0; i < iCookies.length; i++) {
      iCookies[i] = iCookies[i].transform(
          new Point( (iCookies[i].getBounds().getWidth()+i) , 0.5), 0);
    }
  }

  //get the bounds of a group of cookie
  public Box getBounds(Cookie[] movingCookies) {
    double left = Double.MAX_VALUE;
    double right = Double.MIN_VALUE;
    double top = Double.MIN_VALUE;
    double bottom = Double.MAX_VALUE;
    for (int c = 0; c<movingCookies.length;c++ ){
      Cookie co= (Cookie)movingCookies[c];
      for (int i = 0; i < co.getmX().length; i++) {
        if (co.getmX()[i] < left) left = co.getmX()[i];
        if (co.getmX()[i] > right) right = co.getmX()[i];
        if (co.getmY()[i] > top) top = co.getmY()[i];
        if (co.getmY()[i] < bottom) bottom = co.getmY()[i];
      }
    }
    return new Box(left, top, right, bottom);
  }

  //flush a bunch of cookies left...
  //Try to flip over too..
  public Cookie[] flushToLeftObstacle(List iPlacedCookies,Cookie[] movingCookies) {
    for (int i = 0; i<movingCookies.length;i++ )
      for (Iterator j = iPlacedCookies.iterator(); j.hasNext(); )
        if (((Cookie)movingCookies[i]).intersects((Cookie) j.next()))
          return movingCookies;
    Box mbounds=getBounds(movingCookies);
    double d = mbounds.getLeft() - Cookie.kEpsilon;

    List movedCookies=new ArrayList();
    while (d > Cookie.kCollideEpsilon ) {//&&mbounds.getLeft() > Cookie.kCollideEpsilon
      movedCookies.clear();
      boolean stillGood=true;
      for (int i = 0; i<movingCookies.length&&stillGood;i++ ){
        Cookie moved = ((Cookie)movingCookies[i]).shift( -d, 0);
        if (moved.intersects(iPlacedCookies)) {
          stillGood=false;
        }
        movedCookies.add(moved);
      }
      if(stillGood){
        movingCookies = (Cookie[]) movedCookies.toArray(new Cookie[0]);
//        mbounds=getBounds(movingCookies);
      }
      else
        d /= 2;


    }
    return movingCookies;
  }

}
