package com.codingame.game.engine;


import com.codingame.game.Player;
import com.codingame.game.modules.MyViewModule;
import com.codingame.gameengine.core.SoloGameManager;
import com.codingame.gameengine.module.entities.*;
import com.codingame.gameengine.module.tooltip.TooltipModule;

import java.util.ArrayList;
import java.util.HashMap;

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

public class Viewer
{

  private GraphicEntityModule graphic;
  private SoloGameManager<Player> manager;
  private TooltipModule tooltip;
  private Engine engine;
  private MyViewModule myview;


  public static int COL_GLOBAL_BG = 0x000000;
  public static int COL_BFOG = 0x000000; //0x00AA00;

  public static double WFOG_ALPHA = 0.5;
  public static double PATH_ALPHA = 0.5;

  public static int Z_GLOBAL_BG = -2;
  public static int Z_TERRAIN   =  0;
  public static int Z_WFOG      =  1;
  public static int Z_BFOG      =  2;
  public static int Z_LAZZIE    =  3;
  public static int Z_PATH     =   4;
  public static int Z_ARROW    =   5;
  public static int Z_SUMMARY   =  6;

  public static int TILESIZE=64; public static int TILESCALE=4;

  public static int ENDSCALE=6;
  public static int GROUP_OPTIMIZER_BUCKET_SIZE=25;

  public static String SPRITE_FOG = "o"; // o1

  public Group allGroup;
  public Group[] subAllGroup = new Group[30];
  public Group viewportGroup;
  public Group pathGroup;
  private Sprite arrowSprite;
  private SpriteAnimation lazzieAnim;

  private Entity[] blackXY = new Entity[MAX_MAP*MAX_MAP];
  private Entity[] whiteXY = new Entity[MAX_MAP*MAX_MAP];
  private Entity[][][] path = new Entity[2*MAX_PATH+1][2*MAX_PATH+1][4];
  private ArrayList<Group> grouptmps = new ArrayList<>();

  private HashMap<String, String[]> lazzieAnimSprites = new HashMap<String, String[]>() {{
    put("S", new String[] {"L0", "L1"});//, "L0", "L1"});
    put("N", new String[] {"L2", "L3"});//, "L2", "L3"});
    put("W", new String[] {"L4", "L5"});//, "L4", "L5"});
    put("E", new String[] {"L6", "L7"});//, "L6", "L7"});
    put("X", new String[] {"L0"});
    put("!S", new String[] {"La"});
    put("!N", new String[] {"Lb"});
    put("!W", new String[] {"Lc"});
    put("!E", new String[] {"Ld"});
    put("!X", new String[] {"L0"});
    put("H", new String[] {"L2"});
  }};



  public Viewer(GraphicEntityModule graphic, SoloGameManager<Player> manager, TooltipModule tooltip, Engine engine)
  {
    this.graphic = graphic;
    this.manager = manager;
    this.tooltip = tooltip;
    this.engine = engine;
  }

  public void init()
  {
    lazzieAnim = createLazzieAnim(engine.lastDir);
    arrowSprite = graphic.createSprite().setImage("arrow.png").setX(VIS_SCREEN_WIDTH/2).setY(VIS_SCREEN_HEIGHT/2).setAnchorY(0.5).setScale(TILESCALE).setZIndex(Z_ARROW);

    allGroup = graphic.createGroup();
    for (int i = 0; i < subAllGroup.length; i++) {subAllGroup[i] = graphic.createGroup(); allGroup.add(subAllGroup[i]); }
    initPaths();
    viewportGroup = graphic.createGroup(allGroup, lazzieAnim, arrowSprite);

    graphic.createRectangle().setX(0).setWidth(VIS_SCREEN_WIDTH).setY(0).setHeight(VIS_SCREEN_HEIGHT).setFillColor(COL_GLOBAL_BG).setZIndex(Z_GLOBAL_BG);


  }

  private void shiftGroup(Group g, String dir) {
    switch (dir){
      case "E": g.setX(g.getX()-TILESIZE); return;
      case "W": g.setX(g.getX()+TILESIZE); return;
      case "N": g.setY(g.getY()+TILESIZE); return;
      case "S": g.setY(g.getY()-TILESIZE); return;
      default: return;
    }
  }

