package com.codingame.game.engine;


import com.codingame.game.Player;
import com.codingame.gameengine.core.SoloGameManager;

import java.util.*;

import static com.codingame.game.engine.Constants.*;

public class Engine
{
  private SoloGameManager<Player> manager;
  //private GraphicEntityModule graphic;

  public Terrain terrain;

  public int width;
  public int height;
  public int radius;
  public int turnlimit;

  public int lazzieXY;
  public int homeXY;
  public String endscr;


  public boolean[] renderedXY = new boolean[MAX_MAP*MAX_MAP];
  public ArrayList<Integer> newlyRendered = new ArrayList<>();

  public boolean[] blackFogXY = new boolean[MAX_MAP*MAX_MAP];
  public ArrayList<Integer> newlyBlackDissappear = new ArrayList<>();

  public boolean[] whiteFogXY = new boolean[MAX_MAP*MAX_MAP];

  public String lastDir = "X";
  public ArrayList<Integer> pathDirs = new ArrayList<>();



  public Engine(SoloGameManager<Player> manager)
  {
    this.manager = manager;
    terrain  = new Terrain(this);
    loadFromConfig(manager.getTestCaseInput());
    for (int xy=0; xy < MAX_MAP*MAX_MAP; xy++) { blackFogXY[xy] = true; whiteFogXY[xy] = true; }

    terrain.computeAll();
  }

  public void loadFromConfig(List<String> input)
  {
    String[] lines = input.get(0).split("\\s+");


    turnlimit = Integer.parseInt(lines[0]);
    radius = Integer.parseInt(lines[1]);
    terrain.rng = new Random(Integer.parseInt(lines[2]));
    terrain.plainDecPbb  = Double.parseDouble(lines[3]);
    terrain.bridgePbb    = Double.parseDouble(lines[4]);
    terrain.bigTreePbb   = Double.parseDouble(lines[5]);

    lines = input.get(1).split(" ");
    terrain.biome = lines[1];
    terrain.hometype = Integer.parseInt(lines[2]);
    endscr = lines[3];
    height = Integer.parseInt(input.get(2).split(" ")[1]);
    width = Integer.parseInt(input.get(3).split(" ")[1]);

    terrain.type = new char[width][height];
    terrain.texture = new String[width][height];

    for (int y=0; y < height; y++)
    {
      for (int x=0; x < width; x++)
      {
        char c = input.get(y+5).charAt(x);
        if (c=='L') {lazzieXY=XY(x,y); c='.';}

        terrain.type[x][y] = c;

        if (c=='H') {homeXY=XY(x,y);}

      }
    }
  }


  public void step(int turn)
  {
    newlyRendered.clear();
    newlyBlackDissappear.clear();
    for (int dx = -radius-1; dx <= radius+1; dx++)
    {
      for (int dy = -radius-1; dy <= radius+1; dy++)
      {

        int nx = X(lazzieXY) + dx;
        int ny = Y(lazzieXY) + dy;

        // if (Math.abs(dx)+Math.abs(dy) <= radius) { newlyBlackDissappear.add(XY(nx,ny)); blackFogXY[XY(nx,ny)] = false;}
        if (dx*dx+dy*dy <= (radius+0.5)*(radius+0.5)) {
          if (blackFogXY[XY(nx,ny)]) newlyBlackDissappear.add(XY(nx,ny));
          blackFogXY[XY(nx,ny)] = false;
          whiteFogXY[XY(nx,ny)] = false;
        } else {
          whiteFogXY[XY(nx,ny)] = true;
        }

        if (!renderedXY[XY(nx,ny)]) {newlyRendered.add(XY(nx,ny));}


        renderedXY[XY(nx,ny)] = true;
      }
    }

  }

  public String getTexture(int xy) {
    String t;
    try {
      t = terrain.texture[X(xy)][Y(xy)];
    } catch (Exception e) {
      System.out.println(String.format("notexture for: %d", xy));
      return "notexture";
    }
    if (t==null) return "notexture";
    if (terrain.biome.equals("snow")) {
      if (t.startsWith("h")) return t+"s";
      if (t.startsWith("w")) return "r"+t.substring(1);
      if (t.startsWith("t")) return "y"+t.substring(1);
      if (t.startsWith("b0")) return "b2";
      if (t.startsWith("b1")) return "b3";
      if (t.startsWith("g")) return "s"+t.substring(1);
    }
    if (terrain.biome.equals("underground")) {
      if (t.startsWith("h")) return t + "u";
    }
    return t;
  }


  public void validateAnswer(List<String> outputs) throws ActionErrorException
  {
    if (outputs==null) throw new ActionErrorException(String.format("[ERROR] No output provided."));
    pathDirs.clear();
    int x, y;
    String[] line = outputs.get(0).trim().split("\\s+");
    String dirs = line[0];
    String dir=dirs.charAt(0)+"";
    lazzieXY = D(lazzieXY, dir);
    if (lazzieXY<0) throw new ActionErrorException(String.format("[ERROR] Output should contain direction ('N', 'S', 'W', 'E') Given '%s'.", dir));
    lastDir = dir;
    for(int i=0; i<Math.min(dirs.length(),MAX_PATH); i++) { if (DtoI(dirs.charAt(i))>=0) pathDirs.add(DtoI(dirs.charAt(i))); };
    if (terrain.isOpen(X(lazzieXY), Y(lazzieXY))) return;

    //System.out.println(pathDirs);
    String tex = terrain.texture[X(lazzieXY)][Y(lazzieXY)];
    switch (terrain.type[X(lazzieXY)][Y(lazzieXY)]) {
      case 'w': if (tex.startsWith("r")) throw new ActionErrorException(String.format("Stupid cat turned itself into a frozen statue, now can be displayed as an example..."));
                else throw new ActionErrorException(String.format("Silly cat, forgot she hates the water...")); // drowned inside a puddle...
      case 't': if (tex.equals("t3")) throw new ActionErrorException(String.format("Poor kitty, the bushes turned out to be too sharp..."));
                else throw new ActionErrorException(String.format("Silly cat, climbed a tree and could not get down...."));
      case 'f': throw new ActionErrorException(String.format("Silly cat, climbed a tree and could not get down..."));
      case 'h': throw new ActionErrorException(String.format("Foolish cat, run itself straight into home wall. Doors were beside..."));
      case '8': throw new ActionErrorException(String.format("Brave kitty... but you could not win against evil Jack Frost."));
      default: throw new ActionErrorException(String.format("Poor kitty, run into an obstacle and hit herself hard..."));
    }
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    for (int dy = -radius; dy <= radius; dy++)
    {
      for (int dx = -radius; dx <= radius; dx++)
      {
        int nx = X(lazzieXY) + dx;
        int ny = Y(lazzieXY) + dy;

        if (dx*dx+dy*dy > (radius+0.5)*(radius+0.5)) {sb.append('?'); continue;}
        if (dx==0 && dy==0) {sb.append('L'); continue;}
        if (XY(nx, ny)==homeXY) {sb.append('H'); continue;}
        if (terrain.isOpen(nx,ny)) {sb.append('.'); continue;}
        sb.append('#');
      }
      sb.append('\n');
    }
    return sb.toString();
  }
}
