using Sandbox.ModAPI.Ingame;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VRage.Game.ModAPI.Ingame.Utilities;
using VRageMath;

namespace IngameScript
{
    public partial class Program : MyGridProgram, IConsoleProgram
    {
        public MyIni Ini { get; } = new MyIni();
        public IConsole Console { get; private set; }

        private FailoverManager _failover;
        private List<IEnumerator<bool>> _jobs = new List<IEnumerator<bool>>();

        private Dictionary<string, AirZone> _zones = new Dictionary<string, AirZone>();
        private List<AirDoor> _doors = new List<AirDoor>();
        private List<IMyLightingBlock> _lights = new List<IMyLightingBlock>();

        private List<AirMonitorDisplay> _displays = new List<AirMonitorDisplay>();
        private List<IMyGasTank> _oxygenTanks = new List<IMyGasTank>();

        private StringBuilder _displayBuffer = new StringBuilder();

        public Program()
        {
            Console = new MainConsole(this, "Air Pressure Monitor");
            _failover = new FailoverManager(this, Console, Ini);

            List<IMyTextSurface> surfaces = new List<IMyTextSurface>();

            // Find all tagged objects and build out zones
            List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
            GridTerminalSystem.GetBlocksOfType(blocks, blockFilter);
            foreach (IMyTerminalBlock block in blocks)
            {
                Ini.TryParse(block.CustomData);
                string[] zones = new string[] { };

                if (Ini.Get("airMonitor", "zones").ToString() != "")
                {
                    zones = Ini.Get("airMonitor", "zones").ToString().Split(',');
                }
                foreach (string zone in zones)
                {
                    if (!_zones.ContainsKey(zone)) _zones[zone] = new AirZone(this, zone);
                }

                if (block is IMyGasTank)
                {
                    _oxygenTanks.Add(block as IMyGasTank);
                }

                else if (block is IMyTextSurface)
                {
                    // TODO: behave differently if zone is defined
                    Console.Print($"Adding monitoring display '{block.CustomName}'");
                    surfaces.Add(block as IMyTextSurface);
                }

                else if (Ini.Get("airMonitor", "display").ToString() != "")
                {
                    // TODO: behave differently if zone is defined
                    int displayIndex = Ini.Get("airMonitor", "display").ToInt32();
                    IMyTextSurfaceProvider provider = block as IMyTextSurfaceProvider;
                    if (provider.SurfaceCount <= displayIndex)
                    {
                        Console.Print($"Invalid display index '{displayIndex}' in block '{block.CustomName}'");
                    }
                    else
                    {
                        surfaces.Add(provider.GetSurface(displayIndex));
                    }
                }

                else if (block is IMyDoor)
                {
                    AirDoor door = new AirDoor(block as IMyDoor);
                    _doors.Add(door);
                    foreach (string zone in zones) _zones[zone].AddDoor(door);
                }

                else if (block is IMyLightingBlock)
                {
                    _lights.Add(block as IMyLightingBlock);
                    foreach (string zone in zones) _zones[zone].AddBlock(block);
                }

                else
                {
                    foreach (string zone in zones)
                    {
                        _zones[zone].AddBlock(block);
                    }
                }
            }

            Console.Print($"Found {_zones.Count} zones:");
            foreach (KeyValuePair<string, AirZone> kvp in _zones)
            {
                AirZone zone = kvp.Value;
                Console.Print(kvp.Key);
                _jobs.Add(zone.Monitor());
            }

            foreach (IMyTextSurface surface in surfaces)
            {
                _displays.Add(new AirMonitorDisplay(surface, _zones.Values.ToList()));
            }

            Runtime.UpdateFrequency = UpdateFrequency.Update100;
            Console.UpdateTickCount();
        }

        public void Main(string argument, UpdateType updateSource)
        {
            Console.UpdateTickCount();
            if (_failover.ActiveCheck() == FailoverState.Standby) return;

            // Light indicators should be set/unset independent of the triggered states and actions.
            foreach (IMyLightingBlock light in _lights) light.Color = Color.White;
            foreach (AirDoor door in _doors) door.Triggered = false;

            foreach (IEnumerator job in _jobs)
            {
                // Run the monitoring jobs, which will update the lights and door states
                if (!job.MoveNext()) Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored.");
            }

            // Unlock all safe doors, but leave them closed.
            // Note that this will make it difficult to manually lock/disable these doors.
            foreach (AirDoor door in _doors)
            {
                if (door.Door.Enabled == false && !door.Triggered)
                {
                    Console.Print($"DEBUG: {door.Door.CustomName} re-enabled");
                    door.Door.Enabled = true;
                }
            }

            updateDisplays();
        }

        private void updateDisplays()
        {
            foreach (AirMonitorDisplay display in _displays)
            {
                display.Draw();
            }
        }

        private bool blockFilter(IMyTerminalBlock block)
        {
            return block.IsSameConstructAs(this.Me) && MyIni.HasSection(block.CustomData, "airMonitor");
        }
    }
}
