using Sandbox.ModAPI.Ingame;
using System.Collections.Generic;

namespace IngameScript
{
    partial class Program
    {
        [Flags]
        public enum FailoverState
        {
            Standby = 0x1,
            Failover = 0x2,
            Active = 0x4,
        }

        public class FailoverManager
        {
            private Program _program;
            private IConsole _console;

            private string _id;
            private int _rank;
            private bool _active = false;
            private SortedDictionary<int, IMyProgrammableBlock> _nodes = new SortedDictionary<int, IMyProgrammableBlock>();

            public FailoverManager(Program program, IConsole console, MyIni ini)
            {
                _program = program;
                _console = new PrefixedConsole(console, "FailoverManager");
                _ini = ini;

                // Read our config
                bool configExists = _ini.TryParse(_program.Me.CustomData);
                if (!configExists)
                {
                    _console.Print("No failover config, assuming we are single instance.");
                    _active = true;
                    return;
                }

                _id = _ini.Get("failover", "id").ToString();
                _rank = _ini.Get("failover", "rank").ToInt32(-1);

                if (_id == "" || _rank == -1)
                {
                    _console.Print("Failover config invalid. Assuming we are single instance.");
                    _active = true;
                    return;
                }

                List<IMyProgrammableBlock> allPBs = new List<IMyProgrammableBlock>();
                _program.GridTerminalSystem.GetBlocksOfType(allPBs, blockFilter);

                foreach (IMyProgrammableBlock node in allPBs)
                {
                    _ini.TryParse(node.CustomData);

                    string foreignId = _ini.Get("failover", "id");
                    if (foreignId != _id) continue;

                    int foreignRank = _ini.Get("failover", "rank");
                    if (foreignRank == -1) continue;

                    _nodes[foreignRank] = node;
                }
            }

            // returns true if we should become the active node
            public FailoverState ActiveCheck()
            {
                // Once we're the active node, we will stay active for the life of the script.
                // TODO: test the assumption that the script restarts from scratch on disable/enable...
                //  if not we may need additional logic.
                if (_active) return FailoverState.Active;

                foreach (KeyValuePair<int, IMyProgrammableBlock> kvp in _nodes)
                {
                    // If we have a higher priority than the nodes we've checked so far,
                    // we must be the active node.
                    if (_rank < kvp.Key)
                    {
                        _active = true;
                        return FailoverState.Failover | FailoverState.Active;
                    }

                    // We've found an active node with higher priority than us.
                    if (!kvp.Value.Closed && kvp.Value.Enabled) return FailoverState.Standby;
                }

                // We're the last node standing. Let's hope we don't go down.
                _active = true;
                return FailoverState.Failover | FailoverState.Active;
            }

            private bool blockFilter(IMyProgrammableBlock block)
            {
                return block.IsSameConstructAs(_program.Me) && MyIni.HasSection(block.CustomData, "failover");
            }
        }
    }
}