<?php
declare(strict_types=1);

namespace App\Services;

class GeometryService
{
    private array $galaxyMap = [];
    private int $width;
    private int $height;

    /**
     * Génère les positions pour une grille hexagonale en forme d'anneau galactique
     * Format paysage 4:3, centre et pourtour vides
     */
    public function generateGalaxyPositions(int $totalSectors): array
    {
        // Calculer les dimensions approximatives (format 4:3)
        $area = $totalSectors * 2.5; // Facteur pour tenir compte des espaces vides
        $this->height = (int) sqrt($area / (4/3));
        $this->width = (int) ($this->height * 4/3);

        // Assurer des dimensions minimales
        $this->width = max($this->width, 20);
        $this->height = max($this->height, 15);

        // Initialiser la carte
        $this->galaxyMap = array_fill(0, $this->height, array_fill(0, $this->width, false));

        // Calculer les centres et rayons pour l'anneau
        $centerX = $this->width / 2;
        $centerY = $this->height / 2;
        $outerRadiusX = $this->width * 0.45;
        $outerRadiusY = $this->height * 0.45;
        $innerRadiusX = $this->width * 0.15;
        $innerRadiusY = $this->height * 0.15;

        $positions = [];

        // Générer les positions dans l'anneau
        for ($y = 0; $y < $this->height; $y++) {
            for ($x = 0; $x < $this->width; $x++) {
                // Calculer la distance normalisée du centre (ellipse)
                $normalizedDistanceX = ($x - $centerX) / $outerRadiusX;
                $normalizedDistanceY = ($y - $centerY) / $outerRadiusY;
                $outerDistance = sqrt($normalizedDistanceX * $normalizedDistanceX + $normalizedDistanceY * $normalizedDistanceY);

                $normalizedInnerDistanceX = ($x - $centerX) / $innerRadiusX;
                $normalizedInnerDistanceY = ($y - $centerY) / $innerRadiusY;
                $innerDistance = sqrt($normalizedInnerDistanceX * $normalizedInnerDistanceX + $normalizedInnerDistanceY * $normalizedInnerDistanceY);

                // Vérifier si la position est dans l'anneau
                if ($outerDistance <= 1.0 && $innerDistance >= 1.0) {
                    // Ajouter de la variation pour rendre la forme plus organique
                    $noise = $this->getNoiseValue($x, $y) * 0.3;
                    if ($outerDistance <= (0.85 + $noise) && $innerDistance >= (1.15 - $noise)) {
                        // Grille hexagonale : décaler les lignes paires
                        $hexX = $x;
                        $hexY = $y;
                        if ($y % 2 === 1) {
                            $hexX += 0.5;
                        }

                        $positions[] = ['x' => $x, 'y' => $y];
                        $this->galaxyMap[$y][$x] = true;
                    }
                }
            }
        }

        // Mélanger et limiter au nombre de secteurs demandé
        shuffle($positions);
        return array_slice($positions, 0, $totalSectors);
    }

    /**
     * Génère une valeur de bruit simple pour rendre la forme plus organique
     */
    private function getNoiseValue(int $x, int $y): float
    {
        $seed = $x * 374761393 + $y * 668265263;
        $seed = ($seed ^ ($seed >> 13)) * 1274126177;
        return (($seed ^ ($seed >> 16)) & 0x7fffffff) / 0x7fffffff * 2.0 - 1.0;
    }

    /**
     * Trouve les voisins directs d'une position dans une grille hexagonale
     */
    public function getHexNeighbors(int $x, int $y): array
    {
        $neighbors = [];

        // Les voisins dépendent de si la ligne est paire ou impaire
        if ($y % 2 === 0) {
            // Ligne paire
            $directions = [
                [-1, -1], [0, -1],  // Nord-Ouest, Nord-Est
                [-1, 0],  [1, 0],   // Ouest, Est
                [-1, 1],  [0, 1]    // Sud-Ouest, Sud-Est
            ];
        } else {
            // Ligne impaire
            $directions = [
                [0, -1],  [1, -1],  // Nord-Ouest, Nord-Est
                [-1, 0],  [1, 0],   // Ouest, Est
                [0, 1],   [1, 1]    // Sud-Ouest, Sud-Est
            ];
        }

        foreach ($directions as [$dx, $dy]) {
            $newX = $x + $dx;
            $newY = $y + $dy;

            if ($newX >= 0 && $newX < $this->width && $newY >= 0 && $newY < $this->height) {
                $neighbors[] = ['x' => $newX, 'y' => $newY];
            }
        }

        return $neighbors;
    }

    /**
     * Calcule la distance Manhattan entre deux secteurs
     */
    public function getManhattanDistance(int $x1, int $y1, int $x2, int $y2): int
    {
        return abs($x1 - $x2) + abs($y1 - $y2);
    }

    /**
     * Génère une représentation graphique de la carte galactique
     */
    public function printMap(array $sectorPositions = []): string
    {
        if (empty($this->galaxyMap)) {
            return "Aucune carte générée";
        }

        // Créer un index des positions occupées
        $occupiedPositions = [];
        foreach ($sectorPositions as $position) {
            $occupiedPositions[$position['y']][$position['x']] = true;
        }

        $map = "";
        for ($y = 0; $y < $this->height; $y++) {
            // Indentation pour simuler la grille hexagonale
            if ($y % 2 === 1) {
                $map .= " ";
            }

            for ($x = 0; $x < $this->width; $x++) {
                if (isset($occupiedPositions[$y][$x])) {
                    $map .= "X ";
                } elseif ($this->galaxyMap[$y][$x]) {
                    $map .= "O "; // Position disponible mais non occupée
                } else {
                    $map .= "- ";
                }
            }
            $map .= "\n";
        }

        return $map;
    }

    /**
     * Trouve les voisins directs occupés par des secteurs
     */
    public function getOccupiedNeighbors(int $x, int $y, array $sectorPositions): array
    {
        $neighbors = $this->getHexNeighbors($x, $y);
        $occupiedNeighbors = [];

        // Créer un index des positions occupées
        $occupiedPositions = [];
        foreach ($sectorPositions as $position) {
            $occupiedPositions[$position['y']][$position['x']] = true;
        }

        foreach ($neighbors as $neighbor) {
            if (isset($occupiedPositions[$neighbor['y']][$neighbor['x']])) {
                $occupiedNeighbors[] = $neighbor;
            }
        }

        return $occupiedNeighbors;
    }
}