using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;


[ExecuteAlways]
public class GameManager : MonoBehaviour
{
    public Transform ConnectionParent;
    public GameObject ConnectionPrefab;
    public Transform NodeParent;
    public GameObject NodePrefab;

    public GameObject hoverObject;
    public TMP_Text energyText;
    public TMP_Text currentPlayerText;
    public TMP_Text timerText;

    public GameObject actionListItemPrefab;
    public Transform actionListParent;
    public Button finishTurnBtn;

    [SerializeField]
    public List<Player> players;

    public int currentPlayer = -1;
    public Node pressedNode = null;
    public Connection pressedCon = null;
    public KeyCode altActionKey = KeyCode.LeftShift;

    public float zoomSpeed = 20f;
    public float minFOV = 20f;
    public float maxFOV = 80f;

    public float roundTime = 90;

    public static GameManager Instance { get; private set; }

    public bool regenerateOnChange = false;

    [SerializeField]
    public List<Action> actions;

    [HideInInspector] public float minConnectionLength = 6;
    [HideInInspector] public float maxConnectionLength = 6;
    [HideInInspector] public int nodeCount = 100;
    [HideInInspector] public int hoverRadiusCon = 50;
    [HideInInspector] public int hoverRadiusNode = 50;
    [HideInInspector] public int selectedLevel = -1;

    [SerializeField] [HideInInspector] public List<LevelData> levels = new List<LevelData>();

    public enum ActionType { NONE, MOVE_HALF_UNITS, MOVE_ALL_UNITS, ATTACK_NODE_WITH_HALF, ATTACK_NODE_WITH_ALL, ATTACK_CON_WITH_HALF, ATTACK_CON_WITH_ALL, CONSTRUCT_CON, DESTRUCT_CON, EXPLODE_CON };
    
    public Dictionary<ActionType, string> ActionTypeDescription = new Dictionary<ActionType, string>
    {
        { ActionType.NONE, "You should really never see this O_o \n(-1 Turn)" },
        { ActionType.MOVE_HALF_UNITS, "Read that title again... \n(1 Turn)" },
        { ActionType.MOVE_ALL_UNITS, "Read that title again... \n(1 Turn)" },
        { ActionType.ATTACK_NODE_WITH_HALF, "Read that title again... \n(1 Turn)" },
        { ActionType.ATTACK_NODE_WITH_ALL, "Read that title again... \n(1 Turn)" },
        { ActionType.ATTACK_CON_WITH_HALF, "Attacks the enemy node using half of your Units to break the pending connection \n(1 Turn)" },
        { ActionType.ATTACK_CON_WITH_ALL, "Attacks the enemy node using all of your Units to break the pending connection \n(1 Turn)" },
        { ActionType.CONSTRUCT_CON, "Begins construction of a connection \n(2 Turns)" },
        { ActionType.DESTRUCT_CON, "Begins deconstruction of a connection \n(2 Turns)" },
        { ActionType.EXPLODE_CON, "Begins blazingly fast deconstruction of a connection \n(1 Turn)" }
    };


    public Dictionary<ActionType, string> ActionTypeText = new Dictionary<ActionType, string>
    {
        { ActionType.NONE, "" },
        { ActionType.MOVE_HALF_UNITS, "Move troops (1/2)" },
        { ActionType.MOVE_ALL_UNITS, "Move troops" },
        { ActionType.ATTACK_NODE_WITH_HALF, "Attack (1/2)" },
        { ActionType.ATTACK_NODE_WITH_ALL, "Attack" },
        { ActionType.ATTACK_CON_WITH_HALF, "Interrupt Construction (1/2)" },
        { ActionType.ATTACK_CON_WITH_ALL, "Interrupt Construction" },
        { ActionType.CONSTRUCT_CON, "Construct" },
        { ActionType.DESTRUCT_CON, "Destruct" },
        { ActionType.EXPLODE_CON, "Explode (immediately)" }
    };

