package com.codingame.game.engine;


import java.util.HashSet;
import java.util.Objects;

public class GameState
{
  public boolean whiteToMove;

  public int whiteKing;
  public int whiteRook;
  public int blackKing;

  public int depth;
  public GameState parent = null;
  public boolean check = false;
  public boolean mate = false;

  public int action = -1;
  public String[] actionShow = null;

  private HashSet<Integer> blackKingAttack = new HashSet<>();
  private HashSet<Integer> whiteKingAttack = new HashSet<>();
  public HashSet<Integer> whiteRookAttack = new HashSet<>();
  public HashSet<Integer> blackKingAction = new HashSet<>();
  public HashSet<Integer> whiteKingAction = new HashSet<>();
  public HashSet<Integer> whiteRookAction = new HashSet<>();

  public GameState(String[] setup) // Depth movingPlayer whiteKing whiteRook blackKing
  {
    whiteToMove = setup[1].equals("white");
    whiteKing = Constants.CoordinatesStrToNum(setup[2]);
    whiteRook = Constants.CoordinatesStrToNum(setup[3]);
    blackKing = Constants.CoordinatesStrToNum(setup[4]);

    depth = 0;
    action = -1;
    computeActions();
  }

  public GameState(GameState parent, int actioncode) // assuming legal action
  {
    this.parent = parent;
    whiteToMove = !parent.whiteToMove;
    whiteKing = parent.whiteKing;
    whiteRook = parent.whiteRook;
    blackKing = parent.blackKing;

    actionShow = new String[5];
    actionShow[2] = "-";
    actionShow[3] = Constants.CoordinatesNumToStr(actioncode%100);
    actionShow[4] = "";

    if (actioncode/100==1)
    {
      actionShow[0] = "♖";
      actionShow[1] = Constants.CoordinatesNumToStr(whiteRook);
      whiteRook = actioncode%100;
    }
    if (actioncode/100==2)
    {
      actionShow[0] = "♔";
      actionShow[1] = Constants.CoordinatesNumToStr(whiteKing);
      whiteKing = actioncode%100;
    }
    if (actioncode/100==3)
    {
      actionShow[0] = "♚";
      actionShow[1] = Constants.CoordinatesNumToStr(blackKing);
      blackKing = actioncode%100;
    }

    if (actioncode/100==3 && actioncode%100 == whiteRook)
    {
      whiteRook = -1;
      actionShow[2] = "x";
    }

    depth = parent.depth+1;
    action = actioncode;
    computeActions();

    check = !whiteToMove && whiteRookAttack.contains(blackKing);
    if (check) actionShow[4] = "+";
    mate = check && blackKingAction.isEmpty();
    if (mate) actionShow[4] = "#";
  }

