package Organisms2.g3;

import java.awt.Color;
import java.io.*;
import java.security.*;
import java.util.*;
import Organisms2.*;

/**
 * Eat, divide, repeat.
 *
 * @author Mark Ayzenshtat
 * @author Yuan Zheng
 * @author Ludvig Ungewitter
 */
public class Group3Player5 implements IFCPlayer
{
  private Color kColor=Color.BLUE;
  private static final String kName="Kill, Kill, Kill";

  private static final int kReproFactorBits=7;
  private static final int kMoveFactorBits=7;

  private static final float kStartingReproFactor=0.5F;
  private static final float kStartingMoveFactor=0.5F;

  // increment for the reproduction factor
  private static final float kReproFactorIncrement=
      (1F/(1<<kReproFactorBits));

  // increment for the move factor
  private static final float kMoveFactorIncrement=
      (1F/(1<<kMoveFactorBits));

  private static Random sRng=new SecureRandom();

  private Organisms2 mOrg;
  private boolean[] mFoodPresent;
  private int[] mEnemies;
  private int mKey;
  private int move; //this organism will always move this direction
  private float mReproFactor;
  private float mMoveFactor;

  private static int sCase1;
  private static int sCase2;
  private static int sCase3;

  public void register(Organisms2 iOrg, int iKey) throws Exception
  {
    mOrg=iOrg;
    sCase1=sCase2=sCase3=0;

    int rVal=sRng.nextInt(3);
    switch(rVal)
    {
      case 0:
        move=IFCConstants._CNORTH;
        break;
      case 1:
        move=IFCConstants._CWEST;
        break;
      case 2:
        move=IFCConstants._CSOUTH;
        break;
      case 3:
        move=IFCConstants._CEAST;
        break;
    }

    if(iKey==-1)
    {
      mKey=sRng.nextInt();
      mReproFactor=kStartingReproFactor;
      mMoveFactor=kStartingMoveFactor;
    }
    else
    {
      mKey=iKey;
      //////////////
      // repro factor
      mReproFactor=
          kReproFactorIncrement*(iKey&((1<<kReproFactorBits)-1));

      switch(sRng.nextInt(3))
      {
        case 0:
          incrementRepro();
          break;
        case 1:
          incrementRepro(-kReproFactorIncrement);
          break;
        case 2:

          // keep parent's repro factor
          break;
      }

      if(mReproFactor<0.4)
        kColor=Color.blue.brighter().brighter().brighter().brighter().brighter().
            brighter().brighter().brighter().brighter();
      else if(mReproFactor<0.6)
        kColor=Color.blue.brighter().brighter().brighter().brighter().brighter();
      else if(mReproFactor<0.8)
        kColor=Color.blue.brighter().brighter().brighter().brighter();
      else if(mReproFactor<0.9)
        kColor=Color.blue.brighter();
      else
        kColor=Color.blue.darker().darker().darker();

        //////////////
        // move factor
      mMoveFactor=
          kMoveFactorIncrement
          *((iKey&(((1<<kMoveFactorBits)-1)<<kReproFactorBits))
            >>>kReproFactorBits);

      switch(sRng.nextInt(3))
      {
        case 0:
          incrementMove();
          break;
        case 1:
          incrementMove(-kMoveFactorIncrement);
          break;
        case 2:

          // keep parent's move factor
          break;
      }
    }

    //System.out.println("MOVE = " + mMoveFactor + ", REPRO = " + mReproFactor);
  }

  private void incrementRepro()
  {
    incrementRepro(kReproFactorIncrement);
  }

  private void incrementRepro(float iAmount)
  {
    mReproFactor+=iAmount;

    if(mReproFactor<=0)
    {
      mReproFactor=0.2F;
    }

    if(mReproFactor>=1)
    {
      mReproFactor=0.95F;
    }
  }

  private void incrementMove()
  {
    incrementMove(kMoveFactorIncrement);
  }

  private void incrementMove(float iAmount)
  {
    mMoveFactor+=iAmount;

    if(mMoveFactor<0)
    {
      mMoveFactor=0;
    }

    if(mMoveFactor>1)
    {
      mMoveFactor=1;
    }
  }

