using System;
using System.Collections.Generic;
using System.Text;
using Sandbox.Game.Entities;
using Sandbox.ModAPI;
using VRage.Game;
using VRage.Game.Components;
using VRage.ModAPI;
using VRage.Utils;
using VRageMath;
using Draygo.API;
using System.IO;
using static Draygo.API.HudAPIv2;
using VRage.Game.ObjectBuilders;
using BlendTypeEnum = VRageRender.MyBillboard.BlendTypeEnum;
using System.Globalization;

namespace Zkillerproxy.Compass
{
    [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation)]
    public class Client : MySessionComponentBase
    {
        //All the variables!
        private bool IsDedicated = false;
        private bool CompassEnabled = true;
        private bool ShouldEnableClock = true;
        private bool PlayerWarnedShouldEnableClockFalse = false;
        private bool ClockEnabled = false;
        private bool CompassUseDeg = false;
        private bool EnableSleeping = false;
        public DateTime UTCTime = new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        public DateTime LocalTime = new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        private string ClockTimeType = "12HourTime";
        private HudAPIv2 API;
        private Vector3 BaseSouth = new Vector3(0f, 1f, 0f);
        private MyPlanet Planet;
        private Vector3D PlanetCenter;
        private Vector3 BaseSunDir = new Vector3();
        private float PI = (float) Math.PI;
        private BillBoardHUDMessage CompassElement;
        private List<HUDMessage> messages = new List<HUDMessage>();
        private Dictionary<long, MyPlanet> planets = new Dictionary<long, MyPlanet>();
        private static List<CompassCharacter> characters = new List<CompassCharacter>()
        {
            new CompassCharacter("S", 0),
            new CompassCharacter("•", (float) -0.0125),
            new CompassCharacter("•", (float) -0.025),
            new CompassCharacter("•", (float) -0.0375),
            new CompassCharacter("•", (float) -0.05),
            new CompassCharacter("•", (float) -0.0625),
            new CompassCharacter("•", (float) -0.075),
            new CompassCharacter("•", (float) -0.0875),
            new CompassCharacter("•", (float) -0.1),
            new CompassCharacter("•", (float) -0.1125),
            new CompassCharacter("SSE", (float) -0.125),
            new CompassCharacter("•", (float) -0.1375),
            new CompassCharacter("•", (float) -0.15),
            new CompassCharacter("•", (float) -0.1625),
            new CompassCharacter("•", (float) -0.175),
            new CompassCharacter("•", (float) -0.1875),
            new CompassCharacter("•", (float) -0.2),
            new CompassCharacter("•", (float) -0.2125),
            new CompassCharacter("•", (float) -0.225),
            new CompassCharacter("•", (float) -0.2375),
            new CompassCharacter("SE", (float) -0.25),
            new CompassCharacter("•", (float) -0.2625),
            new CompassCharacter("•", (float) -0.275),
            new CompassCharacter("•", (float) -0.2875),
            new CompassCharacter("•", (float) -0.3),
            new CompassCharacter("•", (float) -0.3125),
            new CompassCharacter("•", (float) -0.325),
            new CompassCharacter("•", (float) -0.3375),
            new CompassCharacter("•", (float) -0.35),
            new CompassCharacter("•", (float) -0.3625),
            new CompassCharacter("ESE", (float) -0.375),
            new CompassCharacter("•", (float) -0.3875),
            new CompassCharacter("•", (float) -0.4),
            new CompassCharacter("•", (float) -0.4125),
            new CompassCharacter("•", (float) -0.425),
            new CompassCharacter("•", (float) -0.4375),
            new CompassCharacter("•", (float) -0.45),
            new CompassCharacter("•", (float) -0.4625),
            new CompassCharacter("•", (float) -0.475),
            new CompassCharacter("•", (float) -0.4875),
            new CompassCharacter("E", (float) -0.5),
            new CompassCharacter("•", (float) -0.5125),
            new CompassCharacter("•", (float) -0.525),
            new CompassCharacter("•", (float) -0.5375),
            new CompassCharacter("•", (float) -0.55),
            new CompassCharacter("•", (float) -0.5625),
            new CompassCharacter("•", (float) -0.575),
            new CompassCharacter("•", (float) -0.5875),
            new CompassCharacter("•", (float) -0.6),
            new CompassCharacter("•", (float) -0.6125),
            new CompassCharacter("ENE", (float) -0.625),
            new CompassCharacter("•", (float) -0.6375),
            new CompassCharacter("•", (float) -0.65),
            new CompassCharacter("•", (float) -0.6625),
            new CompassCharacter("•", (float) -0.675),
            new CompassCharacter("•", (float) -0.6875),
            new CompassCharacter("•", (float) -0.7),
            new CompassCharacter("•", (float) -0.7125),
            new CompassCharacter("•", (float) -0.725),
            new CompassCharacter("•", (float) -0.7375),
            new CompassCharacter("NE", (float) -0.75),
            new CompassCharacter("•", (float) -0.7625),
            new CompassCharacter("•", (float) -0.775),
            new CompassCharacter("•", (float) -0.7875),
            new CompassCharacter("•", (float) -0.8),
            new CompassCharacter("•", (float) -0.8125),
            new CompassCharacter("•", (float) -0.825),
            new CompassCharacter("•", (float) -0.8375),
            new CompassCharacter("•", (float) -0.85),
            new CompassCharacter("•", (float) -0.8625),
            new CompassCharacter("NNE", (float) -0.875),
            new CompassCharacter("•", (float) -0.8875),
            new CompassCharacter("•", (float) -0.9),
            new CompassCharacter("•", (float) -0.9125),
            new CompassCharacter("•", (float) -0.925),
            new CompassCharacter("•", (float) -0.9375),
            new CompassCharacter("•", (float) -0.95),
            new CompassCharacter("•", (float) -0.9625),
            new CompassCharacter("•", (float) -0.975),
            new CompassCharacter("•", (float) -0.9875),
            new CompassCharacter("N", -1),
            new CompassCharacter("•", (float) 0.8875),
            new CompassCharacter("•", (float) 0.9),
            new CompassCharacter("•", (float) 0.9125),
            new CompassCharacter("•", (float) 0.925),
            new CompassCharacter("•", (float) 0.9375),
            new CompassCharacter("•", (float) 0.95),
            new CompassCharacter("•", (float) 0.9625),
            new CompassCharacter("•", (float) 0.975),
            new CompassCharacter("•", (float) 0.9875),
            new CompassCharacter("NNW", (float) 0.875),
            new CompassCharacter("•", (float) 0.7625),
            new CompassCharacter("•", (float) 0.775),
            new CompassCharacter("•", (float) 0.7875),
            new CompassCharacter("•", (float) 0.8),
            new CompassCharacter("•", (float) 0.8125),
            new CompassCharacter("•", (float) 0.825),
            new CompassCharacter("•", (float) 0.8375),
            new CompassCharacter("•", (float) 0.85),
            new CompassCharacter("•", (float) 0.8625),
            new CompassCharacter("NW", (float) 0.75),
            new CompassCharacter("•", (float) 0.6375),
            new CompassCharacter("•", (float) 0.65),
            new CompassCharacter("•", (float) 0.6625),
            new CompassCharacter("•", (float) 0.675),
            new CompassCharacter("•", (float) 0.6875),
            new CompassCharacter("•", (float) 0.7),
            new CompassCharacter("•", (float) 0.7125),
            new CompassCharacter("•", (float) 0.725),
            new CompassCharacter("•", (float) 0.7375),
            new CompassCharacter("WNW", (float) 0.625),
            new CompassCharacter("•", (float) 0.5125),
            new CompassCharacter("•", (float) 0.525),
            new CompassCharacter("•", (float) 0.5375),
            new CompassCharacter("•", (float) 0.55),
            new CompassCharacter("•", (float) 0.5625),
            new CompassCharacter("•", (float) 0.575),
            new CompassCharacter("•", (float) 0.5875),
            new CompassCharacter("•", (float) 0.6),
            new CompassCharacter("•", (float) 0.6125),
            new CompassCharacter("W", (float) 0.5),
            new CompassCharacter("•", (float) 0.3875),
            new CompassCharacter("•", (float) 0.4),
            new CompassCharacter("•", (float) 0.4125),
            new CompassCharacter("•", (float) 0.425),
            new CompassCharacter("•", (float) 0.4375),
            new CompassCharacter("•", (float) 0.45),
            new CompassCharacter("•", (float) 0.4625),
            new CompassCharacter("•", (float) 0.475),
            new CompassCharacter("•", (float) 0.4875),
            new CompassCharacter("WSW", (float) 0.375),
            new CompassCharacter("•", (float) 0.2625),
            new CompassCharacter("•", (float) 0.275),
            new CompassCharacter("•", (float) 0.2875),
            new CompassCharacter("•", (float) 0.3),
            new CompassCharacter("•", (float) 0.3125),
            new CompassCharacter("•", (float) 0.325),
            new CompassCharacter("•", (float) 0.3375),
            new CompassCharacter("•", (float) 0.35),
            new CompassCharacter("•", (float) 0.3625),
            new CompassCharacter("SW", (float) 0.25),
            new CompassCharacter("•", (float) 0.1375),
            new CompassCharacter("•", (float) 0.15),
            new CompassCharacter("•", (float) 0.1625),
            new CompassCharacter("•", (float) 0.175),
            new CompassCharacter("•", (float) 0.1875),
            new CompassCharacter("•", (float) 0.2),
            new CompassCharacter("•", (float) 0.2125),
            new CompassCharacter("•", (float) 0.225),
            new CompassCharacter("•", (float) 0.2375),
            new CompassCharacter("SSW", (float) 0.125),
            new CompassCharacter("•", (float) 0.0125),
            new CompassCharacter("•", (float) 0.025),
            new CompassCharacter("•", (float) 0.0375),
            new CompassCharacter("•", (float) 0.05),
            new CompassCharacter("•", (float) 0.0625),
            new CompassCharacter("•", (float) 0.075),
            new CompassCharacter("•", (float) 0.0875),
            new CompassCharacter("•", (float) 0.1),
            new CompassCharacter("•", (float) 0.1125)
        };
        private static List<CompassCharacter> charactersDeg = new List<CompassCharacter>()
        {
            new CompassCharacter("180", 0),
            new CompassCharacter("175", (float) -0.0277),
            new CompassCharacter("170", (float) -0.0555),
            new CompassCharacter("165", (float) -0.0833),
            new CompassCharacter("160", (float) -0.1111),
            new CompassCharacter("155", (float) -0.1388),
            new CompassCharacter("150", (float) -0.1666),
            new CompassCharacter("145", (float) -0.1944),
            new CompassCharacter("140", (float) -0.2222),
            new CompassCharacter("135", (float) -0.2499),
            new CompassCharacter("130", (float) -0.2777),
            new CompassCharacter("125", (float) -0.3055),
            new CompassCharacter("120", (float) -0.3333),
            new CompassCharacter("115", (float) -0.3611),
            new CompassCharacter("110", (float) -0.3888),
            new CompassCharacter("105", (float) -0.4166),
            new CompassCharacter("100", (float) -0.4444),
            new CompassCharacter("95", (float) -0.4722),
            new CompassCharacter("90", (float) -0.5),
            new CompassCharacter("85", (float) -0.5277),
            new CompassCharacter("80", (float) -0.5555),
            new CompassCharacter("75", (float) -0.5833),
            new CompassCharacter("70", (float) -0.6111),
            new CompassCharacter("65", (float) -0.6388),
            new CompassCharacter("60", (float) -0.6666),
            new CompassCharacter("55", (float) -0.6944),
            new CompassCharacter("50", (float) -0.7222),
            new CompassCharacter("45", (float) -0.7499),
            new CompassCharacter("40", (float) -0.7777),
            new CompassCharacter("35", (float) -0.8055),
            new CompassCharacter("30", (float) -0.8333),
            new CompassCharacter("25", (float) -0.8611),
            new CompassCharacter("20", (float) -0.8888),
            new CompassCharacter("15", (float) -0.9166),
            new CompassCharacter("10", (float) -0.9444),
            new CompassCharacter("5", (float) -0.9722),
            new CompassCharacter("0", -1),
            new CompassCharacter("185", (float) 0.0277),
            new CompassCharacter("190", (float) 0.0555),
            new CompassCharacter("195", (float) 0.0833),
            new CompassCharacter("200", (float) 0.1111),
            new CompassCharacter("205", (float) 0.1388),
            new CompassCharacter("210", (float) 0.1666),
            new CompassCharacter("215", (float) 0.1944),
            new CompassCharacter("220", (float) 0.2222),
            new CompassCharacter("225", (float) 0.2499),
            new CompassCharacter("230", (float) 0.2777),
            new CompassCharacter("235", (float) 0.3055),
            new CompassCharacter("240", (float) 0.3333),
            new CompassCharacter("245", (float) 0.3611),
            new CompassCharacter("250", (float) 0.3888),
            new CompassCharacter("255", (float) 0.4166),
            new CompassCharacter("260", (float) 0.4444),
            new CompassCharacter("265", (float) 0.4722),
            new CompassCharacter("270", (float) 0.5),
            new CompassCharacter("275", (float) 0.5277),
            new CompassCharacter("280", (float) 0.5555),
            new CompassCharacter("285", (float) 0.5833),
            new CompassCharacter("290", (float) 0.6111),
            new CompassCharacter("295", (float) 0.6388),
            new CompassCharacter("300", (float) 0.6666),
            new CompassCharacter("305", (float) 0.6944),
            new CompassCharacter("310", (float) 0.7222),
            new CompassCharacter("315", (float) 0.7499),
            new CompassCharacter("320", (float) 0.7777),
            new CompassCharacter("325", (float) 0.8055),
            new CompassCharacter("330", (float) 0.8333),
            new CompassCharacter("335", (float) 0.8611),
            new CompassCharacter("340", (float) 0.8888),
            new CompassCharacter("345", (float) 0.9166),
            new CompassCharacter("350", (float) 0.9444),
            new CompassCharacter("355", (float) 0.9722),
        };
        //private static readonly ushort ClientComm = 20101; //This is disused.

        //Setup Text HUD API.
        public override void Init(MyObjectBuilder_SessionComponent sessionComponent)
        {
            if (MyAPIGateway.Utilities.IsDedicated == true)
            {
                IsDedicated = true;
                Log("Compass not started. (Running on dedicated)");

                loadConfig();
            }
            else
            {
                API = new HudAPIv2();
                MyAPIGateway.Utilities.MessageEntered += CommandEntered;

                loadConfig();

                if (MyAPIGateway.Session.SessionSettings.EnableSunRotation == true)
                {
                    ShouldEnableClock = true;
                }
                else
                {
                    ShouldEnableClock = false;
                }

                Log("Compass Started!");

                if (EnableSleeping)
                {
                    MyAPIGateway.Utilities.ShowMessage("HUD Compass", "Sleeping has been removed from this mod and published as its own mod, Sleep Mod. To prevent this message from appearing again type '/Compass Accept' in chat.");
                }
            }
        }

        //I need to get the base sun direction from the save (not the other ones we have access to, they're diffrent) to determin the REAL sun rotation axis (keens values make assumptions).
        public override void BeforeStart()
        {
            //No idea how this works, midspace put it on the ksh forms, it gets the component I need.
            var cpnt = MyAPIGateway.Session.GetCheckpoint("null");
            MyObjectBuilder_SectorWeatherComponent weatherComp = null;
            foreach (var comp in cpnt.SessionComponents)
            {
                MyObjectBuilder_SectorWeatherComponent component = comp as MyObjectBuilder_SectorWeatherComponent;
                if (component != null)
                    weatherComp = component;
            }

            BaseSunDir = new Vector3(weatherComp.BaseSunDirection.X, weatherComp.BaseSunDirection.Y, weatherComp.BaseSunDirection.Z);
        }

        //Register event handlers
        public override void LoadData()
        {
            MyAPIGateway.Entities.OnEntityAdd += checkAndAddPlanetEntity;
        }

        //Unregister event handlers
        protected override void UnloadData()
        {
            MyAPIGateway.Utilities.MessageEntered -= CommandEntered;
            MyAPIGateway.Entities.OnEntityAdd -= checkAndAddPlanetEntity;

            saveConfig();
        }

        //Command Handeler
        private void CommandEntered(string messageText, ref bool sendToOthers)
        {
            if (IsDedicated == false && messageText[0] == '/' && messageText.Length >= 8)
            {
                if (messageText.Substring(0, 8) == "/Compass")
                {
                    sendToOthers = false;

                    string[] command = messageText.Split(' ');
                    int commandLength = command.Length;
                    
                    if (commandLength >= 3)
                    {
                        if (command[1] == "ShowClock")
                        {
                            if (command[2] == "24HourDateTime")
                            {
                                ClockEnabled = true;
                                ClockTimeType = "24HourDateTime";
                                CallChatCommandConfirm();
                            }
                            else if (command[2] == "12HourDateTime")
                            {
                                ClockEnabled = true;
                                ClockTimeType = "12HourDateTime";
                                CallChatCommandConfirm();
                            }
                            else if (command[2] == "24HourTime")
                            {
                                ClockEnabled = true;
                                ClockTimeType = "24HourTime";
                                CallChatCommandConfirm();
                            }
                            else if (command[2] == "12HourTime")
                            {
                                ClockEnabled = true;
                                ClockTimeType = "12HourTime";
                                CallChatCommandConfirm();
                            }
                            else if (command[2] == "Off")
                            {
                                ClockEnabled = false;
                                CallChatCommandConfirm();
                            }
                            else
                            {
                                CallChatCommandError();
                            }
                        }
                        else if (command[1] == "UseDeg")
                        {
                            if (command[2] == "True")
                            {
                                CompassUseDeg = true;
                                CallChatCommandConfirm();
                            }
                            else if (command[2] == "False")
                            {
                                CompassUseDeg = false;
                                CallChatCommandConfirm();
                            }
                            else
                            {
                                CallChatCommandError();
                            }
                        }
                        else if (command[1] == "Enable")
                        {
                            if (command[2] == "True")
                            {
                                CompassEnabled = true;
                                CallChatCommandConfirm();
                            }
                            else if (command[2] == "False")
                            {
                                CompassEnabled = false;
                                CallChatCommandConfirm();
                            }
                            else
                            {
                                CallChatCommandError();
                            }
                        }
                        else
                        {
                            CallChatCommandError();
                        }
                    }
                    else if (commandLength == 2)
                    {
                        if (command[1] == "ResetTime")
                        {
                            MyAPIGateway.Session.GameDateTime = new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                            CallChatCommandConfirm();
                        }
                        else if (command[1] == "Accept")
                        {
                            EnableSleeping = false;
                            CallChatCommandConfirm();
                        }
                        else
                        {
                            CallChatCommandError();
                        }
                    }
                    else
                    {
                        CallChatCommandError();
                    }
                }
            }
        }

        //Command Feedback (Error)
        private void CallChatCommandError()
        {
            MyAPIGateway.Utilities.ShowNotification("Error Executing Command" , 3000, "Red");
        }

        //Command Feedback (Confirm)
        private void CallChatCommandConfirm()
        {
            MyAPIGateway.Utilities.ShowNotification("Executed Command", 3000, "Green");

            //Save State
            saveConfig();
        }

        //On render, do the stuff.
        public override void Draw()
        {
            //Check that Text HUD API exists, this is not a server and that the compass is enabled.
            if (API != null && IsDedicated == false && CompassEnabled == true)
            {
                //Check that Text HUD API is running, then make a compass! (And maybe a clock)
                if (API.Heartbeat == true)
                {
                    //Reset the compass and HUD element.
                    ResetMessages();
                    if (CompassElement != null)
                    {
                        CompassElement.DeleteMessage();
                    }
                    
                    //Get some variables.
                    Vector3D PlayerPos;
                    try
                    {
                        PlayerPos = MyAPIGateway.Session.Player.Character.GetPosition();
                    }
                    catch
                    {
                        //Unable to get player, abort.
                        return;
                    }
                    
                    //Check if the player is near a planet.
                    if (IsNearPlanets(PlayerPos, out Planet, out PlanetCenter))
                    {
                        //Add the decorational compass HUD element using Text HUD API.
                        CompassElement = new BillBoardHUDMessage(MyStringId.GetOrCompute("compass"), new Vector2D(0f, 0.5125f), Color.White, new Vector2D(0f, 0f), 10, 1, 1, 1, 0, true, true, BlendTypeEnum.PostPP);
                        
                        //Find the direction the player is looking.
                        Vector3 PlayerRotForwardVector = MyAPIGateway.Session.Player.Character.WorldMatrix.Rotation.Forward;
                        
                        //Get Player Position Normal (up in refrence to the planet)
                        Vector3D RelativePlayerPos = (PlayerPos - PlanetCenter);
                        Vector3 RelativePlayerPosNormal = RelativePlayerPos / RelativePlayerPos.Length();
                        
                        //Get North Normal (North relative to the planet)
                        Vector3D RelativeNorth = PlanetCenter + new Vector3D(0f, Planet.AverageRadius, 0f);
                        
                        //Calculate North relative to the player, this is exactly north towords the horizon.
                        Vector3D side1 = RelativeNorth - PlayerPos;
                        Vector3D side2 = PlanetCenter - PlayerPos;
                        Vector3D cross = Vector3D.Cross(side1, side2);
                        Vector3D PlayerRelativeNorth = Vector3D.Cross(cross, side2);
                        Vector3 PlayerRelativeNorthNormal = PlayerRelativeNorth / PlayerRelativeNorth.Length();
                        
                        //Caclulate the axes offset introduced by the planets curve on the player.
                        Matrix RelativePlayerPosNormalOffset = new Matrix();
                        Matrix.CreateRotationFromTwoVectors(ref RelativePlayerPosNormal, ref BaseSouth, out RelativePlayerPosNormalOffset);
                        
                        //Apply that offset to the players forward direction and North.
                        Vector3 PlayerRotForwardVectorCorrected = Vector3.Transform(PlayerRotForwardVector, RelativePlayerPosNormalOffset);
                        Vector3 PlayerRelativeNorthNormalCorrected = Vector3.Transform(PlayerRelativeNorthNormal, RelativePlayerPosNormalOffset);
                        
                        //Caclulate the players Azimuth and Elevation from their forward direction.
                        float PlayerAzimuth = 0f;
                        float PlayerElevation = 0f;
                        Vector3.GetAzimuthAndElevation(PlayerRotForwardVectorCorrected, out PlayerAzimuth, out PlayerElevation);
                        
                        //Caclulate the North direction Azimuth and Elevation from its direction.
                        float NorthAzimuth = 0f;
                        float NorthElevation = 0f;
                        Vector3.GetAzimuthAndElevation(PlayerRelativeNorthNormalCorrected, out NorthAzimuth, out NorthElevation);
                        
                        //Cancel out the offset and project the resulting "Compass" to a orthiographic display.
                        float PlayerDirection = PlayerAzimuth + PI;
                        float North = NorthAzimuth + PI;
                        float Compass = PlayerDirection - North;
                        if (Compass < 0)
                        {
                            Compass = PI * 2 + Compass;
                        }
                        else if (Compass > PI * 2)
                        {
                            Compass = Compass - PI * 2;
                        }
                        Compass = (Compass - PI) / PI;
                        
                        //Display each character with its offset.
                        foreach (CompassCharacter character in CompassUseDeg == true ? charactersDeg : characters)
                        {
                            //Apply the characters offset.
                            float CompassOffset = Compass + character.Offset;
                            if (CompassOffset < -1)
                            {
                                CompassOffset = 2 + CompassOffset;
                            }
                            else if (CompassOffset > 1)
                            {
                                CompassOffset = CompassOffset - 2;
                            }

                            //Display the character.
                            DisplayCharacter(character.Character, CompassOffset);
                        }
                    }

                    //Calculate time of day and display clock.
                    if (ClockEnabled == true)
                    {
                        if (ShouldEnableClock == true)
                        {
                            DateTime GameDateTime = MyAPIGateway.Session.GameDateTime;
                            float SunRotationIntervalSeconds = MyAPIGateway.Session.SessionSettings.SunRotationIntervalMinutes * 60;

                            float DaysElipsedRaw = (float)((GameDateTime - new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds / SunRotationIntervalSeconds);
                            int DaysElipsed = (int)Math.Floor(DaysElipsedRaw);
                            float TimeRatioUTC = DaysElipsedRaw - DaysElipsed;

                            UTCTime = new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(DaysElipsed).AddMilliseconds(TimeRatioUTC * 86400000);

                            LocalTime = new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                            float TimeRatioLocal = 0;

                            if (Planet != null)
                            {
                                Vector3D RelativePlayerPos = (PlayerPos - PlanetCenter);
                                Vector3 RelativePlayerPosNormal = RelativePlayerPos / RelativePlayerPos.Length();
                                
                                if (BaseSunDir != Vector3.Backward)
                                {
                                    Quaternion offset;
                                    Quaternion.CreateFromTwoVectors(ref BaseSunDir, ref Vector3.Forward, out offset);

                                    RelativePlayerPosNormal = Vector3.Transform(RelativePlayerPosNormal, offset);
                                }

                                float PlayerAzimuth = 0f;
                                float PlayerElevation = 0f;
                                Vector3.GetAzimuthAndElevation(RelativePlayerPosNormal, out PlayerAzimuth, out PlayerElevation);
                                
                                TimeRatioLocal = (PlayerAzimuth / PI * 12);

                                LocalTime = UTCTime.AddHours(-TimeRatioLocal - (BaseSunDir == Vector3.Backward ? 0 : 12));
                            }

                            if (ClockTimeType == "24HourDateTime")
                            {
                                DisplayClock("UTC: " + UTCTime.ToString("HH:mm '-' dd/MM/yyyy", CultureInfo.InvariantCulture), "Local: " + LocalTime.ToString("HH:mm", CultureInfo.InvariantCulture));
                            }
                            else if (ClockTimeType == "12HourDateTime")
                            {
                                DisplayClock("UTC: " + UTCTime.ToString("hh:mm tt '-' dd/MM/yyyy", CultureInfo.InvariantCulture), "Local: " + LocalTime.ToString("hh:mm tt", CultureInfo.InvariantCulture));
                            }
                            else if (ClockTimeType == "24HourTime")
                            {
                                DisplayClock("UTC: " + UTCTime.ToString("HH:mm", CultureInfo.InvariantCulture), "Local: " + LocalTime.ToString("HH:mm", CultureInfo.InvariantCulture));
                            }
                            else if (ClockTimeType == "12HourTime")
                            {
                                DisplayClock("UTC: " + UTCTime.ToString("hh:mm tt", CultureInfo.InvariantCulture), "Local: " + LocalTime.ToString("hh:mm tt", CultureInfo.InvariantCulture));
                            }

                            /*Vector3D RelativePlayerPos = (PlayerPos - PlanetCenter);
                            Vector3 RelativePlayerPosNormal = RelativePlayerPos / RelativePlayerPos.Length();

                            if (BaseSunDir != new Vector3(0, 0, 1))
                            {
                                Matrix RelativePlayerPosNormalSunOffset = new Matrix();
                                Matrix.CreateRotationFromTwoVectors(ref Vector3.Forward, ref BaseSunDir, out RelativePlayerPosNormalSunOffset);

                                RelativePlayerPosNormal = Vector3.Transform(RelativePlayerPosNormal, RelativePlayerPosNormalSunOffset);
                            }*/

                            //The old way of getting the time, leaving it here because it includes keens own formula for calculating the vector to the sun, if anyone wants to use this feel free.
                            /*
                            //Some values.
                            Vector3 SunAxis = MySunProperties.Default.SunRotationAxis;
                            DateTime GameDateTime = MyAPIGateway.Session.GameDateTime;
                            float SunRotationIntervalSeconds = MyAPIGateway.Session.SessionSettings.SunRotationIntervalMinutes * 60;

                            Vector3 Up = new Vector3(0, 1, 0);

                            //This is the equation used in the SE code for calculating sun direction, but using values I have access to. It accounts for time offset because time offset actualy chages 'GameDateTime'.
                            Vector3 ToSun = Vector3.Transform(MySunProperties.Default.SunDirectionNormalized, Matrix.CreateFromAxisAngle(SunAxis, 6.283186f * ((float) (GameDateTime - new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds / SunRotationIntervalSeconds)));

                            //Get a rotation matrix for canceling out the suns axis offset.
                            Matrix SunAxisOffset = new Matrix();
                            Matrix.CreateRotationFromTwoVectors(ref SunAxis, ref Up, out SunAxisOffset);

                            //Apply that rotation matrix.
                            Vector3 ToSunCorrected = Vector3.Transform(ToSun, SunAxisOffset);

                            //Get azumuth an elevation for the sun, elevation should be 0 (or like 10^-20 because floating point rounding error XD).
                            float SunAzimuth = 0f;
                            float SunElevation = 0f;
                            Vector3.GetAzimuthAndElevation(ToSunCorrected, out SunAzimuth, out SunElevation);

                            //Make the azimuth into a more wieldly value, a ratio (0 to 1)
                            float TimeRatio = (SunAzimuth + PI) / (PI * 2);

                            //Final step, make a new DateTime from our time ratio by multipling by the number of ms in a day.
                            DateTime Time = (new DateTime(2081, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((TimeRatio * 86400000));
                            */


                        }
                        else if (PlayerWarnedShouldEnableClockFalse == false)
                        {
                            MyAPIGateway.Utilities.ShowNotification("Sun Rotation Disabled, Not Showing Clock", 3000, "Red");
                            PlayerWarnedShouldEnableClockFalse = true;
                        }
                    }
                }
            }
        }

        private void DisplayCharacter(string character, float offset)
        {
            //Convert the orthiographic character offset to the given perspective FoV. (Using a cheaty quartic equation thats a little inaccurate XD)
            float FOV = MyAPIGateway.Session.Camera.FovWithZoom;
            offset = (FOV * ((float)5.596 * ((float)Math.Pow(FOV, 2)) + (float)-18.43 * FOV + (float)16.16) * offset) + (FOV * 12 * (float)Math.Pow(offset, 3));

            //Make sure that the character is within the compass bounds.
            if (offset > 0.33 || offset < -0.33)
            {
                return;
            }

            //Show the character on the HUD using Text HUD API.
            messages.Add(new HUDMessage(new StringBuilder(character), new Vector2D(offset, 0.984f), new Vector2D(-0.0085f * character.Length, 0f), 10, 1, true, false, null, BlendTypeEnum.PostPP));
        }

        private void DisplayClock(string UTCtime, string LocalTime)
        {
            if (Planet != null)
            {
                //Show the UTC clock on the HUD using Text HUD API, under the compass.
                messages.Add(new HUDMessage(new StringBuilder(UTCtime), new Vector2D(-0.3, 0.935f), new Vector2D(0, 0), 10, 1, true, false, null, BlendTypeEnum.PostPP));

                //Show the Local clock on the HUD using Text HUD API, under the compass.
                messages.Add(new HUDMessage(new StringBuilder(LocalTime), new Vector2D(0.16, 0.935f), new Vector2D(0, 0), 10, 1, true, false, null, BlendTypeEnum.PostPP));
            }
            else
            {
                //Show the UTC clock on the HUD using Text HUD API.
                messages.Add(new HUDMessage(new StringBuilder(UTCtime), new Vector2D(-0.07, 0.98f), new Vector2D(0, 0), 10, 1, true, false, null, BlendTypeEnum.PostPP));
            }
        }

        private void ResetMessages()
        {
            foreach (HUDMessage message in messages)
            {
                message.DeleteMessage();
            }

            messages.Clear();
        }

        private string HandleMinutes(int Minute)
        {
            return Minute < 10 ? "0" + Minute.ToString() : Minute.ToString();
        }

        public static void Log(string Input)
        {
            MyLog.Default.WriteLineAndConsole("Compass: " + Input);

            //MyAPIGateway.Utilities.ShowMessage("Compass", Input);
        }

        private void checkAndAddPlanetEntity(IMyEntity entity)
        {
            if (entity as MyPlanet == null)
            {
                return;
            }
            
            MyPlanet planet = entity as MyPlanet;
            
            if (!planets.ContainsKey(planet.EntityId))
            {
                planets.Add(planet.EntityId, planet);
                planet.OnMarkForClose += closePlanetEntity;
            }
        }

        private void closePlanetEntity(IMyEntity entity)
        {
            planets.Remove(entity.EntityId);
            entity.OnMarkForClose -= closePlanetEntity;
        }

        private bool IsNearPlanets(Vector3D PlayerPos, out MyPlanet Planet, out Vector3D PlanetPosition)
        {
            foreach (MyPlanet planet in planets.Values)
            {
                PlanetPosition = planet.PositionComp.GetPosition();

                if (Vector3D.DistanceSquared(PlanetPosition, PlayerPos) < Math.Pow(planet.AtmosphereRadius, 2))
                {
                    Planet = planet;
                    return true;
                }
            }

            PlanetPosition = new Vector3D();
            Planet = null;
            return false;
        }

        private void loadConfig()
        {
            if (MyAPIGateway.Utilities.FileExistsInGlobalStorage("CompassConfig.cfg") == true)
            {
                string ConfigString = MyAPIGateway.Utilities.ReadFileInGlobalStorage("CompassConfig.cfg").ReadToEnd();

                CompassConfig Config = null;
                try
                {
                    Config = MyAPIGateway.Utilities.SerializeFromXML<CompassConfig>(ConfigString);
                }
                catch
                {
                    Log("Unable to serialize config, assuming defult");
                }

                if (Config != null)
                {
                    if (Config.CompassEnabled.HasValue)
                    {
                        CompassEnabled = Config.CompassEnabled.Value;
                    }
                    if (Config.ClockEnabled.HasValue)
                    {
                        ClockEnabled = Config.ClockEnabled.Value;
                    }
                    ClockTimeType = Config.ClockTimeType;
                    if (Config.CompassUseDeg.HasValue)
                    {
                        CompassUseDeg = Config.CompassUseDeg.Value;
                    }
                    if (Config.EnableSleeping.HasValue)
                    {
                        EnableSleeping = Config.EnableSleeping.Value;
                    }

                    Log("Config Loaded");
                }
            }
            else
            {
                Log("Did not find config in global storage");
            }
        }

        private void saveConfig()
        {
            string ConfigXML = MyAPIGateway.Utilities.SerializeToXML(new CompassConfig(CompassEnabled, ClockEnabled, ClockTimeType, CompassUseDeg, EnableSleeping));
            using (TextWriter writer = MyAPIGateway.Utilities.WriteFileInGlobalStorage("CompassConfig.cfg"))
            {
                writer.Write(ConfigXML);
            }
            Log("Config Saved");
        }
    }

    public class CompassCharacter
    {
        public string Character;
        public float Offset;

        public CompassCharacter(string character, float offset)
        {
            Character = character;
            Offset = offset;
        }
    }

    public class CompassConfig
    {
        public bool? CompassEnabled;
        public bool? ClockEnabled;
        public string ClockTimeType;
        public bool? CompassUseDeg;
        public bool? EnableSleeping;

        public CompassConfig(bool EnableCompass, bool EnableClock, string TimeType, bool CompassDeg, bool EnableSleeping)
        {
            this.CompassEnabled = EnableCompass;
            this.ClockEnabled = EnableClock;
            this.ClockTimeType = TimeType;
            this.CompassUseDeg = CompassDeg;
            this.EnableSleeping = EnableSleeping;
        }

        public CompassConfig()
        {
            //Parameterless constructor for serialisation.
        }
    }
}
