using System;
using System.IO;
using System.Globalization;
using System.ComponentModel;
using System.Collections.Generic;
using SharpDX;
using SharpDX.Direct3D11;
using CodeWalker.Utils;
using CodeWalker.World;
using CodeWalker.GameFiles;

namespace CodeWalker
{


    public class MapIcon
    {
        public string Name { get; set; }
        public string Filepath { get; set; }
        public Texture2D Tex { get; set; }
        public ShaderResourceView TexView { get; set; }
        public Vector3 Center { get; set; } //in image pixels
        public float Scale { get; set; } //screen pixels per icon pixel
        public int TexWidth { get; set; }
        public int TexHeight { get; set; }

        public MapIcon(string name, string filepath, int texw, int texh, float centerx, float centery, float scale)
        {
            Name = name;
            Filepath = filepath;
            TexWidth = texw;
            TexHeight = texh;
            Center = new Vector3(centerx, centery, 0.0f);
            Scale = scale;

            if (!File.Exists(filepath))
            {
                throw new Exception("File not found.");
            }
        }

        public void LoadTexture(Device device, Action<string> errorAction)
        {
            try
            {
                if (device != null)
                {
                    Tex = TextureLoader.CreateTexture2DFromBitmap(device, TextureLoader.LoadBitmap(new SharpDX.WIC.ImagingFactory2(), Filepath));
                    TexView = new ShaderResourceView(device, Tex);
                }
            }
            catch (Exception ex)
            {
                errorAction("Could not load map icon " + Filepath + " for " + Name + "!\n\n" + ex.ToString());
            }
        }

        public void UnloadTexture()
        {
            if (TexView != null)
            {
                TexView.Dispose();
                TexView = null;
            }
            if (Tex != null)
            {
                Tex.Dispose();
                Tex = null;
            }
        }

        public override string ToString()
        {
            return Name;
        }
    }

    public class MapMarker
    {
        public MapIcon Icon { get; set; }
        public Vector3 WorldPos { get; set; } //actual world pos
        public Vector3 CamRelPos { get; set; } //updated per frame
        public Vector3 ScreenPos { get; set; } //position on screen (updated per frame)
        public string Name { get; set; }
        public List<string> Properties { get; set; } //additional data
        public bool IsMovable { get; set; }
        public float Distance { get; set; } //length of CamRelPos, updated per frame

        public void Parse(string s)
        {
            Vector3 p = new Vector3(0.0f);
            string[] ss = s.Split(',');
            if (ss.Length > 1)
            {
                FloatUtil.TryParse(ss[0].Trim(), out p.X);
                FloatUtil.TryParse(ss[1].Trim(), out p.Y);
            }
            if (ss.Length > 2)
            {
                FloatUtil.TryParse(ss[2].Trim(), out p.Z);
            }
            if (ss.Length > 3)
            {
                Name = ss[3].Trim();
            }
            else
            {
                Name = string.Empty;
            }
            for (int i = 4; i < ss.Length; i++)
            {
                if (Properties == null) Properties = new List<string>();
                Properties.Add(ss[i].Trim());
            }
            WorldPos = p;
        }

        public override string ToString()
        {
            string cstr = Get3DWorldPosString();
            if (!string.IsNullOrEmpty(Name))
            {
                cstr += ", " + Name;
                if (Properties != null)
                {
                    foreach (string prop in Properties)
                    {
                        cstr += ", " + prop;
                    }
                }
            }
            return cstr;
        }

