using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;

namespace CodeWalker.Project.Panels
{
    public partial class EditYmapPanel : ProjectPanel
    {
        public ProjectForm ProjectForm;
        public YmapFile Ymap { get; set; }

        private bool populatingui = false;
        private bool waschanged = false;

        public EditYmapPanel(ProjectForm projectForm)
        {
            ProjectForm = projectForm;
            InitializeComponent();
        }

        public void SetYmap(YmapFile ymap)
        {
            Ymap = ymap;
            Tag = ymap;
            UpdateFormTitle();
            UpdateYmapUI();
            waschanged = ymap?.HasChanged ?? false;
        }

        public void UpdateFormTitleYmapChanged()
        {
            bool changed = Ymap.HasChanged;
            if (!waschanged && changed)
            {
                UpdateFormTitle();
                waschanged = true;
            }
            else if (waschanged && !changed)
            {
                UpdateFormTitle();
                waschanged = false;
            }
        }
        private void UpdateFormTitle()
        {
            string fn = Ymap.RpfFileEntry?.Name ?? Ymap.Name;
            if (string.IsNullOrEmpty(fn)) fn = "untitled.ymap";
            Text = fn + (Ymap.HasChanged ? "*" : "");
        }

        public void UpdateYmapUI()
        {
            if (Ymap == null)
            {
                YmapNameTextBox.Text = "<No ymap selected>";
                YmapNameHashLabel.Text = "Hash: 0";
                YmapParentTextBox.Text = string.Empty;
                YmapParentHashLabel.Text = "Hash: 0";
                YmapFlagsTextBox.Text = string.Empty;
                YmapContentFlagsTextBox.Text = string.Empty;
                YmapCFlagsHDCheckBox.Checked = false;
                YmapCFlagsLODCheckBox.Checked = false;
                YmapCFlagsSLOD2CheckBox.Checked = false;
                YmapCFlagsInteriorCheckBox.Checked = false;
                YmapCFlagsSLODCheckBox.Checked = false;
                YmapCFlagsOcclusionCheckBox.Checked = false;
                YmapCFlagsPhysicsCheckBox.Checked = false;
                YmapCFlagsLODLightsCheckBox.Checked = false;
                YmapCFlagsDistLightsCheckBox.Checked = false;
                YmapCFlagsCriticalCheckBox.Checked = false;
                YmapCFlagsGrassCheckBox.Checked = false;
                YmapFlagsScriptedCheckBox.Checked = false;
                YmapFlagsLODCheckBox.Checked = false;
                YmapPhysicsDictionariesTextBox.Text = string.Empty;
                YmapEntitiesExtentsMinTextBox.Text = string.Empty;
                YmapEntitiesExtentsMaxTextBox.Text = string.Empty;
                YmapStreamingExtentsMinTextBox.Text = string.Empty;
                YmapStreamingExtentsMaxTextBox.Text = string.Empty;
                YmapFileLocationTextBox.Text = string.Empty;
                YmapProjectPathTextBox.Text = string.Empty;
            }
            else
            {
                populatingui = true;
                var md = Ymap.CMapData;
                if (md.name.Hash == 0)
                {
                    string name = Path.GetFileNameWithoutExtension(Ymap.Name);
                    JenkIndex.Ensure(name);
                    md.name = new MetaHash(JenkHash.GenHash(name));
                }

                var project = ProjectForm?.CurrentProjectFile;

                YmapNameTextBox.Text = md.name.ToString();
                YmapNameHashLabel.Text = "Hash: " + md.name.Hash.ToString();
                YmapParentTextBox.Text = md.parent.ToString();
                YmapParentHashLabel.Text = "Hash: " + md.parent.Hash.ToString();
                YmapEntitiesExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMin);
                YmapEntitiesExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMax);
                YmapStreamingExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMin);
                YmapStreamingExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMax);
                YmapFileLocationTextBox.Text = Ymap.RpfFileEntry?.Path ?? Ymap.FilePath;
                YmapProjectPathTextBox.Text = (project != null) ? project.GetRelativePath(Ymap.FilePath) : Ymap.FilePath;

                UpdateYmapFlagsUI(true, true);

                UpdateYmapPhysicsDictionariesUI();

                populatingui = false;

                ////struct CMapData:
                //MetaHash name { get; set; } //8   8: Hash: 0: name
                //MetaHash parent { get; set; } //12   12: Hash: 0: parent
                //uint flags { get; set; } //16   16: UnsignedInt: 0: flags
                //uint contentFlags { get; set; } //20   20: UnsignedInt: 0: contentFlags//1785155637
                //Vector3 streamingExtentsMin { get; set; } //32   32: Float_XYZ: 0: streamingExtentsMin//3710026271
                //Vector3 streamingExtentsMax { get; set; } //48   48: Float_XYZ: 0: streamingExtentsMax//2720965429
                //Vector3 entitiesExtentsMin { get; set; } //64   64: Float_XYZ: 0: entitiesExtentsMin//477478129
                //Vector3 entitiesExtentsMax { get; set; } //80   80: Float_XYZ: 0: entitiesExtentsMax//1829192759
                //Array_StructurePointer entities { get; set; } //96   96: Array: 0: entities  {0: StructurePointer: 0: 256}
                //Array_Structure containerLods { get; set; } //112   112: Array: 0: containerLods//2935983381  {0: Structure: 372253349: 256}
                //Array_Structure boxOccluders { get; set; } //128   128: Array: 0: boxOccluders//3983590932  {0: Structure: SectionUNKNOWN7: 256}
                //Array_Structure occludeModels { get; set; } //144   144: Array: 0: occludeModels//2132383965  {0: Structure: SectionUNKNOWN5: 256}
                //Array_uint physicsDictionaries { get; set; } //160   160: Array: 0: physicsDictionaries//949589348  {0: Hash: 0: 256}
                //rage__fwInstancedMapData instancedData { get; set; } //176   176: Structure: rage__fwInstancedMapData: instancedData//2569067561
                //Array_Structure timeCycleModifiers { get; set; } //224   224: Array: 0: timeCycleModifiers  {0: Structure: CTimeCycleModifier: 256}
                //Array_Structure carGenerators { get; set; } //240   240: Array: 0: carGenerators//3254823756  {0: Structure: CCarGen: 256}
                //CLODLight LODLightsSOA { get; set; } //256   256: Structure: CLODLight: LODLightsSOA//1774371066
                //CDistantLODLight DistantLODLightsSOA { get; set; } //392   392: Structure: CDistantLODLight: DistantLODLightsSOA//2954466641
                //CBlockDesc block { get; set; } //440   440: Structure: CBlockDesc//3072355914: block

            }


        }

        private void UpdateYmapFlagsUI(bool updateCheckboxes, bool updateTextboxes)
        {
            if (Ymap == null) return;

            var md = Ymap.CMapData;
            var flags = md.flags;
            var contentFlags = md.contentFlags;

            if (updateCheckboxes)
            {
                YmapCFlagsHDCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 0); //1
                YmapCFlagsLODCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 1); //2
                YmapCFlagsSLOD2CheckBox.Checked = BitUtil.IsBitSet(contentFlags, 2); //4
                YmapCFlagsInteriorCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 3); //8
                YmapCFlagsSLODCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 4); //16
                YmapCFlagsOcclusionCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 5); //32
                YmapCFlagsPhysicsCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 6); //64
                YmapCFlagsLODLightsCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 7); //128
                YmapCFlagsDistLightsCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 8); //256
                YmapCFlagsCriticalCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 9); //512
                YmapCFlagsGrassCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 10); //1024

                YmapFlagsScriptedCheckBox.Checked = BitUtil.IsBitSet(flags, 0); //1
                YmapFlagsLODCheckBox.Checked = BitUtil.IsBitSet(flags, 1); //2
            }
            if (updateTextboxes)
            {
                YmapFlagsTextBox.Text = flags.ToString();
                YmapContentFlagsTextBox.Text = contentFlags.ToString();
            }
        }

        private void UpdateYmapPhysicsDictionariesUI()
        {
            if ((Ymap == null) || (Ymap.physicsDictionaries == null))
            {
                YmapPhysicsDictionariesTextBox.Text = string.Empty;
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                foreach (var hash in Ymap.physicsDictionaries)
                {
                    sb.AppendLine(hash.ToString());
                }
                YmapPhysicsDictionariesTextBox.Text = sb.ToString();
            }
        }

        private void SetYmapHasChanged(bool changed)
        {
            ProjectForm.SetYmapHasChanged(changed);
            UpdateFormTitleYmapChanged();
        }

        private void SetYmapPhysicsDictionariesFromTextbox()
        {
            if (populatingui) return;
            if (Ymap == null) return;

            List<MetaHash> hashes = new List<MetaHash>();

            var strs = YmapPhysicsDictionariesTextBox.Text.Split('\n');
            foreach (var str in strs)
            {
                var tstr = str.Trim();
                if (!string.IsNullOrEmpty(tstr))
                {
                    uint h = 0;
                    if (uint.TryParse(tstr, out h))
                    {
                        hashes.Add(h);
                    }
                    else
                    {
                        h = JenkHash.GenHash(tstr.ToLowerInvariant());
                        hashes.Add(h);
                    }
                }
            }

            lock (ProjectForm.ProjectSyncRoot)
            {
                Ymap.physicsDictionaries = (hashes.Count > 0) ? hashes.ToArray() : null;
                SetYmapHasChanged(true);
            }
        }

        private void SetYmapFlagsFromCheckBoxes()
        {
            if (populatingui) return;
            if (Ymap == null) return;

            uint flags = 0;
            uint contentFlags = 0;

            contentFlags = BitUtil.UpdateBit(contentFlags, 0, YmapCFlagsHDCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 1, YmapCFlagsLODCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 2, YmapCFlagsSLOD2CheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 3, YmapCFlagsInteriorCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 4, YmapCFlagsSLODCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 5, YmapCFlagsOcclusionCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 6, YmapCFlagsPhysicsCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 7, YmapCFlagsLODLightsCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 8, YmapCFlagsDistLightsCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 9, YmapCFlagsCriticalCheckBox.Checked);
            contentFlags = BitUtil.UpdateBit(contentFlags, 10, YmapCFlagsGrassCheckBox.Checked);

            flags = BitUtil.UpdateBit(flags, 0, YmapFlagsScriptedCheckBox.Checked);
            flags = BitUtil.UpdateBit(flags, 1, YmapFlagsLODCheckBox.Checked);


            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap._CMapData.flags != flags)
                {
                    Ymap._CMapData.flags = flags;
                    SetYmapHasChanged(true);
                }
                if (Ymap._CMapData.contentFlags != contentFlags)
                {
                    Ymap._CMapData.contentFlags = contentFlags;
                    SetYmapHasChanged(true);
                }
            }

            populatingui = true;
            UpdateYmapFlagsUI(false, true); //update textbox
            populatingui = false;
        }

        private void SetYmapFlagsFromTextBoxes()
        {
            if (populatingui) return;
            if (Ymap == null) return;

            uint flags = 0;
            uint contentFlags = 0;
            uint.TryParse(YmapFlagsTextBox.Text, out flags);
            uint.TryParse(YmapContentFlagsTextBox.Text, out contentFlags);

            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap._CMapData.flags != flags)
                {
                    Ymap._CMapData.flags = flags;
                    SetYmapHasChanged(true);
                }
                if (Ymap._CMapData.contentFlags != contentFlags)
                {
                    Ymap._CMapData.contentFlags = contentFlags;
                    SetYmapHasChanged(true);
                }
            }

            populatingui = true;
            UpdateYmapFlagsUI(true, false); //update checkboxes
            populatingui = false;
        }

        private void CalcYmapFlags()
        {
            if (populatingui) return;
            if (Ymap == null) return;

            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap.CalcFlags())
                {
                    SetYmapHasChanged(true);
                }
            }

            populatingui = true;
            UpdateYmapFlagsUI(true, true); //update checkboxes and textboxes
            populatingui = false;
        }

        private void CalcYmapExtents()
        {
            if (populatingui) return;
            if (Ymap == null) return;

            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap.CalcExtents())
                {
                    SetYmapHasChanged(true);
                }
            }

            populatingui = true;
            var md = Ymap.CMapData;
            YmapEntitiesExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMin);
            YmapEntitiesExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMax);
            YmapStreamingExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMin);
            YmapStreamingExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMax);
            populatingui = false;
        }


        private void YmapNameTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui) return;
            uint hash = 0;
            string name = YmapNameTextBox.Text;
            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            YmapNameHashLabel.Text = "Hash: " + hash.ToString();

            if (Ymap != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    Ymap.SetName(name);
                    if (!File.Exists(Ymap.FilePath))
                    {
                        Ymap.FilePath = name;
                    }
                    SetYmapHasChanged(true);
                    UpdateFormTitle();
                }
            }
        }

        private void YmapParentTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui) return;
            uint hash = 0;
            string name = YmapParentTextBox.Text;
            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            YmapParentHashLabel.Text = "Hash: " + hash.ToString();

            if (hash != 0)
            {
                var entry = ProjectForm.FindParentYmapEntry(hash);
                if (entry == null)
                {
                    YmapParentHashLabel.Text += " (not found!)";
                }
            }

            if (Ymap != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    if (Ymap._CMapData.parent.Hash != hash)
                    {
                        Ymap._CMapData.parent = new MetaHash(hash);
                        SetYmapHasChanged(true);

                        //TODO: confirm entity parent linkage?
                    }
                }
            }

        }

        private void YmapFlagsTextBox_TextChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromTextBoxes();
        }

        private void YmapContentFlagsTextBox_TextChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromTextBoxes();
        }

        private void YmapCFlagsHDCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsLODCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsSLOD2CheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsInteriorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsSLODCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsOcclusionCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsPhysicsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsLODLightsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsDistLightsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsCriticalCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCFlagsGrassCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapFlagsScriptedCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapFlagsLODCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            SetYmapFlagsFromCheckBoxes();
        }

        private void YmapCalculateFlagsButton_Click(object sender, EventArgs e)
        {
            CalcYmapFlags();
        }

        private void YmapEntitiesExtentsMinTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui) return;
            if (Ymap == null) return;
            Vector3 v = FloatUtil.ParseVector3String(YmapEntitiesExtentsMinTextBox.Text);
            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap._CMapData.entitiesExtentsMin != v)
                {
                    Ymap._CMapData.entitiesExtentsMin = v;
                    SetYmapHasChanged(true);
                }
            }
        }

        private void YmapEntitiesExtentsMaxTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui) return;
            if (Ymap == null) return;
            Vector3 v = FloatUtil.ParseVector3String(YmapEntitiesExtentsMaxTextBox.Text);
            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap._CMapData.entitiesExtentsMax != v)
                {
                    Ymap._CMapData.entitiesExtentsMax = v;
                    SetYmapHasChanged(true);
                }
            }
        }

        private void YmapStreamingExtentsMinTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui) return;
            if (Ymap == null) return;
            Vector3 v = FloatUtil.ParseVector3String(YmapStreamingExtentsMinTextBox.Text);
            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap._CMapData.streamingExtentsMin != v)
                {
                    Ymap._CMapData.streamingExtentsMin = v;
                    SetYmapHasChanged(true);
                }
            }
        }

        private void YmapStreamingExtentsMaxTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui) return;
            if (Ymap == null) return;
            Vector3 v = FloatUtil.ParseVector3String(YmapStreamingExtentsMaxTextBox.Text);
            lock (ProjectForm.ProjectSyncRoot)
            {
                if (Ymap._CMapData.streamingExtentsMax != v)
                {
                    Ymap._CMapData.streamingExtentsMax = v;
                    SetYmapHasChanged(true);
                }
            }
        }

        private void YmapCalculateExtentsButton_Click(object sender, EventArgs e)
        {
            CalcYmapExtents();
        }

        private void YmapPhysicsDictionariesTextBox_TextChanged(object sender, EventArgs e)
        {
            SetYmapPhysicsDictionariesFromTextbox();
        }
    }
}