    public Dictionary<ActionType, int> ActionTypeEnergyUsage = new Dictionary<ActionType, int>
    {
        { ActionType.NONE, 0},
        { ActionType.MOVE_HALF_UNITS, 1},
        { ActionType.MOVE_ALL_UNITS, 1},
        { ActionType.ATTACK_NODE_WITH_HALF, 1},
        { ActionType.ATTACK_NODE_WITH_ALL, 1},
        { ActionType.ATTACK_CON_WITH_HALF, 1},
        { ActionType.ATTACK_CON_WITH_ALL, 1},
        { ActionType.CONSTRUCT_CON, 1},
        { ActionType.DESTRUCT_CON, 1},
        { ActionType.EXPLODE_CON, 2 }
    };

    [Serializable]
    public struct Action
    {
        public ActionType intendedAction;

        public bool onConnection;
        public bool altAction;
        public int nodeFromId;
        public int nodeToId;
        public int amount;
        public int player;
        public int tempDaniel;
    }

    [Serializable]
    public class LevelData
    {
        [Serializable]
        public class NodeData
        {
            public Vector3 position;
            public int owner;
            public int units;
        }

        [Serializable]
        public class ConnectionData
        {
            public int nodeAIndex;
            public int nodeBIndex;
            public bool allowed = true;
        }

        public float minConnectionLength = 0;
        public float maxConnectionLength = 0;
        public List<NodeData> nodes = new List<NodeData>();
        public List<ConnectionData> connections = new List<ConnectionData>();
    }