        public string Get2DWorldPosString()
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", WorldPos.X, WorldPos.Y);
        }
        public string Get3DWorldPosString()
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}, {1}, {2}", WorldPos.X, WorldPos.Y, WorldPos.Z);
        }


    }



    public struct MapSphere
    {
        public Vector3 CamRelPos { get; set; }
        public float Radius { get; set; }
    }

    public struct MapBox
    {
        public Vector3 CamRelPos { get; set; }
        public Vector3 BBMin { get; set; }
        public Vector3 BBMax { get; set; }
        public Quaternion Orientation { get; set; }
        public Vector3 Scale { get; set; }
    }








    [TypeConverter(typeof(ExpandableObjectConverter))]
    public struct MapSelection
    {
        public YmapEntityDef EntityDef { get; set; }
        public Archetype Archetype { get; set; }
        public DrawableBase Drawable { get; set; }
        public DrawableGeometry Geometry { get; set; }
        public MetaWrapper EntityExtension { get; set; }
        public MetaWrapper ArchetypeExtension { get; set; }
        public YmapTimeCycleModifier TimeCycleModifier { get; set; }
        public YmapCarGen CarGenerator { get; set; }
        public YmapGrassInstanceBatch GrassBatch { get; set; }
        public YmapDistantLODLights DistantLodLights { get; set; }
        public YmapBoxOccluder BoxOccluder { get; set; }
        public YmapOccludeModel OccludeModel { get; set; }
        public YmapEntityDef MloEntityDef { get; set; }
        public MCMloRoomDef MloRoomDef { get; set; }
        public WaterQuad WaterQuad { get; set; }
        public Bounds CollisionBounds { get; set; }
        public BoundPolygon CollisionPoly { get; set; }
        public YnvPoly NavPoly { get; set; }
        public YnvPoint NavPoint { get; set; }
        public YnvPortal NavPortal { get; set; }
        public YndNode PathNode { get; set; }
        public YndLink PathLink { get; set; }
        public TrainTrackNode TrainTrackNode { get; set; }
        public ScenarioNode ScenarioNode { get; set; }
        public MCScenarioChainingEdge ScenarioEdge { get; set; }
        public AudioPlacement Audio { get; set; }

        public bool MultipleSelection { get; set; }
        public Vector3 MultipleSelectionCenter { get; set; }

        public Vector3 BBOffset { get; set; }
        public Quaternion BBOrientation { get; set; }
        public BoundingBox AABB { get; set; }
        public BoundingSphere BSphere { get; set; }
        public int GeometryIndex { get; set; }
        public Vector3 CamRel { get; set; }
        public float HitDist { get; set; }


        public bool HasValue
        {
            get
            {
                return (EntityDef != null) ||
                    (Archetype != null) ||
                    (Drawable != null) ||
                    (Geometry != null) ||
                    (EntityExtension != null) ||
                    (ArchetypeExtension != null) ||
                    (TimeCycleModifier != null) ||
                    (CarGenerator != null) ||
                    (GrassBatch != null) ||
                    (WaterQuad != null) ||
                    (CollisionBounds != null) ||
                    (CollisionPoly != null) ||
                    (NavPoly != null) ||
                    (NavPoint != null) ||
                    (NavPortal != null) ||
                    (PathNode != null) ||
                    (TrainTrackNode != null) ||
                    (DistantLodLights != null) ||
                    (BoxOccluder != null) ||
                    (OccludeModel != null) ||
                    (MloEntityDef != null) ||
                    (ScenarioNode != null) ||
                    (Audio != null) ||
                    (MloRoomDef != null);
            }
        }

        public bool HasHit
        {
            get { return (HitDist != float.MaxValue); }
        }


        public bool CheckForChanges(MapSelection mhit)
        {
            return (EntityDef != mhit.EntityDef)
                || (Archetype != mhit.Archetype)
                || (Drawable != mhit.Drawable)
                || (TimeCycleModifier != mhit.TimeCycleModifier)
                || (ArchetypeExtension != mhit.ArchetypeExtension)
                || (EntityExtension != mhit.EntityExtension)
                || (CarGenerator != mhit.CarGenerator)
                || (MloEntityDef != mhit.MloEntityDef)
                || (DistantLodLights != mhit.DistantLodLights)
                || (GrassBatch != mhit.GrassBatch)
                || (BoxOccluder != mhit.BoxOccluder)
                || (OccludeModel != mhit.OccludeModel)
                || (WaterQuad != mhit.WaterQuad)
                || (CollisionBounds != mhit.CollisionBounds)
                || (CollisionPoly != mhit.CollisionPoly)
                || (NavPoly != mhit.NavPoly)
                || (NavPoint != mhit.NavPoint)
                || (NavPortal != mhit.NavPortal)
                || (PathNode != mhit.PathNode)
                || (TrainTrackNode != mhit.TrainTrackNode)
                || (ScenarioNode != mhit.ScenarioNode)
                || (Audio != mhit.Audio)
                || (MloRoomDef != mhit.MloRoomDef);
        }
        public bool CheckForChanges()
        {
            return (EntityDef != null)
                || (Archetype != null)
                || (Drawable != null)
                || (TimeCycleModifier != null)
                || (ArchetypeExtension != null)
                || (EntityExtension != null)
                || (CarGenerator != null)
                || (MloEntityDef != null)
                || (DistantLodLights != null)
                || (GrassBatch != null)
                || (BoxOccluder != null)
                || (OccludeModel != null)
                || (WaterQuad != null)
                || (CollisionBounds != null)
                || (CollisionPoly != null)
                || (NavPoly != null)
                || (NavPoint != null)
                || (NavPortal != null)
                || (PathNode != null)
                || (PathLink != null)
                || (TrainTrackNode != null)
                || (ScenarioNode != null)
                || (Audio != null)
                || (MloRoomDef != null);
        }


        public void Clear()
        {
            EntityDef = null;
            Archetype = null;
            Drawable = null;
            Geometry = null;
            EntityExtension = null;
            ArchetypeExtension = null;
            TimeCycleModifier = null;
            CarGenerator = null;
            GrassBatch = null;
            BoxOccluder = null;
            OccludeModel = null;
            WaterQuad = null;
            CollisionBounds = null;
            CollisionPoly = null;
            NavPoly = null;
            NavPoint = null;
            NavPortal = null;
            PathNode = null;
            PathLink = null;
            TrainTrackNode = null;
            DistantLodLights = null;
            MloEntityDef = null;
            ScenarioNode = null;
            ScenarioEdge = null;
            Audio = null;
            MultipleSelection = false;
            AABB = new BoundingBox();
            GeometryIndex = 0;
            CamRel = new Vector3();
            HitDist = float.MaxValue;
        }

        public string GetNameString(string defval)
        {
            string name = defval;
            if (MultipleSelection)
            {
                name = "Multiple items";
            }
            else if (EntityDef != null)
            {
                name = EntityDef._CEntityDef.archetypeName.ToString();
            }
            else if (Archetype != null)
            {
                name = Archetype.Hash.ToString();
            }
            else if (TimeCycleModifier != null)
            {
                name = TimeCycleModifier.CTimeCycleModifier.name.ToString();
            }
            else if (CarGenerator != null)
            {
                name = CarGenerator.CCarGen.carModel.ToString();
            }
            else if (DistantLodLights != null)
            {
                name = DistantLodLights.Ymap?.Name ?? "";
            }
            else if (BoxOccluder != null)
            {
                name = "BoxOccluder " + (BoxOccluder.Ymap?.Name ?? "") + ": " + BoxOccluder.Index.ToString();
            }
            else if (OccludeModel != null)
            {
                name = "OccludeModel " + (OccludeModel.Ymap?.Name ?? "") + ": " + OccludeModel.Index.ToString();
            }
            else if (CollisionPoly != null)
            {
                name = "Poly " + CollisionPoly.Index.ToString() + ((CollisionBounds != null) ? (": " + CollisionBounds.GetName()) : string.Empty);
            }
            else if (CollisionBounds != null)
            {
                name = CollisionBounds.GetName();
            }
            if (EntityExtension != null)
            {
                name = EntityExtension.Name;
            }
            if (ArchetypeExtension != null)
            {
                name = ArchetypeExtension.Name;
            }
            if (WaterQuad != null)
            {
                name = "WaterQuad " + WaterQuad.ToString();
            }
            if (NavPoly != null)
            {
                name = "NavPoly " + NavPoly.ToString();
            }
            if (NavPoint != null)
            {
                name = "NavPoint " + NavPoint.ToString();
            }
            if (NavPortal != null)
            {
                name = "NavPortal " + NavPortal.ToString();
            }
            if (PathNode != null)
            {
                name = "PathNode " + PathNode.AreaID.ToString() + "." + PathNode.NodeID.ToString(); //+ FloatUtil.GetVector3String(PathNode.Position);
            }
            if (TrainTrackNode != null)
            {
                name = "TrainTrackNode " + FloatUtil.GetVector3String(TrainTrackNode.Position);
            }
            if (ScenarioNode != null)
            {
                name = ScenarioNode.ToString();
            }
            if (Audio != null)
            {
                name = Audio.ShortTypeName + " " + Audio.GetNameString();// FloatUtil.GetVector3String(Audio.InnerPos);
            }
            if (MloRoomDef != null)
            {
                name = "MloRoomDef " + MloRoomDef.RoomName;
            }
            return name;
        }

        public string GetFullNameString(string defval)
        {
            string name = defval;
            if (MultipleSelection)
            {
                name = "Multiple items";
            }
            else if (EntityDef != null)
            {
                name = EntityDef._CEntityDef.archetypeName.ToString();
            }
            else if (Archetype != null)
            {
                name = Archetype.Hash.ToString();
            }
            else if (CollisionPoly != null)
            {
                name = "Poly " + CollisionPoly.Index.ToString() + ((CollisionBounds != null) ? (": " + CollisionBounds.GetName()) : string.Empty);
            }
            else if (CollisionBounds != null)
            {
                name = CollisionBounds.GetName();
            }
            if (Geometry != null)
            {
                name += " (" + GeometryIndex.ToString() + ")";
            }
            if (TimeCycleModifier != null)
            {
                name = TimeCycleModifier.CTimeCycleModifier.name.ToString();
            }
            if (BoxOccluder != null)
            {
                name = "BoxOccluder " + (BoxOccluder.Ymap?.Name ?? "") + ": " + BoxOccluder.Index.ToString();
            }
            if (OccludeModel != null)
            {
                name = "OccludeModel " + (OccludeModel.Ymap?.Name ?? "") + ": " + OccludeModel.Index.ToString();
            }
            if (CarGenerator != null)
            {
                name = CarGenerator.NameString();
            }
            if (EntityExtension != null)
            {
                name += ": " + EntityExtension.Name;
            }
            if (ArchetypeExtension != null)
            {
                name += ": " + ArchetypeExtension.Name;
            }
            if (WaterQuad != null)
            {
                name = "WaterQuad " + WaterQuad.ToString();
            }
            if (NavPoly != null)
            {
                name = "NavPoly " + NavPoly.ToString();
            }
            if (NavPoint != null)
            {
                name = "NavPoint " + NavPoint.ToString();
            }
            if (NavPortal != null)
            {
                name = "NavPortal " + NavPortal.ToString();
            }
            if (PathNode != null)
            {
                name = "PathNode " + PathNode.AreaID.ToString() + "." + PathNode.NodeID.ToString();// + FloatUtil.GetVector3String(PathNode.Position);
            }
            if (TrainTrackNode != null)
            {
                name = "TrainTrackNode " + FloatUtil.GetVector3String(TrainTrackNode.Position);
            }
            if (ScenarioNode != null)
            {
                name = ScenarioNode.ToString();
            }
            if (Audio != null)
            {
                name = Audio.ShortTypeName + " " + Audio.GetNameString();//  + FloatUtil.GetVector3String(Audio.InnerPos);
            }
            if (MloRoomDef != null)
            {
                name = "MloRoomDef " + MloRoomDef.RoomName;
            }
            return name;
        }



        public bool CanShowWidget
        {
            get
            {
                bool res = false;

                if (MultipleSelection)
                {
                    res = true;
                }
                else if (EntityDef != null)
                {
                    res = true;
                }
                else if (CarGenerator != null)
                {
                    res = true;
                }
                else if (NavPoly != null)
                {
                    res = true;
                }
                else if (CollisionPoly != null)
                {
                    res = true;
                }
                else if (CollisionBounds != null)
                {
                    res = true;
                }
                else if (NavPoint != null)
                {
                    res = true;
                }
                else if (NavPortal != null)
                {
                    res = true;
                }
                else if (PathNode != null)
                {
                    res = true;
                }
                else if (TrainTrackNode != null)
                {
                    res = true;
                }
                else if (ScenarioNode != null)
                {
                    res = true;
                }
                else if (Audio != null)
                {
                    res = true;
                }
                return res;
            }
        }
        public Vector3 WidgetPosition
        {
            get
            {
                if (MultipleSelection)
                {
                    return MultipleSelectionCenter;
                }
                else if (EntityDef != null)
                {
                    return EntityDef.WidgetPosition;
                }
                else if (CarGenerator != null)
                {
                    return CarGenerator.Position;
                }
                else if (CollisionPoly != null)
                {
                    return CollisionPoly.Position;
                }
                else if (CollisionBounds != null)
                {
                    return CollisionBounds.Position;
                }
                else if (NavPoly != null)
                {
                    return NavPoly.Position;
                }
                else if (NavPoint != null)
                {
                    return NavPoint.Position;
                }
                else if (NavPortal != null)
                {
                    return NavPortal.Position;
                }
                else if (PathNode != null)
                {
                    return PathNode.Position;
                }
                else if (TrainTrackNode != null)
                {
                    return TrainTrackNode.Position;
                }
                else if (ScenarioNode != null)
                {
                    return ScenarioNode.Position;
                }
                else if (Audio != null)
                {
                    return Audio.InnerPos;
                }
                return Vector3.Zero;
            }
        }
        public Quaternion WidgetRotation
        {
            get
            {
                if (MultipleSelection)
                {
                    return Quaternion.Identity;
                }
                else if (EntityDef != null)
                {
                    return EntityDef.WidgetOrientation;
                }
                else if (CarGenerator != null)
                {
                    return CarGenerator.Orientation;
                }
                else if (CollisionPoly != null)
                {
                    return Quaternion.Identity;
                }
                else if (CollisionBounds != null)
                {
                    return Quaternion.Identity;
                }
                else if (NavPoly != null)
                {
                    return Quaternion.Identity;
                }
                else if (NavPoint != null)
                {
                    return NavPoint.Orientation;
                }
                else if (NavPortal != null)
                {
                    return NavPortal.Orientation;
                }
                else if (PathNode != null)
                {
                    return Quaternion.Identity;
                }
                else if (TrainTrackNode != null)
                {
                    return Quaternion.Identity;
                }
                else if (ScenarioNode != null)
                {
                    return ScenarioNode.Orientation;
                }
                else if (Audio != null)
                {
                    return Audio.Orientation;
                }
                return Quaternion.Identity;
            }
        }
        public WidgetAxis WidgetRotationAxes
        {
            get
            {
                if (MultipleSelection)
                {
                    return WidgetAxis.XYZ;
                }
                else if (EntityDef != null)
                {
                    return WidgetAxis.XYZ;
                }
                else if (CarGenerator != null)
                {
                    return WidgetAxis.Z;
                }
                else if (CollisionPoly != null)
                {
                    return WidgetAxis.XYZ;
                }
                else if (CollisionBounds != null)
                {
                    return WidgetAxis.XYZ;
                }
                else if (NavPoly != null)
                {
                    return WidgetAxis.XYZ;
                }
                else if (NavPoint != null)
                {
                    return WidgetAxis.Z;
                }
                else if (NavPortal != null)
                {
                    return WidgetAxis.Z;
                }
                else if (PathNode != null)
                {
                    return WidgetAxis.None;
                }
                else if (TrainTrackNode != null)
                {
                    return WidgetAxis.None;
                }
                else if (ScenarioNode != null)
                {
                    return WidgetAxis.Z;
                }
                else if (Audio != null)
                {
                    return WidgetAxis.Z;
                }
                return WidgetAxis.None;
            }
        }
        public Vector3 WidgetScale
        {
            get
            {
                if (MultipleSelection)
                {
                    return Vector3.One;
                }
                else if (EntityDef != null)
                {
                    return EntityDef.Scale;
                }
                else if (CarGenerator != null)
                {
                    return new Vector3(CarGenerator.CCarGen.perpendicularLength);
                }
                else if (CollisionPoly != null)
                {
                    return Vector3.One;
                }
                else if (CollisionBounds != null)
                {
                    return Vector3.One;
                }
                else if (NavPoly != null)
                {
                    return Vector3.One;
                }
                else if (NavPoint != null)
                {
                    return Vector3.One;
                }
                else if (NavPortal != null)
                {
                    return Vector3.One;
                }
                else if (PathNode != null)
                {
                    return Vector3.One;
                }
                else if (TrainTrackNode != null)
                {
                    return Vector3.One;
                }
                else if (ScenarioNode != null)
                {
                    return Vector3.One;
                }
                else if (Audio != null)
                {
                    return Vector3.One;
                }
                return Vector3.One;
            }
        }




        public void SetPosition(Vector3 newpos, bool editPivot)
        {
            if (MultipleSelection)
            {
                //don't do anything here for multiselection
            }
            else if (EntityDef != null)
            {
                if (editPivot)
                {
                    EntityDef.SetPivotPositionFromWidget(newpos);
                }
                else
                {
                    EntityDef.SetPositionFromWidget(newpos);
                }
            }
            else if (CarGenerator != null)
            {
                CarGenerator.SetPosition(newpos);
            }
            else if (PathNode != null)
            {
                PathNode.SetPosition(newpos);
            }
            else if (CollisionPoly != null)
            {
                CollisionPoly.Position = newpos;
            }
            else if (CollisionBounds != null)
            {
                CollisionBounds.Position = newpos;
            }
            else if (NavPoly != null)
            {
                NavPoly.SetPosition(newpos);
            }
            else if (NavPoint != null)
            {
                NavPoint.SetPosition(newpos);
            }
            else if (NavPortal != null)
            {
                NavPortal.SetPosition(newpos);
            }
            else if (TrainTrackNode != null)
            {
                TrainTrackNode.SetPosition(newpos);
            }
            else if (ScenarioNode != null)
            {
                ScenarioNode.SetPosition(newpos);
            }
            else if (Audio != null)
            {
                Audio.SetPosition(newpos);
            }

        }
        public void SetRotation(Quaternion newrot, Quaternion oldrot, bool editPivot)
        {
            if (EntityDef != null)
            {
                if (editPivot)
                {
                    EntityDef.SetPivotOrientationFromWidget(newrot);
                }
                else
                {
                    EntityDef.SetOrientationFromWidget(newrot);
                }
            }
            else if (CarGenerator != null)
            {
                CarGenerator.SetOrientation(newrot);
            }
            else if (ScenarioNode != null)
            {
                ScenarioNode.SetOrientation(newrot);
            }
            else if (NavPoint != null)
            {
                NavPoint.SetOrientation(newrot);
            }
            else if (NavPortal != null)
            {
                NavPortal.SetOrientation(newrot);
            }
            else if (Audio != null)
            {
                Audio.SetOrientation(newrot);
            }
        }
        public void SetScale(Vector3 newscale, Vector3 oldscale, bool editPivot)
        {
            if (EntityDef != null)
            {
                EntityDef.SetScale(newscale);
            }
            else if (CarGenerator != null)
            {
                CarGenerator.SetScale(newscale);
                AABB = new BoundingBox(CarGenerator.BBMin, CarGenerator.BBMax);
            }
        }


        public override string ToString()
        {
            return GetFullNameString("[Empty]");
        }
    }

    public enum MapSelectionMode
    {
        None = 0,
        Entity = 1,
        EntityExtension = 2,
        ArchetypeExtension = 3,
        TimeCycleModifier = 4,
        CarGenerator = 5,
        Grass = 6,
        WaterQuad = 7,
        Collision = 8,
        NavMesh = 9,
        Path = 10,
        TrainTrack = 11,
        DistantLodLights = 12,
        MloInstance = 13,
        Scenario = 14,
        PopZone = 15,
        Audio = 16,
        Occlusion = 17,
    }







}