package Organisms2.g7;
import java.security.*;
import java.util.*;

import java.awt.*;

import Organisms2.*;

/**
 * Modified BY Yaniv Schiller
 *Original Authors:
 * @author Mark Ayzenshtat
 * @author Yuan Zheng
 * @author Ludvig Ungewitter
 */
public class Group7PlayerY implements IFCPlayer {
  private Color kColor =  new Color(1.0f, 0.4f, 0.2f);;
  private static final String kName = "Mutant X";

  private static final int kReproFactorBits = 7;
  private static final int kMoveFactorBits = 8;
  private static final int kRoundNumBits = 4;
    private static final int kActionNumBits = 3;

  private static final int kRoundNumMuliplier=25;
    private static final float kStartingReproFactor = 0.2F;
    private static final float kStartingMoveFactor = 0.01F;

    private static final float kSteadyReproFactor = 0.4F;
    private static final float kSteadyMoveFactor = 0.3F;
private static boolean first=false;

private int farmstatus;
  // 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 gonnahavebabies;
  private float gonnaLeave;

  private int sRoundNumber;
    private int sIntRoundNumber;
    private int sAction;

  public void register(Organisms2 iOrg, int iKey) throws Exception {

    mOrg = iOrg;
    sIntRoundNumber=sRoundNumber=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();gonnahavebabies = kStartingReproFactor;
      gonnaLeave = kStartingMoveFactor;
      first=true;
    } else {
      first=false;
      mKey = iKey;
      gonnahavebabies =kReproFactorIncrement * (iKey & ((1 << kReproFactorBits) - 1));
      switch (sRng.nextInt(3)) {
        case 0 :kidsReadinessGrowth();break;
        case 1 :kidsReadinessGrowth(-kReproFactorIncrement);break;
      }
      gonnaLeave =kMoveFactorIncrement* ((iKey & (((1 << kMoveFactorBits) - 1) << kReproFactorBits))>>> kReproFactorBits);
      switch (sRng.nextInt(3)) {
        case 0 :moveReadinessGrowth();break;
        case 1 :moveReadinessGrowth(-kMoveFactorIncrement);break;
      }
      sRoundNumber=kRoundNumMuliplier*((iKey & (((1 << kRoundNumBits) - 1) << 15))>>> 15);
      sAction= ((iKey & (((1 << kActionNumBits) - 1) << 19))>>> 19);

    }
  }

    private void kidsReadinessGrowth() {kidsReadinessGrowth(kReproFactorIncrement);}
    private void kidsReadinessGrowth(float iAmount) {gonnahavebabies += iAmount;
      gonnahavebabies =(gonnahavebabies<=0?0.2F:gonnahavebabies);
      gonnahavebabies =(gonnahavebabies>1?0.9F:gonnahavebabies);
    }
    private void moveReadinessGrowth() {moveReadinessGrowth(kMoveFactorIncrement);}
    private void moveReadinessGrowth(float iAmount) {gonnaLeave += iAmount;
      gonnaLeave=(gonnaLeave < 0?0:gonnaLeave);
      gonnaLeave=(gonnaLeave > 1?1:gonnaLeave);
    }

  public Move move(boolean[] iFoodPresent, int[] iEnemies, int iFoodLeft,int iEnergyLeft)  {
    try {
      mFoodPresent = iFoodPresent;
      mEnemies = iEnemies;
      sRoundNumber++;
      sIntRoundNumber++;

      switch (sAction) {
        case 1:
          if (sIntRoundNumber == 1) {
            sAction = 5;
            return new Move(_CREPRODUCE, IFCConstants._CEAST, encodeKey(3));
          }
          if (iEnergyLeft <= 2 * mOrg.v() &&
              mEnemies[IFCConstants._CSOUTH] == -1) {
            sAction = 5;
            return new Move(IFCConstants._CSOUTH);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 2:
          if (sIntRoundNumber == 1) {
            return new Move(_CREPRODUCE, IFCConstants._CWEST, encodeKey(4));
          }
          if (iEnergyLeft <= 2 * mOrg.v() &&
              mEnemies[IFCConstants._CNORTH] == -1) {
            sAction = 6;
            return new Move(IFCConstants._CNORTH);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 3:
          if (iEnergyLeft <= 2 * mOrg.v() &&
              mEnemies[IFCConstants._CWEST] == -1) {
            sAction = 7;
            return new Move(IFCConstants._CWEST);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 4:
          if (sIntRoundNumber == 1) {
            return new Move(IFCConstants._CNORTH);
          }
          if (iEnergyLeft <= 2 * mOrg.v() &&
              mEnemies[IFCConstants._CEAST] == -1) {
            sAction = 8;
            return new Move(IFCConstants._CEAST);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 5:
          if (iEnergyLeft >= 3.5F * mOrg.v() &&
              mEnemies[IFCConstants._CNORTH] == -1) {
            sAction = 1;
            return new Move(IFCConstants._CNORTH);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 6:
          if (iEnergyLeft >= 3.5F * mOrg.v() &&
              mEnemies[IFCConstants._CSOUTH] == -1) {
            sAction = 2;
            return new Move(IFCConstants._CSOUTH);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 7:
          if (iEnergyLeft >= 3.5F * mOrg.v() &&
              mEnemies[IFCConstants._CEAST] == -1) {
            sAction = 3;
            return new Move(IFCConstants._CEAST);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
        case 8:
          if (iEnergyLeft >= 3.5F * mOrg.v() &&
              mEnemies[IFCConstants._CWEST] == -1) {
            sAction = 4;
            return new Move(IFCConstants._CWEST);
          }
          else {
            return new Move(IFCConstants._CSTAYPUT);
          }
      }
      if (sRoundNumber == 1000) {
        if (gonnahavebabies < kSteadyReproFactor) {
          gonnahavebabies = kSteadyReproFactor;
        }
        if (gonnaLeave < kSteadyMoveFactor) {
          gonnaLeave = kSteadyMoveFactor;
        }
      }

      int foodNeighbor = getNeighborWithFood(iFoodPresent);
      int numEnemies = countEnemies();
      if (sRoundNumber < 100) {
        if (first) {
//        sAction=1;
//        sIntRoundNumber=0;
//        return new Move(_CREPRODUCE, IFCConstants._CSOUTH, encodeKey(2));
          return reproduce(foodNeighbor);
        }
        if (iEnergyLeft >= 10 * mOrg.v() && foodNeighbor > -1 &&
            mEnemies[foodNeighbor] == -1 && numEnemies <= 2) {
          return reproduce(foodNeighbor);
        }
        else if (iEnergyLeft <= 10 * mOrg.v() && iFoodLeft > 0) {
          return new Move(0);
        }
        else if (iEnergyLeft <= 10 * mOrg.v() && foodNeighbor > -1) {
          return new Move(foodNeighbor);
        }

        if (sRng.nextFloat() > .05F) {
          return new Move(move);
        }
        else {
          return reproduce(foodNeighbor);
        }
      }
      else
      if (numCellsWithFood(iFoodPresent) >= 2) {
        kidsReadinessGrowth( -kReproFactorIncrement);
        moveReadinessGrowth( -kMoveFactorIncrement);
      }
      else if (numEnemies >= 2) {
        kidsReadinessGrowth();
        moveReadinessGrowth();
      }
      if ( (iEnergyLeft / (float) mOrg.M()) > gonnahavebabies) {
        if (foodNeighbor == -1) {
          switch (move) {
            case IFCConstants._CNORTH:
              return new Move(_CREPRODUCE, IFCConstants._CEAST, encodeKey(0));
            case IFCConstants._CEAST:
              return new Move(_CREPRODUCE, IFCConstants._CSOUTH, encodeKey(0));
            case IFCConstants._CSOUTH:
              return new Move(_CREPRODUCE, IFCConstants._CWEST, encodeKey(0));
            case IFCConstants._CWEST:
              return new Move(_CREPRODUCE, IFCConstants._CNORTH, encodeKey(0));
          }
        }
        else {
          return new Move(_CREPRODUCE, foodNeighbor, encodeKey(0));
        }
      }
      if (iFoodLeft > 0) {
        /*Bulid farm??
         */
        if (iEnergyLeft + iFoodLeft * mOrg.u() > 15 * mOrg.v() &&
            iFoodLeft >= 2 && numEnemies==0) {

          sAction = 1;
          sIntRoundNumber = 0;
          return new Move(_CREPRODUCE, IFCConstants._CSOUTH, encodeKey(2));
        }

        return new Move(_CSTAYPUT);
      }
      if (foodNeighbor != -1) {
        return new Move(foodNeighbor);
      }
      float stayMoveRatio = (mOrg.s() / (float) mOrg.v()) * gonnaLeave;

      if (sRng.nextFloat() >= stayMoveRatio) {
        return new Move(0);
      }
      else {
        return new Move(move);
      }
    }
    catch (Exception ex) {
      return null;
    }
  }

  private int countEnemies() {
    int mcountEnemies=0;
    for(int i=1;i<5;i++)
      mcountEnemies+=(mEnemies[i]==-1?0:1);
    return mcountEnemies;

  }

  private Move reproduce(    int foodNeighbor) throws Exception {
    if (foodNeighbor == -1)
      return new Move(_CREPRODUCE, sRng.nextInt(4) + 1, encodeKey(0));
    else
      return new Move(_CREPRODUCE, foodNeighbor, encodeKey(0));
  }

  private int encodeKey(int tAction) {
    int mycode = 0;
    mycode |= (mKey & 0xc0000000);
    int encodedRepro = (int) (gonnahavebabies / kReproFactorIncrement);
    mycode |= (encodedRepro & ((1 << kReproFactorBits) - 1));
    int encodedMove = (int) (gonnaLeave / kMoveFactorIncrement);
    mycode |= ((encodedMove & ((1 << kMoveFactorBits) - 1)) << kReproFactorBits);
    int encodeRound= (int) (sRoundNumber / kRoundNumMuliplier);
    mycode |= ((encodeRound & ((1 << kRoundNumBits) - 1)) << 15);
    mycode |= ((tAction & ((1 << kActionNumBits) - 1)) << 19);
    return mycode;
  }

  private int getNeighborWithFood(boolean[] iFoodPresent) {
    for (int i = 1; i <= 4; i++) {
      if (iFoodPresent[i]) {
        return i;
      }
    }
    return -1;
  }


    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 {

    return sRng.nextInt();

  }

  public boolean interactive() throws Exception {
    return false;
  }

  public String name() throws Exception {
    return kName;
  }

  public Color color() throws Exception {
//   if(sAction==0)
    return kColor;
//  else
  //  return kColor;//Color.yellow;
  }
}
