package fr.monkhanny.dorfromantik.game;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Arrays;

import fr.monkhanny.dorfromantik.enums.Biome;
import fr.monkhanny.dorfromantik.enums.TileOrientation;
import fr.monkhanny.dorfromantik.utils.Hexagon;
import fr.monkhanny.dorfromantik.utils.HexagonDrawer;


public class Tile extends Cell {

  private HashMap<TileOrientation, Biome> sideBiomes = new HashMap<>();


  public Tile(Board board, int x, int y, int radius, Biome... biomes) {
    super(board, x, y, radius);
    TileOrientation[] sides = TileOrientation.values();
    for (int i = 0; i < sides.length; i++) {
      this.sideBiomes.put(sides[i], biomes[i]);
    }
  }

 
  public Tile(Board board, int x, int y, int radius) {
    super(board, x, y, radius);
    this.assignRandomBiomes();
  }

  
  public Tile(Board board, Point center, int radius, Biome... biomes) {
    this(board, center.x, center.y, radius, biomes);
  }


  public Tile(Board board, Point center, int radius) {
    this(board, center.x, center.y, radius);
  }

 
  public void setBiomes(Biome... biomes) {
    TileOrientation[] sides = TileOrientation.values();
    for (int i = 0; i < sides.length; i++) {
      this.sideBiomes.put(sides[i], biomes[i]);
    }
  }

  
  public void assignRandomBiomes() {
    Game game = this.getBoard().getGame();
    Biome[] biomes = Biome.values();
    TileOrientation[] sides = TileOrientation.values();
    
    this.sideBiomes.clear();

    Biome firstBiome = biomes[game.getRandomInt(biomes.length)];
    biomes = Arrays.stream(biomes).filter(b -> b != firstBiome).toArray(Biome[]::new);
    Biome secondBiome = biomes[game.getRandomInt(biomes.length)];

    int firstBiomeSideCount = game.getRandomInt(sides.length + 1);
    int firstBiomeSideOffset = game.getRandomInt(sides.length);

    for (int i = 0; i < sides.length; i++) {
      TileOrientation side = sides[(i + firstBiomeSideOffset) % sides.length];
      Biome assignedBiome = (i < firstBiomeSideCount) ? firstBiome : secondBiome;
      this.sideBiomes.put(side, assignedBiome);
    }
  }

  public Biome getBiome(TileOrientation side) {
    return this.sideBiomes.get(side);
  }

 
  public Biome getDominantBiome() {
    TileOrientation[] sides = TileOrientation.values();

    int firstBiomeCount = 0;
    int secondBiomeCount = 0;
    Biome firstBiome = this.getBiome(sides[0]);
    Biome secondBiome = null;

    for (TileOrientation side : sides) {
      Biome currentBiome = this.getBiome(side);
      if (currentBiome.equals(firstBiome)) {
        firstBiomeCount++;
      } else {
        secondBiome = currentBiome;
        secondBiomeCount++;
      }
    }

    if (firstBiomeCount > secondBiomeCount) {
      return firstBiome;
    } else if (firstBiomeCount < secondBiomeCount) {
      return secondBiome;
    }

    return null;
  }


  public Biome[] getBiomes() {
    Biome[] biomes = new Biome[TileOrientation.values().length];
    for (TileOrientation side : TileOrientation.values()) {
      biomes[side.ordinal()] = this.getBiome(side);
    }
    return biomes;
  }

  
  public void rotate(boolean clockwise) {
    TileOrientation[] sides = TileOrientation.values();
    HashMap<TileOrientation, Biome> newBiomesMap = new HashMap<>();

    for (int i = 0; i < sides.length; i++) {
      TileOrientation side = sides[i];
      TileOrientation newSide = clockwise ? sides[(i + 1) % sides.length] : sides[(i + sides.length - 1) % sides.length];
      newBiomesMap.put(newSide, this.sideBiomes.get(side));
    }

    this.sideBiomes = newBiomesMap;
    this.repaint();
  }

 
  public boolean containsBiome(Biome biome) {
    for (TileOrientation side : TileOrientation.values()) {
      if (this.getBiome(side) == biome) {
        return true;
      }
    }
    return false;
  }


