using System.Collections.Generic;
using Orbiter.Interfaces;

namespace Orbiter.Multiplayer
{
    public class Vessel:GlobalObject
    {
        private string navigationInfo;
        private string dockingInfo;
        private string className;
        private List<string>[] stateInfo;
        private SortedDictionary<int, string> animationInfo;
        private User user;
        private NTPTime lastInfoPingTime;
        private NTPTime firstNameSetTime;
        private StateInfoPacket state;
        private string classMapping;

        public Vessel(Server server, User user):base(server)
        {
            this.user = user;            
            lastInfoPingTime=new NTPTime(0,0); //Start information gathering
            firstNameSetTime = new NTPTime(0, 0); //After name was first set, this will block request resolution for 5 seconds in order to cope with old clients that do not send vessel-specific states
            stateInfo = new List<string>[2];
            stateInfo[0] = new List<string>();
            stateInfo[1] = new List<string>();
            animationInfo = new SortedDictionary<int, string>();
        }
        
        public void Update(StateInfoPacket pac)
        {
            lock (this)
            {
                state = pac;
                lock(Server.Map.Cluster)
                {
                    Server.Map.Cluster.Set(this,
                                       pac.LogarithmicScale,
                                       user.Time.Update(user, pac),
                                       new Vector3(pac.Position.X, pac.Position.Y, pac.Position.Z));
                }
                Host userHost = user.Host;
                if (lastInfoPingTime != null && Server.NTP.Now - lastInfoPingTime > 5 && userHost != null)
                {
                    userHost.Send("REQST " + GlobalId + "\n\r");
                    lastInfoPingTime = Server.NTP.Now;
                }
            }
        }

        public User User
        {
            get { return user; }
        }

        public string ClassName
        {
            get { return className; }
        }

        public string ClassMapping
        {
            get { return classMapping; }
        }

        public StateInfoPacket State
        {
            get
            {
                return state;
            }
        }

        public override bool EvaluateInfo(params string[] strings)
        {
            lock (this)
            {
                if (strings.Length > 0 && strings[0].Length > 0) switch (strings[0][0])
                {
                    case 'C':
                        if (lastInfoPingTime != null && strings[0].Length > 1 && strings.Length > 1)
                        {
                            Name = strings[0].Substring(1);
                            className = strings[1];
                            classMapping = Server.Map.GetVesselClass(className);                            
                            firstNameSetTime = Server.NTP.Now;
                        }
                        break;
                    case 'N':
                        navigationInfo = strings[0].Substring(1);
                        return true;
                    case 'D':
                        dockingInfo = strings[0].Substring(1);
                        return true;
                    case 'E':
                        //Request state info on event notification
                        Host userHost = user.Host;
                        if (userHost != null) userHost.Send("REQST " + GlobalId + "\n\r");
                        break;
                    case '|': //Store and broadcast all non-streamed vessel states
                        {
                            var line = strings[0].Substring(1);
                            switch (line.Split(' ')[0])
                            {
                                case "STATUS":
                                case "BASE":
                                case "POS":
                                case "HEADING":
                                case "RPOS":
                                case "RVEL":
                                case "AROT":
                                case "VROT":
                                case "ELEMENTS":
                                    return false;
                            }                            
                            stateInfo[1].Add(line);
                        }
                        return true;
                    case '-': //Broadcast end of vessel states
                        stateInfo[0].Clear();
                        stateInfo[0].AddRange(stateInfo[1]);
                        stateInfo[1].Clear();
                        lastInfoPingTime = firstNameSetTime = null;                        
                        return true;
                    case '^': //Store and broadcast animation status lines
                        {
                            var line = strings[0].Substring(1);
                            var i=line.IndexOf(' ');
                            if (i < 0) return false; //No need to store and broadcast an invalid animation status line
                            try
                            {
                                var n = System.Convert.ToInt32(line.Substring(0, i), 16);
                                animationInfo[n] = line;
                            }
                            catch (System.Exception)
                            {
                                return false;
                            }                            
                        }
                        return true;
                }
                return false;
            }
        }

        public override string[] GenerateInfo()
        {
            lock (this)
            {
                if (firstNameSetTime != null && Server.NTP.Now - firstNameSetTime > 5) lastInfoPingTime = firstNameSetTime = null;
                if (firstNameSetTime != null) return null;
                var strings = new List<string>(3+stateInfo[0].Count+1+animationInfo.Count) {"\"C" + Name + '"' + ClassMapping};
                if (navigationInfo != null) strings.Add("\"N" + navigationInfo);
                if (dockingInfo != null) strings.Add("\"D" + dockingInfo);
                foreach (var s in stateInfo[0]) strings.Add(":|" + s);
                if (stateInfo[0].Count > 0) strings.Add(":-");
                foreach (var s in animationInfo.Values) strings.Add(":^" + s);
                return strings.ToArray();
            }
        }

        public void HandOver(User user)
        {
            this.user = user;
        }
    }
}
