using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using TMPro;

[ExecuteAlways]
public class Node : MonoBehaviour
{
    [Range(-1, 1)]
    [SerializeField]
    public int Owner = -1;
    public int Units;
    public int id;
    public TMP_Text unitText;
    public Material materialOwnerSelf, materialOwnerOther, materialOwnerNone, materialHover;

    public List<Node> connected;

    public bool hovered;

    private int Nachos = 0;

    void Awake()
    {
        UpdateColor();
    }

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
    }

    private void FixedUpdate()
    {
        CalculateNachos();
    }


    void CalculateNachos()
    {
        Nachos = CycleFinder.FindLargestCycleAmongNeighbors(this).Count;
    }

    // Update is called once per frame
    void Update()
    {
        unitText.enabled = hovered;
        unitText.text = Nachos.ToString();
        unitText.transform.forward = Camera.main.transform.forward;
        UpdateColor();
        UpdateTransform();
    }

    private void OnValidate()
    {
        UpdateColor();
        UpdateTransform();
    }

    private void OnDestroy()
    {
        GameManager gm = FindFirstObjectByType<GameManager>();

        if (gm == null || Application.isPlaying)
            return;

        List<Connection> looseConnections = gm.GetConnections().FindAll(c => c.nodeA == this || c.nodeB == this) ?? new();

        foreach (Connection c in looseConnections)
        {
            if (c.lineRenderer != null)
                DestroyImmediate(c.lineRenderer.gameObject);
        }
    }

    public void UpdateTransform()
    {
        transform.localPosition = transform.localPosition.normalized * 20f;
        if (transform.position != Vector3.zero)
            transform.forward = transform.position;
    }

    public void UpdateColor()
    {
        Material material = null;
        int currentPlayer = GameManager.Instance ? GameManager.Instance.currentPlayer : -1;

        if (hovered)
            material = materialHover;
        else if (Owner == -1 || currentPlayer == -1)
            material = materialOwnerNone;
        else if (Owner == currentPlayer)
            material = materialOwnerSelf;
        else
            material = materialOwnerOther;

        transform.GetChild(0).GetComponent<Renderer>().sharedMaterial = material; 

        hovered = false;
    }

}


public class CycleFinder
{
    private HashSet<Node> allowedNodes;
    private List<Node> bestCycle;
    private Node centerNode;
    private int Owner;

    public CycleFinder(Node center, int owner)
    {
        centerNode = center;
        allowedNodes = new HashSet<Node>(center.connected.Where(obj => obj.Owner == owner));
        bestCycle = new List<Node>();
        Owner = owner;
    }

    public static List<Node> FindLargestCycleAmongNeighbors(Node center)
    {
        var finder = new CycleFinder(center, center.Owner);
        return finder.FindBestCycle();
    }

    private List<Node> FindBestCycle()
    {
        // Versuche von jedem Nachbarknoten aus einen Zyklus zu finden
        foreach (var startNode in allowedNodes)
        {
            var visited = new HashSet<Node>();
            var currentPath = new List<Node>();

            SearchForCycles(startNode, startNode, visited, currentPath);
        }

        return bestCycle;
    }

    private void SearchForCycles(Node startNode, Node currentNode, HashSet<Node> visited, List<Node> path)
    {
        // Aktuellen Knoten zum Pfad hinzufugen
        path.Add(currentNode);
        visited.Add(currentNode);

        // Durchsuche alle Verbindungen des aktuellen Knotens
        foreach (var neighbor in currentNode.connected)
        {
            // Nur Nachbarn des Zentralknotens sind erlaubt
            if (!allowedNodes.Contains(neighbor))
                continue;

            // Wenn wir zum Startknoten zuruckgekehrt sind und mindestens 3 Knoten im Pfad haben
            if (neighbor == startNode && path.Count >= 3)
            {
                // Geschlossenen Zyklus gefunden - prufe ob er besser ist
                if (path.Count > bestCycle.Count)
                {
                    bestCycle = new List<Node>(path);
                }
            }
            // Wenn der Knoten noch nicht besucht wurde, setze die Suche fort
            else if (!visited.Contains(neighbor))
            {
                SearchForCycles(startNode, neighbor, new HashSet<Node>(visited), new List<Node>(path));
            }
        }
    }
}