  public boolean isAdjacentTo(Tile otherTile) {
    
    int radius = this.getRadius(); // Utilisation du rayon pour la distance correcte

    // Calculer les différences de position
    int deltaX = otherTile.getXCoord() - this.getXCoord();
    int deltaY = otherTile.getYCoord() - this.getYCoord();

    // Calculer la distance euclidienne entre les deux tuiles
    double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

    // Distance attendue pour des tuiles adjacentes dans une grille hexagonale
    double expectedDistance = (int) (1.5 * radius); // ou une autre valeur calculée correctement

    // Définir une tolérance pour la précision
    double epsilon = 15.0;

    // Vérifier si la différence de distance est dans la tolérance
    if (Math.abs(distance - expectedDistance) < epsilon) {
        return true;
    }
    return false;
}


public Tile getNeighbor(TileOrientation orientation) {
  int radius = this.getRadius();
  int neighborX = this.getXCoord();
  int neighborY = this.getYCoord();

  if (orientation == TileOrientation.NORTH) {
      neighborY -= (int) (Math.sqrt(3) * radius);
  } else if (orientation == TileOrientation.NORTH_EAST) {
      neighborX += (int) (1.5 * radius);
      neighborY -= (int) (Math.sqrt(3) / 2 * radius);
  } else if (orientation == TileOrientation.SOUTH_EAST) {
      neighborX += (int) (1.5 * radius);
      neighborY += (int) (Math.sqrt(3) / 2 * radius);
  } else if (orientation == TileOrientation.SOUTH) {
      neighborY += (int) (Math.sqrt(3) * radius);
  } else if (orientation == TileOrientation.SOUTH_WEST) {
      neighborX -= (int) (1.5 * radius);
      neighborY += (int) (Math.sqrt(3) / 2 * radius);
  } else if (orientation == TileOrientation.NORTH_WEST) {
      neighborX -= (int) (1.5 * radius);
      neighborY -= (int) (Math.sqrt(3) / 2 * radius);
  }

  // Rechercher la tuile à la position calculée
  return this.getBoard().getTileAt(neighborX, neighborY);
}



  public TileOrientation determineSide(int x, int y) {
    int radius = this.getRadius();
    TileOrientation[] sides = TileOrientation.values();
    double angle = Cell.to360Degrees(Math.toDegrees(Math.atan2(y - radius, x - radius)) + 120);

    int floorSide = (int) Math.floor(Cell.to360Degrees(angle - 2) / 60);
    int ceilSide = (int) Math.floor(Cell.to360Degrees(angle + 2) / 60);

    if (floorSide == ceilSide) {
      return sides[floorSide];
    }

    Biome floorBiome = this.getBiome(sides[floorSide]);
    Biome dominantBiome = this.getDominantBiome();

    if (dominantBiome == null && y > radius) {
      return TileOrientation.SOUTH;
    }

    if (dominantBiome == null && y < radius) {
      return TileOrientation.NORTH;
    }

    return floorBiome.equals(dominantBiome) ? sides[ceilSide] : sides[floorSide];
  }

  protected void drawTileAt(Graphics g, int x, int y, float scale) {
      // Sauvegarde de l'état actuel du graphique
      Graphics2D g2d = (Graphics2D) g.create();

      // Déplacement du contexte graphique à la position souhaitée
      g2d.translate(x, y);

      // Appel de la méthode de dessin de la tuile à la nouvelle position
      paintTile(g2d, scale);
      g2d.dispose();
  }


  /**
   * Méthode principale de dessin de la tuile.
   * 
   * @param g     Le contexte graphique
   * @param scale L'échelle de la tuile
   */
  protected void paintTile(Graphics g, float scale) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        int radius = this.getRadius();
        Point center = new Point(radius, radius);
        radius = (int) (radius * scale);
        Hexagon hexagon = new Hexagon(center, radius);

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setClip(hexagon);

        HexagonDrawer hexDrawer = new HexagonDrawer(this);
        hexDrawer.drawHexagon(g2d, radius, center);

        g2d.setClip(null);
        g2d.setStroke(new BasicStroke((int) radius / 15));
        g2d.setColor(Color.BLACK);
        g2d.drawPolygon(hexagon);

        g2d.dispose();
    }

}
