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

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

        private MyCommandLine _cli = new MyCommandLine();
        private Dictionary<string, AirZone> _zones = new Dictionary<string, AirZone>();
        private List<IEnumerator<bool>> _jobs = new List<IEnumerator<bool>>();
        private List<IMyTextSurface> _displays = new List<IMyTextSurface>();
        private StringBuilder _displayBuffer = new StringBuilder();
        private List<IMyGasTank> _oxygenTanks = new List<IMyGasTank>();

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

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

                // TODO: how do we display text on e.g. decorative console blocks?
                // They have configurable screens but the docs say they don't implement this.
                // Meanwhile *every* functional block seems to implement IMyTextSurfaceProvider...
                // It'd probably be safe to just check SurfaceCount... experiment with this later
                if (block is IMyTextSurface)
                {
                    Console.Print($"Adding monitoring display '{block.CustomName}'");
                    _displays.Add(block as IMyTextSurface);
                }
                if (Ini.Get("airMonitor", "display").ToString() != "")
                {
                    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
                    {
                        _displays.Add(provider.GetSurface(displayIndex));
                    }
                }
                if (block is IMyGasTank)
                {
                    _oxygenTanks.Add(block as IMyGasTank);
                }

                if (Ini.Get("airMonitor", "zones").ToString() != "")
                {
                    zones = Ini.Get("airMonitor", "zones").ToString().Split(',');
                }
                else if (Ini.Get("airMonitor", "zone").ToString() != "")
                {
                    zones = new string[] { Ini.Get("airMonitor", "zone").ToString() };
                }

                foreach (string zone in zones)
                {
                    if (!_zones.ContainsKey(zone))
                    {
                        _zones[zone] = new AirZone(this, zone);
                    }
                    _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());
            }
            Runtime.UpdateFrequency = UpdateFrequency.Update100;
            Console.UpdateTickCount();
        }

        public void Main(string argument, UpdateType updateSource)
        {
            Console.UpdateTickCount();

            if (argument != "")
            {
                _cli.TryParse(argument);
                if (_cli.Switch("reset") || _cli.Switch("trigger"))
                {
                    List<string> zonesToControl;
                    if (_cli.ArgumentCount == 0) zonesToControl = _zones.Keys.ToList();
                    else zonesToControl = new List<string>();
                    for (int i = 0; i < _cli.ArgumentCount; i++)
                    {
                        string zone = _cli.Argument(i);
                        if (!_zones.ContainsKey(zone))
                        {
                            Console.Print($"Ignoring non-existent zone '{zone}'");
                            continue;
                        }
                        zonesToControl.Add(zone);
                    }

                    foreach (string zone in zonesToControl)
                    {
                        if (_cli.Switch("reset"))
                        {
                            Console.Print($"Resetting {zone}.");
                            _zones[zone].Reset();
                        }
                        if (_cli.Switch("trigger"))
                        {
                            Console.Print($"Manually triggering {zone}.");
                            _zones[zone].Triggered = true;
                        }
                    }
                }
            }

            foreach (IEnumerator job in _jobs)
            {
                if (job.MoveNext()) continue;
                Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored.");
            }

            _updateDisplays();
        }

        // write diagnostics to any configured display screens
        private void _updateDisplays()
        {
            if (_displays.Count == 0) return;

            _displayBuffer.Clear();
            _displayBuffer.Append("AIR PRESSURE REPORT\n\n");
            foreach (AirZone zone in _zones.Values)
            {
                _displayBuffer.Append(zone.Name);
                _displayBuffer.Append(": ");
                _displayBuffer.Append(zone.Triggered ? "ALARM TRIPPED " : "nominal ");
                foreach (IMyAirVent vent in zone.Vents)
                {
                    _displayBuffer.Append((int)(vent.GetOxygenLevel() * 100F));
                    _displayBuffer.Append("% ");
                }

                _displayBuffer.Append("\n");
            }

            _displayBuffer.Append("\n");
            _displayBuffer.Append("OXYGEN TANK LEVELS\n");
            foreach (IMyGasTank tank in _oxygenTanks)
            {
                _displayBuffer.Append((int)(tank.FilledRatio * 100));
                _displayBuffer.Append("% ");
            }

            foreach (IMyTextSurface display in _displays) display.WriteText(_displayBuffer.ToString());
        }
    }
}