  public void step(int turn)
  {
    lazzieAnim.setImages(lazzieAnimSprites.get(engine.lastDir));
    graphic.commitEntityState(0.0, lazzieAnim);

    arrowSprite.setRotation(computeAngle(X(engine.lazzieXY), Y(engine.lazzieXY), X(engine.homeXY), Y(engine.homeXY)));
    //arrowSprite.setVisible(Math.abs(X(engine.lazzieXY) - X(engine.homeXY)) + Math.abs(Y(engine.lazzieXY) - Y(engine.homeXY)) > 2);
    arrowSprite.setAlpha(Math.abs(X(engine.lazzieXY) - X(engine.homeXY)) + Math.abs(Y(engine.lazzieXY) - Y(engine.homeXY)) > 2 ? 1.0 : 0.0);

    //updatePaths();
    shiftGroup(allGroup,engine.lastDir);
    updatePaths();

    Group turnGroup = graphic.createGroup(); int turnGroupSize = 0;
    grouptmps.add(turnGroup);

    for(int xy:engine.newlyRendered) {
      Sprite s = createSpriteTile(engine.getTexture(xy), xy, engine.lazzieXY, Z_TERRAIN);
      turnGroup.add(s); turnGroupSize++;

      blackXY[xy] = createRectTile(COL_BFOG,  xy, engine.lazzieXY, Z_BFOG); // alternatively SpriteTile // todo if (blackXY[xy]==null) ?
      turnGroup.add(blackXY[xy]); turnGroupSize++;
    }

    for (int dx = -engine.radius-1; dx <= engine.radius+1; dx++) {
      for (int dy = -engine.radius - 1; dy <= engine.radius + 1; dy++) {
        int xy = XY(X(engine.lazzieXY) + dx, Y(engine.lazzieXY) + dy);
        if (whiteXY[xy]==null) {
            whiteXY[xy] = createSpriteTile(SPRITE_FOG, xy, engine.lazzieXY, Z_WFOG).setAlpha(WFOG_ALPHA);
            turnGroup.add(whiteXY[xy]);  turnGroupSize++;
        }
        whiteXY[xy].setAlpha(engine.whiteFogXY[xy] ? WFOG_ALPHA : 0.0);
      }
    }

    //if (turnGroupSize!=0) allGroup.add(turnGroup.setX(turnGroup.getX()- allGroup.getX()).setY(turnGroup.getY()- allGroup.getY()));
    if (turnGroupSize!=0) subAllGroup[turn/GROUP_OPTIMIZER_BUCKET_SIZE].add(turnGroup.setX(turnGroup.getX()- allGroup.getX()).setY(turnGroup.getY()- allGroup.getY()));


    for(int xy:engine.newlyBlackDissappear) {blackXY[xy].setAlpha(0.0);}
  }


  public void showSummary(boolean passed)
  {
    if (passed) lazzieAnim.setImages(lazzieAnimSprites.get("H"));
    else        lazzieAnim.setImages(lazzieAnimSprites.get("!"+engine.lastDir));
    graphic.commitEntityState(0.0, lazzieAnim);

    arrowSprite.setVisible(false);
    graphic.commitEntityState(0.0, arrowSprite);

    graphic.commitWorldState(0);


    Entity bg = graphic.createRectangle().setX(0).setWidth(VIS_SCREEN_WIDTH).setY(0).setHeight(VIS_SCREEN_HEIGHT).setFillColor(COL_GLOBAL_BG).setZIndex(Z_SUMMARY);
    graphic.commitEntityState(0.2, bg);
    Sprite pic = graphic.createSprite().setImage(engine.endscr+ (passed? "w.png":"l.png")).setX(VIS_SCREEN_WIDTH/2).setY(VIS_SCREEN_HEIGHT/2)
            .setScale(ENDSCALE).setZIndex(Z_SUMMARY).setAlpha(1).setAnchor(0.5);
    graphic.commitEntityState(0.3, pic);

//    Sprite end = graphic.createSprite().setImage(passed?"passed.png":"failed.png").setX(960).setY(570)
//            .setScale(40.8).setZIndex(Z_SUMMARY).setAlpha(1).setAnchor(0.5);
//    graphic.commitEntityState(0.25, end);
//    end.setScale(0.5);
//    graphic.commitEntityState(0.4, end);


    //win.setScale(1);
    //score.setScale(0.9);
    //graphic.commitEntityState(0.4, win, score);
    //win.setScale(1.1);
    //score.setScale(1.0);
    //graphic.commitEntityState(0.5, win, score);
  }