    float timer = 0;
    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            if (Application.isPlaying)
                Destroy(gameObject);
            return;
        }

        Instance = this;
        if (Application.isPlaying)
            DontDestroyOnLoad(gameObject);


        GetConnections().ForEach(obj => obj.DelConnect());
        GetConnections().ForEach(obj => obj.SetConnect());

    }

    private void Start()
    {
#if UNITY_EDITOR
        LoadLevelData(selectedLevel);
#endif
    }

    private void Update()
    {
        hoverObject.SetActive(false);

        Player player = players.Find(p => p.id == currentPlayer);

        int totalEnergyCost = 0;
        actions.ForEach(a => totalEnergyCost += ActionTypeEnergyUsage[a.intendedAction]);

        if (player != null && Application.isPlaying)
        {
            timer -= Time.deltaTime;
            if (timer < 0)
            {
                ExecuteTurn();
                return;
            }
            timerText.text = "[" + (int)timer / 60 + ":" + ((int)timer % 60 < 10 ? "0" : "") + $"{((int)timer % 60)}" + "]";
            energyText.text = string.Concat(Enumerable.Repeat("□", Math.Max(0, player.energy - totalEnergyCost))) + string.Concat(Enumerable.Repeat("-", Math.Min(player.energy, totalEnergyCost)));
            currentPlayerText.text = "[ Player " + currentPlayer + " ]";
        }


        if (Input.GetMouseButtonDown(0))
        {
            pressedNode = GetNodes().Find(n => n.hovered && n.Owner == currentPlayer);
        }
        if (Input.GetMouseButton(0))
        {
            if(pressedNode != null)
            {
                Node node = GetNodes().Find(n => n.hovered && n != pressedNode);

                if (node)
                {
                    Connection con = GetConnections().Find(c => (c.nodeA == pressedNode && c.nodeB == node) || (c.nodeB == pressedNode && c.nodeA == node)) ?? null;
                    ActionType possibleAction = CalcActionBetweenNodes(currentPlayer, pressedNode.id, node.id, Input.GetKey(altActionKey));
                    hoverObject.GetComponentsInChildren<TMP_Text>()[0].text = ActionTypeText[possibleAction];
                    hoverObject.GetComponentsInChildren<TMP_Text>()[1].text = ActionTypeDescription[possibleAction];
                    hoverObject.GetComponentsInChildren<TMP_Text>()[2].text = string.Concat(Enumerable.Repeat("□", ActionTypeEnergyUsage[possibleAction]));
                    hoverObject.SetActive(true);

                    if (con != null && con.allowed)
                        con.hovered = true;
                }
            }
        }
        if (Input.GetMouseButtonUp(0))
        {
            if (pressedNode != null) 
            {
                Node toNode = GetNodes().Find(n => n.hovered && pressedNode != n);

                if (toNode)
                {
                    Connection con = GetConnections().Find(c => (c.nodeA == pressedNode && c.nodeB == toNode) || (c.nodeB == pressedNode && c.nodeA == toNode)) ?? null;
                    if (con != null && con.allowed)                    
                        PushNewAction(pressedNode.id, toNode.id);
                }
            }
        }

        if (!hoverObject.activeSelf)
        {
            Connection hoveredCon = GetConnections().Find(c => c.hovered);
            if (hoveredCon != null)
            {
                ActionType possibleAction = CalcActionBetweenNodes(currentPlayer, hoveredCon.nodeA.id, hoveredCon.nodeB.id, Input.GetKey(altActionKey), true);

                if (possibleAction != ActionType.NONE)
                {

                    hoverObject.GetComponentsInChildren<TMP_Text>()[0].text = ActionTypeText[possibleAction];
                    hoverObject.GetComponentsInChildren<TMP_Text>()[1].text = ActionTypeDescription[possibleAction];
                    hoverObject.GetComponentsInChildren<TMP_Text>()[2].text = string.Concat(Enumerable.Repeat("□", ActionTypeEnergyUsage[possibleAction]));
                    hoverObject.SetActive(true);
                }
            }
        }
    }

    public ActionType CalcActionBetweenNodes(int player, int nodeFromId, int nodeToId, bool altAction = false, bool onConnection = false)
    {
        Node nodeTo = GetNodes().Find(n => n.id == nodeToId);
        Node nodeFrom = GetNodes().Find(n => n.id == nodeFromId);
        Connection con = GetConnections().Find(c => (c.nodeA == nodeTo && c.nodeB == nodeFrom) || (c.nodeA == nodeFrom && c.nodeB == nodeTo));

        if (con == null || con.allowed == false)
            return ActionType.NONE;

        // Clicked on connection
        if (onConnection)
        {
            if (con.state == Connection.BuildState.BUILT && nodeFrom.Owner == player && nodeTo.Owner == player)
            {
                return altAction ? ActionType.EXPLODE_CON : ActionType.DESTRUCT_CON;
            }
            else if (con.state == Connection.BuildState.EMPTY && (nodeFrom.Owner == player || nodeTo.Owner == player))
            {
                return ActionType.CONSTRUCT_CON;
            }
        }
        // Dragged owned nodeFrom to nodeTo
        else if (nodeFrom.Owner == player)
        {
            // To Owned
            if (nodeTo.Owner == player)
            {
                switch (con.state)
                {
                    case Connection.BuildState.EMPTY:
                        return ActionType.CONSTRUCT_CON;

                    case Connection.BuildState.CONSTRUCTING:
                    case Connection.BuildState.DECONSTRUCTING:
                        return ActionType.NONE;

                    case Connection.BuildState.BUILT:
                        return altAction ? ActionType.MOVE_HALF_UNITS : ActionType.MOVE_ALL_UNITS;
                }
            }
            // To Hostile
            else if (nodeTo.Owner >= 0)
            {
                switch (con.state)
                {
                    case Connection.BuildState.EMPTY:
                        return ActionType.CONSTRUCT_CON;

                    case Connection.BuildState.CONSTRUCTING:
                        return altAction ? ActionType.ATTACK_CON_WITH_HALF : ActionType.ATTACK_CON_WITH_ALL;

                    case Connection.BuildState.DECONSTRUCTING:
                        return ActionType.NONE;

                    case Connection.BuildState.BUILT:
                        return altAction ? ActionType.ATTACK_NODE_WITH_HALF : ActionType.ATTACK_NODE_WITH_ALL;
                }
            }
            // To Unclaimed
            else
            {
                switch (con.state)
                {
                    case Connection.BuildState.EMPTY:
                        return ActionType.CONSTRUCT_CON;

                    case Connection.BuildState.CONSTRUCTING:
                    case Connection.BuildState.DECONSTRUCTING:
                        return ActionType.NONE;

                    case Connection.BuildState.BUILT:
                        return altAction ? ActionType.MOVE_HALF_UNITS : ActionType.MOVE_ALL_UNITS;
                }
            }
        }

        return ActionType.NONE;
    }
    public List<Node> GetNodes() => NodeParent.GetComponentsInChildren<Node>().ToList();
    public List<Connection> GetConnections() => ConnectionParent != null ? ConnectionParent.GetComponentsInChildren<Connection>()?.ToList() ?? new() : new();

    public void UpdateConstructions()
    {
        foreach(Connection con in GetConnections())
        {
            if (con.constructingPlayerId == currentPlayer)
            {
                if (con.state == Connection.BuildState.CONSTRUCTING)
                {
                    con.state = Connection.BuildState.BUILT;
                    con.SetConnect();
                }
                    
                else if (con.state == Connection.BuildState.DECONSTRUCTING)
                {
                    con.state = Connection.BuildState.EMPTY;
                    con.DelConnect();
                }

                con.constructingPlayerId = -1;
            }
        }
    }

    public void ExecuteTurn()
    {
        Player player = players.Find(p => p.id == currentPlayer);

        if (player == null)
        {
            Debug.LogWarning("Player " + currentPlayer + " not found!");
            return;
        }


        int totalEnergyCost = 0;
        actions.ForEach(a => totalEnergyCost += ActionTypeEnergyUsage[a.intendedAction]);

        if (totalEnergyCost > player.energy)
        {
            Debug.Log("Insuficcient Energy for Turn! (needs " + totalEnergyCost + " has " + player.energy + ")");
            return;
        }

        actions.ForEach(a => ExecuteAction(a));
        for (int i = 0; i < actionListParent.childCount; i++)
        {
            Destroy(actionListParent.GetChild(i).gameObject);
        }
        actions.ForEach(action =>
        {
            var to = GetNodes().Where(n => n.id == action.nodeToId).First();
            var from = GetNodes().Where(n => n.id == action.nodeFromId).First();

            to.ExpectedUnitDiff = 0;
            from.ExpectedUnitDiff = 0;
        });
       

        actions.Clear();

        // Select next player
        currentPlayer++;
        Player nextPlayer = players.Find(p => p.id == currentPlayer);

        // Executed turn of last Player => Round ended
        if(nextPlayer == null)     
            currentPlayer = 0;

        UpdateConstructions();
        nextPlayer = players.Find(p => p.id == currentPlayer);

        // Refill energy
        nextPlayer.energy = 3;
        nextPlayer.GetOwnedNodes().ForEach(node => {
            nextPlayer.energy += node.Nachos;
            if (node.Nachos > 0)
            {
                node.Units += 5 * node.Nachos;
            }
            });

        timer = roundTime;
    }

    public void PushAction(Action action)
    {
        var ui = Instantiate(actionListItemPrefab, actionListParent);

        ui.GetComponentInChildren<TMP_Text>().text = ActionTypeText[action.intendedAction] + " (" + action.nodeFromId + ">" + action.nodeToId + ")";
        ui.GetComponentInChildren<Button>().onClick.AddListener(() => {
            actions.Remove(action);
            var to = GetNodes().Where(n => n.id == action.nodeToId).First();
            var from = GetNodes().Where(n => n.id == action.nodeFromId).First();

            switch (action.intendedAction)
            {
                case ActionType.MOVE_HALF_UNITS:
                    from.ExpectedUnitDiff += action.tempDaniel;
                    to.ExpectedUnitDiff -= action.tempDaniel;
                    break;
                case ActionType.MOVE_ALL_UNITS:
                    from.ExpectedUnitDiff += action.tempDaniel;
                    to.ExpectedUnitDiff -= action.tempDaniel;
                    break;
                case ActionType.ATTACK_NODE_WITH_HALF:
                case ActionType.ATTACK_CON_WITH_HALF:
                    from.ExpectedUnitDiff += action.tempDaniel;
                    to.ExpectedUnitDiff += action.tempDaniel;
                    break;
                case ActionType.ATTACK_NODE_WITH_ALL:
                case ActionType.ATTACK_CON_WITH_ALL:
                    from.ExpectedUnitDiff += action.tempDaniel;
                    to.ExpectedUnitDiff += action.tempDaniel;
                    break;
            }

            Destroy(ui);
            });

        var from = GetNodes().Where(n => n.id == action.nodeFromId).First();
        var to = GetNodes().Where(n => n.id == action.nodeToId).First();

        // TODO: Alle diffs durchgehen nach jedem break
        switch (action.intendedAction)
        {
            case ActionType.MOVE_HALF_UNITS:
                to.ExpectedUnitDiff += (from.Units + from.ExpectedUnitDiff) / 2;
                action.tempDaniel += (from.Units + from.ExpectedUnitDiff) / 2;
                from.ExpectedUnitDiff -= (from.Units + from.ExpectedUnitDiff) / 2;
                break;
            case ActionType.MOVE_ALL_UNITS:
                to.ExpectedUnitDiff += (from.Units+ from.ExpectedUnitDiff) ;
                action.tempDaniel += (from.Units+ from.ExpectedUnitDiff) ;
                from.ExpectedUnitDiff -= (from.Units+ from.ExpectedUnitDiff) ;
                break;
            case ActionType.ATTACK_NODE_WITH_HALF:
            case ActionType.ATTACK_CON_WITH_HALF:
                to.ExpectedUnitDiff -= (from.Units + from.ExpectedUnitDiff) / 2;
                action.tempDaniel += (from.Units + from.ExpectedUnitDiff) / 2;
                from.ExpectedUnitDiff -= (from.Units + from.ExpectedUnitDiff) / 2;
                break;
            case ActionType.ATTACK_NODE_WITH_ALL:
            case ActionType.ATTACK_CON_WITH_ALL:
                to.ExpectedUnitDiff -= (from.Units+ from.ExpectedUnitDiff) ;
                action.tempDaniel += (from.Units+ from.ExpectedUnitDiff) ;
                from.ExpectedUnitDiff -= (from.Units+ from.ExpectedUnitDiff) ;
                break;
        }
        actions.Add(action);
    }
    public Action PushNewAction(int nodeFromId, int nodeToId, bool onConnection = false, int? player = null, bool ? altAction = null)
    {
        int _player = player != null ? (int)player : currentPlayer;
        bool _altAction = altAction != null ? (bool)altAction : Input.GetKey(altActionKey);

        ActionType type = CalcActionBetweenNodes(_player, nodeFromId, nodeToId, _altAction, onConnection);
        Action action = new Action
        {
            intendedAction = type,
            nodeFromId = nodeFromId,
            nodeToId = nodeToId,
            player = _player,
            altAction = _altAction,
            onConnection = onConnection            
        };

        int totalEnergyCost = 0;
        actions.ForEach(a => totalEnergyCost += ActionTypeEnergyUsage[a.intendedAction]);
        totalEnergyCost += ActionTypeEnergyUsage[action.intendedAction];

        if (type != ActionType.NONE && players.Find(p => p.id == _player).energy >= totalEnergyCost )
            PushAction(action);
        
        return action;
    }
    

    public void ExecuteAction(Action action)
    {
        Node nodeTo = GetNodes().Find(n => n.id == action.nodeToId);
        Node nodeFrom = GetNodes().Find(n => n.id == action.nodeFromId);
        Connection con = GetConnections().Find(c => (c.nodeA == nodeTo && c.nodeB == nodeFrom) || (c.nodeA == nodeFrom && c.nodeB == nodeTo));
        Player p = players.Find(p => (p.id == action.player));

        if (p == null)
        {
            Debug.LogWarning("Player " + action.player + " not found!");
            return;
        }

        ActionType possibleAction = CalcActionBetweenNodes(action.player, action.nodeFromId, action.nodeToId, action.altAction, action.onConnection);

        if (possibleAction != action.intendedAction)
        {
            Debug.LogWarning("Intended action not possible (TODO: execute counter action) PS : BONUS");
            return;
        }

        if (p.energy < ActionTypeEnergyUsage[action.intendedAction])
        {
            Debug.Log("Insuficcient Energy! (needs " + ActionTypeEnergyUsage[action.intendedAction] + " has " + p.energy + ")");
            return;
        }

        switch (action.intendedAction)
        {
            case ActionType.NONE:
                break;

            case ActionType.MOVE_HALF_UNITS:
            case ActionType.MOVE_ALL_UNITS:
                Debug.Log("Moving units from " + nodeTo.id + " to " + nodeFrom.id);

                if (action.intendedAction == ActionType.MOVE_ALL_UNITS)
                {
                    nodeTo.Units += nodeFrom.Units;
                    nodeFrom.Units = 0;
                }
                else if (action.intendedAction == ActionType.MOVE_HALF_UNITS)
                {
                    int diff = Mathf.CeilToInt(nodeFrom.Units / 2);
                    nodeTo.Units += diff;
                    nodeFrom.Units -= diff;
                }
                if (nodeFrom.Units <= 0)
                    nodeFrom.Owner = -1;
                if (nodeTo.Units > 0)
                    nodeTo.Owner = currentPlayer;
                break;

            case ActionType.ATTACK_NODE_WITH_HALF:
                Debug.Log("Attacking hostile units with half from " + nodeTo.id + " to " + nodeFrom.id);
                int attackers = nodeFrom.Units / 2;
                nodeTo.Units -= attackers;
                nodeFrom.Units -= attackers;

                if (nodeTo.Units < 0)
                {
                    nodeTo.Owner = currentPlayer;
                    nodeTo.Units = Math.Abs(nodeTo.Units);
                } else

                if (nodeFrom.Units == 0)
                {
                    nodeFrom.Owner = -1;
                    nodeFrom.Units = 0;
                }

                break;

            case ActionType.ATTACK_NODE_WITH_ALL:
                Debug.Log("Attacking hostile units with all from " + nodeTo.id + " to " + nodeFrom.id);

                attackers = nodeFrom.Units;
                nodeTo.Units -= attackers;
                nodeFrom.Units -= attackers;

                if (nodeTo.Units < 0)
                {
                    nodeTo.Owner = nodeTo.Units == 0 ? -1 : currentPlayer;
                    nodeTo.Units = Math.Abs(nodeTo.Units);
                } else

                if (nodeFrom.Units == 0)
                {
                    nodeFrom.Owner = -1;
                    nodeFrom.Units = 0;
                }
                break;

            case ActionType.ATTACK_CON_WITH_HALF:
                Debug.Log("Attacking hostile construction with half from " + nodeTo.id + " to " + nodeFrom.id);

                attackers = nodeFrom.Units / 2;
                nodeTo.Units -= attackers;
                nodeFrom.Units = Math.Max(0, nodeFrom.Units -attackers);

                if (nodeTo.Units <= 0)
                {
                    nodeTo.Owner = nodeTo.Units == 0 ? -1 : currentPlayer;
                    con.state = Connection.BuildState.EMPTY;
                    con.DelConnect();
                }
                break;

            case ActionType.ATTACK_CON_WITH_ALL:
                Debug.Log("Attacking hostile construction with all from " + nodeTo.id + " to " + nodeFrom.id);

                attackers = nodeFrom.Units;
                nodeTo.Units -= attackers;
                nodeFrom.Units = Math.Max(0, nodeFrom.Units - attackers);

                if (nodeTo.Units <= 0)
                {
                    nodeTo.Owner = nodeTo.Units == 0 ? -1 : currentPlayer;
                    con.state = Connection.BuildState.EMPTY;
                    con.DelConnect();
                }
                break;

            case ActionType.CONSTRUCT_CON:
                Debug.Log("Starting construction from " + nodeTo.id + " to " + nodeFrom.id);
                con.state = Connection.BuildState.CONSTRUCTING;
                con.SetConnect();
                con.constructingPlayerId = action.player;
                break;

            case ActionType.DESTRUCT_CON:
                Debug.Log("Starten deconstruction from " + nodeTo.id + " to " + nodeFrom.id);
                con.state = Connection.BuildState.DECONSTRUCTING;
                con.DelConnect();
                con.constructingPlayerId = action.player;
                break;

            case ActionType.EXPLODE_CON:
                if (p.energy < 2)
                {
                    Debug.Log("Not enough energy");
                    return;
                }
                p.energy -= 2;
                Debug.Log("Exploding connection from " + nodeTo.id + " to " + nodeFrom.id);
                con.state = Connection.BuildState.EMPTY;
                con.DelConnect();
                break;
        }
    }