  public Move move(boolean[] iFoodPresent, int[] iEnemies, int iFoodLeft,
                   int iEnergyLeft) throws Exception
  {  	
  	
    mFoodPresent=iFoodPresent;
    mEnemies=iEnemies;


    //learn within generation
    if(numCellsWithFood(iFoodPresent)>=2)
    {
      incrementRepro(-kReproFactorIncrement);
      incrementMove();
      //System.out.println("FOOD");
    }
    if(numNeighbors(iEnemies)>=2)
    {
      incrementRepro();
      incrementMove();
      //System.out.println("CROWDED");
    }

    //int foodNeighbor = getNeighborWithFood(iFoodPresent);
    int emptyFoodCell=getEmptyFoodCell(iFoodPresent, iEnemies);

    if((iEnergyLeft/(float)mOrg.M())>mReproFactor)
    {
      // reproduce
      if(emptyFoodCell==-1)
      {

        //reproduce in other direction than you are walking
        switch(move)
        {
          case IFCConstants._CNORTH:
            return new Move(_CREPRODUCE, IFCConstants._CEAST, encodeKey());
          case IFCConstants._CEAST:
            return new Move(_CREPRODUCE, IFCConstants._CSOUTH, encodeKey());
          case IFCConstants._CSOUTH:
            return new Move(_CREPRODUCE, IFCConstants._CWEST, encodeKey());
          case IFCConstants._CWEST:
            return new Move(_CREPRODUCE, IFCConstants._CNORTH, encodeKey());
        }

        // if there's no neighboring food, reproduce onto a random neighbor
        //return new Move(_CREPRODUCE, sRng.nextInt(4) + 1, encodeKey());
      }
      else
      {
        // otherwise, reproduce onto the neighbor w/ food
        return new Move(_CREPRODUCE, emptyFoodCell, encodeKey());
      }
    }

    // if there's food here, eat it
    if(iFoodLeft>0)
    {
      sCase1++;
      //there is an empty cell with food and there is a hungry friend, move away
      if((emptyFoodCell!=-1)&&getHungryFriend(iFoodPresent, iEnemies)!=-1)
      {
        return new Move(emptyFoodCell);
      }
      else
        return new Move(_CSTAYPUT);
    }

    // if there's food next to us, claim it
    if(emptyFoodCell!=-1)
    {
      sCase2++;
      return new Move(emptyFoodCell);
    }

    sCase3++;

    float stayMoveRatio=(mOrg.s()/(float)mOrg.v())*mMoveFactor;

		//if (mOrg.u() >= 30) {
			//mMoveFactor += ((1 - mMoveFactor) / 2);
		//}		

/*
    System.out.println("mOrgs.s " + mOrg.s());
    System.out.println("mOrgs.v " + mOrg.v());
    System.out.println("Movefactor: " + mMoveFactor);
    System.out.println("ration: " + stayMoveRatio + " \n");
*/
    //if(sRng.nextFloat()>=stayMoveRatio)
    if(sRng.nextFloat()<=mMoveFactor)
    {
      // stand still
      return new Move(0);
    }
    else
    {
      // move randomly
      return new Move(move);
    }
  }

  private int encodeKey()
  {
    int key=0;

    key|=(mKey&0xff000000);

    int encodedRepro=(int)(mReproFactor/kReproFactorIncrement);
    key|=(encodedRepro&((1<<kReproFactorBits)-1));

    int encodedMove=(int)(mMoveFactor/kMoveFactorIncrement);
    key|=((encodedMove&((1<<kMoveFactorBits)-1))<<kReproFactorBits);

    return key;
  }

  private int getHungryFriend(boolean[] iFoodPresent, int[] iEnemies)
  {
    int id=0;
    int key=0;
    key|=(mKey&0xff000000);
    key=key>>>24;
    for(int i=1; i<=4; i++)
    {
      //check neighbbor's state and compare with own state
      id|=(iEnemies[i]&0xff);
      if((id==key)&&!iFoodPresent[i])
      {
        //System.out.println("hungry friend at "+i);
        return i;
      }
      id=0; //reset ID
    }
    return-1;
  }

  private int getEmptyFoodCell(boolean[] iFoodPresent, int[] iEnemies)
  {
    for(int i=1; i<=4; i++)
    {
      if(iFoodPresent[i]&&iEnemies[i]==-1)
        return i;
    }
    return-1;
  }

  private int getNeighborWithFood(boolean[] iFoodPresent)
  {
    for(int i=1; i<=4; i++)
    {
      if(iFoodPresent[i])
      {
        return i;
      }
    }

    return-1;
  }

  private int numNeighbors(int[] iEnemies)
  {
    int count=0;
    for(int i=1; i<=4; i++)
    {
      if(iEnemies[i]!=-1)
      {
        count++;
      }
    }
    return count;
  }

  private int numCellsWithFood(boolean[] iFood)
  {
    int count=0;
    for(int i=1; i<=4; i++)
    {
      if(iFood[i])
      {
        count++;
      }
    }

    return count;
  }

  public int externalState() throws Exception
  {
    int state=0;
    state|=(mKey&0xff000000);
    state=state>>>24;
    return state;
  }

  public boolean interactive() throws Exception
  {
    return false;
  }

  public String name() throws Exception
  {
    return kName;
  }

  public Color color() throws Exception
  {
    return kColor;
  }
}