  private void computeActions()
  {
    if (blackKing/10>0) blackKingAttack.add((blackKing/10-1)*10+(blackKing%10));
    if (blackKing/10<7) blackKingAttack.add((blackKing/10+1)*10+(blackKing%10));
    if (blackKing%10>0) blackKingAttack.add((blackKing/10)*10+(blackKing%10)-1);
    if (blackKing%10<7) blackKingAttack.add((blackKing/10)*10+(blackKing%10)+1);
    if (blackKing/10>0 && blackKing%10>0) blackKingAttack.add((blackKing/10-1)*10+(blackKing%10)-1);
    if (blackKing/10>0 && blackKing%10<7) blackKingAttack.add((blackKing/10-1)*10+(blackKing%10)+1);
    if (blackKing/10<7 && blackKing%10>0) blackKingAttack.add((blackKing/10+1)*10+(blackKing%10)-1);
    if (blackKing/10<7 && blackKing%10<7) blackKingAttack.add((blackKing/10+1)*10+(blackKing%10)+1);

    if (whiteKing/10>0) whiteKingAttack.add((whiteKing/10-1)*10+(whiteKing%10));
    if (whiteKing/10<7) whiteKingAttack.add((whiteKing/10+1)*10+(whiteKing%10));
    if (whiteKing%10>0) whiteKingAttack.add((whiteKing/10)*10+(whiteKing%10)-1);
    if (whiteKing%10<7) whiteKingAttack.add((whiteKing/10)*10+(whiteKing%10)+1);
    if (whiteKing/10>0 && whiteKing%10>0) whiteKingAttack.add((whiteKing/10-1)*10+(whiteKing%10)-1);
    if (whiteKing/10>0 && whiteKing%10<7) whiteKingAttack.add((whiteKing/10-1)*10+(whiteKing%10)+1);
    if (whiteKing/10<7 && whiteKing%10>0) whiteKingAttack.add((whiteKing/10+1)*10+(whiteKing%10)-1);
    if (whiteKing/10<7 && whiteKing%10<7) whiteKingAttack.add((whiteKing/10+1)*10+(whiteKing%10)+1);

    if (whiteRook >=0) // not captured
    {
      for (int dx=1; dx < 8; dx++) { if (whiteRook/10-dx < 0 || (whiteRook/10-dx)*10+whiteRook%10 == whiteKing) break; whiteRookAttack.add((whiteRook/10-dx)*10+whiteRook%10);}
      for (int dx=1; dx < 8; dx++) { if (whiteRook/10+dx > 7 || (whiteRook/10+dx)*10+whiteRook%10 == whiteKing) break; whiteRookAttack.add((whiteRook/10+dx)*10+whiteRook%10);}
      for (int dy=1; dy < 8; dy++) { if (whiteRook%10-dy < 0 || whiteRook-dy == whiteKing) break; whiteRookAttack.add(whiteRook-dy);}
      for (int dy=1; dy < 8; dy++) { if (whiteRook%10+dy > 7 || whiteRook+dy == whiteKing) break; whiteRookAttack.add(whiteRook+dy);}

      for (int dx=1; dx < 8; dx++)
      {
        if (whiteRook/10-dx < 0 || (whiteRook/10-dx)*10+whiteRook%10 == whiteKing) break;
        if ((whiteRook/10-dx)*10+whiteRook%10 == blackKing) {
          //whiteRookAction.add((whiteRook/10-dx)*10+whiteRook%10);
          break;}
        whiteRookAction.add((whiteRook/10-dx)*10+whiteRook%10);
      }
      for (int dx=1; dx < 8; dx++)
      {
        if (whiteRook/10+dx > 7 || (whiteRook/10+dx)*10+whiteRook%10 == whiteKing) break;
        if ((whiteRook/10+dx)*10+whiteRook%10 == blackKing) {
          //whiteRookAction.add((whiteRook/10+dx)*10+whiteRook%10);
          break;}
        whiteRookAction.add((whiteRook/10+dx)*10+whiteRook%10);
      }
      for (int dy=1; dy < 8; dy++)
      {
        if (whiteRook%10-dy < 0 || whiteRook-dy == whiteKing) break;
        if (whiteRook-dy == blackKing) {
          //whiteRookAction.add(whiteRook-dy);
          break;}
        whiteRookAction.add(whiteRook-dy);
      }
      for (int dy=1; dy < 8; dy++)
      {
        if (whiteRook%10+dy > 7 || whiteRook+dy == whiteKing) break;
        if (whiteRook+dy == blackKing) {
          //whiteRookAction.add(whiteRook+dy);
          break;}
        whiteRookAction.add(whiteRook+dy);
      }
    }

    for (Integer xy: blackKingAttack)
    {
      if (!whiteKingAttack.contains(xy) && !whiteRookAttack.contains(xy))
        blackKingAction.add(xy);
    }

    for (Integer xy: whiteKingAttack)
    {
      if (!blackKingAttack.contains(xy) && xy != whiteRook)
        whiteKingAction.add(xy);
    }
  }

  @Override
  public String toString()
  {
    return "GameState{" +
            "whiteToMove=" + whiteToMove +
            ", whiteKing=" + Constants.CoordinatesNumToStr(whiteKing) +
            ", whiteRook=" + Constants.CoordinatesNumToStr(whiteRook) +
            ", blackKing=" + Constants.CoordinatesNumToStr(blackKing) +
            ", depth=" + depth +
            '}';
  }

  @Override
  public boolean equals(Object o)
  {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    return hashCode() == o.hashCode();
  }

  @Override
  public int hashCode()
  {
    return whiteKing+100*whiteRook+10000*blackKing+(whiteToMove?1000000:0)+10000000*depth;
  }
}