#if UNITY_EDITOR
    public void GenerateAlongSphere()
    {
        for (int i = NodeParent.childCount - 1; i >= 0; i--)
            DestroyImmediate(NodeParent.GetChild(i).gameObject);

        float radius = 20f;
        float goldenRatio = (1f + Mathf.Sqrt(5f)) / 2f;
        float angleIncrement = 2f * Mathf.PI * goldenRatio;

        for (int i = 0; i < nodeCount; i++)
        {
            float t = (float)i / nodeCount; // von 0 bis 1
            float inclination = Mathf.Acos(1f - 2f * t);
            float azimuth = angleIncrement * i;

            float x = Mathf.Sin(inclination) * Mathf.Cos(azimuth);
            float y = Mathf.Sin(inclination) * Mathf.Sin(azimuth);
            float z = Mathf.Cos(inclination);

            Vector3 pos = new Vector3(x, y, z) * radius;

            var auto = PrefabUtility.InstantiatePrefab(NodePrefab, NodeParent) as GameObject;
            auto.GetComponent<Node>().id = i;
            auto.transform.localPosition = pos;
        }
    }

    public void GenerateConnections()
    {
        for (int i = ConnectionParent.childCount - 1; i >= 0; i--)
            DestroyImmediate(ConnectionParent.GetChild(i).gameObject);

        var nodes = GetNodes();

        foreach (Node nodeA in nodes)
        {
            if (nodeA == null) continue;
            foreach (Node nodeB in nodes)
            {
                if (nodeB == null) continue;
                bool conExists = false;
                float dist = Vector3.Distance(nodeA.transform.position, nodeB.transform.position);
                if (nodeA == nodeB || dist > maxConnectionLength)
                    continue;

                foreach (Connection con in GetConnections())
                {
                    if ((con.nodeA == nodeA && con.nodeB == nodeB) || (con.nodeA == nodeB && con.nodeB == nodeA))
                    {
                        conExists = true;
                        break;
                    }
                }
                if (!conExists)
                {
                    AddConnection(nodeA, nodeB, dist < minConnectionLength);
                }
            }
                   
        }
    }

    public void AddConnection(Node nodeA, Node nodeB, bool allowed = true)
    {
        var newCon = PrefabUtility.InstantiatePrefab(ConnectionPrefab, ConnectionParent).GetComponent<Connection>();
        newCon.nodeA = nodeA;
        newCon.nodeB = nodeB;
        newCon.allowed = allowed;
    }

    public void LoadLevelData(int index)
    {
        if(index >= levels.Count)
        {
            Debug.LogWarning("LevelIndex out of range");
            return;
        }

        for (int i = NodeParent.childCount - 1; i >= 0; i--)
            DestroyImmediate(NodeParent.GetChild(i).gameObject);

        foreach (LineRenderer line in ConnectionParent.GetComponentsInChildren<LineRenderer>())
            DestroyImmediate(line.gameObject);


        for(int i = 0; i < levels[index].nodes.Count; i++)
        {
            var nodeData = levels[index].nodes[i];
            var auto = PrefabUtility.InstantiatePrefab(NodePrefab, NodeParent) as GameObject;
            auto.transform.localPosition = nodeData.position;

            Node node = auto.GetComponent<Node>();
            node.Owner = nodeData.owner;
            node.Units = nodeData.units;
            node.id = i;
        }

        var currentNodes = GetNodes();

        int idx = 0;
        foreach (Node node in currentNodes)
            node.id = idx++;

        currentNodes = GetNodes();


        foreach (LevelData.ConnectionData conData in levels[index].connections)        
            AddConnection(currentNodes[conData.nodeAIndex], currentNodes[conData.nodeBIndex], conData.allowed);
        
        selectedLevel = index;
        minConnectionLength = levels[index].minConnectionLength;
        maxConnectionLength = levels[index].maxConnectionLength;
        nodeCount = currentNodes.Count;
    }

    public void SaveLevelData(int index = -1)
    {
        LevelData data = new LevelData();

        data.minConnectionLength = minConnectionLength;
        data.maxConnectionLength = maxConnectionLength;

        // Nodes speichern
        foreach (var node in GetNodes())
        {
            data.nodes.Add(new LevelData.NodeData
            {
                position = node.transform.localPosition,
                owner = node.Owner,
                units = node.Units
            });
        }

        // Connections speichern
        foreach (var con in GetConnections())
        {
            if (con.nodeA.id >= 0 && con.nodeB.id >= 0)
            {
                data.connections.Add(new LevelData.ConnectionData
                {
                    nodeAIndex = con.nodeA.id,
                    nodeBIndex = con.nodeB.id,
                    allowed = con.allowed
                });
            }
        }

        if (index == -1 || index >= levels.Count)
            levels.Add(data);
        else
            levels[index] = data;

        int newIndex = index < 0 ? levels.Count - 1 : index;
        selectedLevel = newIndex;
    }
#endif
}