  private void initPaths() {
    pathGroup = graphic.createGroup();

    for (int dx = -MAX_PATH; dx <= MAX_PATH; dx++) {
      for (int dy = -MAX_PATH; dy <= MAX_PATH; dy++) {
        if (Math.abs(dx)+Math.abs(dy) > MAX_PATH) continue;

        int x = VIS_SCREEN_WIDTH/2 + dx*TILESIZE;int y = VIS_SCREEN_HEIGHT/2 + dy*TILESIZE;
        for (int d=0; d<4; d++) {
          path[MAX_PATH + dx][MAX_PATH + dy][d] = graphic.createSprite().setImage(lazzieAnimSprites.get(ItoD[d])[0]).setX(x).setY(y).setAnchor(0.5).setScale(TILESCALE).setAlpha(PATH_ALPHA);
          pathGroup.add(path[MAX_PATH + dx][MAX_PATH + dy][d]);
        }
      }
    }
    pathGroup.setZIndex(Z_PATH);
    allGroup.add(pathGroup);
  }

  private void updatePaths() {

    System.out.println(engine.lastDir);

    for (int i=0; i<2*MAX_PATH+1; i++) {
      for (int j=0; j<2*MAX_PATH+1; j++) {
        for (int d=0; d<4; d++) {
          if (path[i][j][d] != null && path[i][j][d].isVisible()) { path[i][j][d].setVisible(false);graphic.commitEntityState(0.0,path[i][j][d] ); }
        }
      }
    }
    int dx = engine.pathDirs.size()>0 ? ItoDX[ItoREV[engine.pathDirs.get(0)]] : 0;
    int dy = engine.pathDirs.size()>0 ? ItoDY[ItoREV[engine.pathDirs.get(0)]] : 0;
    for (int d: engine.pathDirs) {
      dx+=ItoDX[d]; dy+=ItoDY[d];
      path[MAX_PATH+dx][MAX_PATH+dy][d].setVisible(true);
      graphic.commitEntityState(0.0, path[MAX_PATH+dx][MAX_PATH+dy][d]);
    }

    shiftGroup(pathGroup,REVDIR(engine.lastDir));
    graphic.commitEntityState(0.0, pathGroup);

  }

  public static double computeAngle(double x1, double y1, double x2, double y2)
  {
    double x = x2 - x1;
    double y = y2 - y1;
    return Math.atan2(y, x);
  }

  public Sprite createSpriteTile(String name, int xy, int centerxy, int z) {
    int x = VIS_SCREEN_WIDTH/2 + (X(xy)-X(centerxy))*TILESIZE;
    int y = VIS_SCREEN_HEIGHT/2 + (Y(xy)-Y(centerxy))*TILESIZE;
    return graphic.createSprite().setImage(name).setX(x).setY(y).setAnchor(0.5).setScale(TILESCALE).setZIndex(z);
  }

  public Rectangle createRectTile(int col, int xy, int centerxy, int z) {
    int x = VIS_SCREEN_WIDTH/2 + (X(xy)-X(centerxy))*TILESIZE;
    int y = VIS_SCREEN_HEIGHT/2 + (Y(xy)-Y(centerxy))*TILESIZE;
    return graphic.createRectangle().setFillColor(col).setX(x-TILESIZE/2).setY(y-TILESIZE/2).setWidth(TILESIZE).setHeight(TILESIZE).setZIndex(z);
  }

  public SpriteAnimation createLazzieAnim(String dir) {
    int x = VIS_SCREEN_WIDTH/2;
    int y = VIS_SCREEN_HEIGHT/2;
    return graphic.createSpriteAnimation().setImages(lazzieAnimSprites.get(dir)).setLoop(true).setX(x).setY(y).setZIndex(Z_LAZZIE).setAnchor(0.5).setScale(TILESCALE);
  }
}
