using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Syroot.Maths;
using Toolbox.Library.IO;
using Toolbox.Library.Animations;
using Toolbox.Library;
using WeifenLuo.WinFormsUI.Docking;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace LayoutBXLYT
{
    public class BasePane : SectionCommon,  ICloneable
    {
        [Browsable(false)]
        public BxlytHeader LayoutFile;

        [Browsable(false)]
        public PaneAnimController animController = new PaneAnimController();

        [Browsable(false)]
        public TreeNodeCustom NodeWrapper;

        [DisplayName("Is Visible"), CategoryAttribute("Flags")]
        public virtual bool Visible { get; set; }

        [Browsable(false)]
        public bool IsRoot
        {
            get { return Parent == null; }
        }

        [Browsable(false)]
        public bool ParentIsRoot
        {
            get { return Parent != null && Parent.IsRoot; }
        }

        internal RenderablePane renderablePane;

        [DisplayName("Alpha"), CategoryAttribute("Alpha")]
        public byte Alpha { get; set; }

        [DisplayName("Influence Alpha"), CategoryAttribute("Alpha")]
        public virtual bool InfluenceAlpha { get; set; }

        [Browsable(false)]
        public virtual bool DisplayInEditor { get; set; } = true;

        private string name;

        [DisplayName("Name"), CategoryAttribute("Pane")]
        public string Name
        {
            get { return name; }
            set
            {
                //Adjust necessary parameters if the user changes the name
                if (name != null && LayoutFile != null)
                {
                    //Adjust name table entry
                    if (LayoutFile != null && LayoutFile.PaneLookup.ContainsKey(name))
                        LayoutFile.PaneLookup.Remove(name);

                    //Adjust material reference
                    if (this is IPicturePane)
                        ((IPicturePane)this).Material.SetName(name, value);
                    else if (this is ITextPane)
                        ((ITextPane)this).Material.SetName(name, value);
                    else if (this is IWindowPane)
                    {
                        var wnd = this as IWindowPane;
                        wnd.Content?.Material?.SetName(name, value);

                        if (wnd.WindowFrames != null)
                        {
                            foreach (var frame in wnd.WindowFrames)
                            {
                                if (frame.Material == null) continue;
                                frame.Material.SetName(name, value);
                            }
                        }
                    }
                }

                name = value;

                if (LayoutFile != null && !LayoutFile.PaneLookup.ContainsKey(name))
                    LayoutFile.PaneLookup.Add(name, this);
            }
        }

        public BxlytMaterial TryGetActiveMaterial()
        {
            if (this is IPicturePane)
                return ((IPicturePane)this).Material;

            return null;
        }

        [DisplayName("Translate"), CategoryAttribute("Pane")]
        public Vector3F Translate { get; set; }

        [DisplayName("Rotate"), CategoryAttribute("Pane")]
        public Vector3F Rotate { get; set; }

        [DisplayName("Scale"), CategoryAttribute("Pane")]
        public Vector2F Scale { get; set; }

        [DisplayName("Width"), CategoryAttribute("Pane")]
        public float Width { get; set; }

        [DisplayName("Height"), CategoryAttribute("Pane")]
        public float Height { get; set; }

        [DisplayName("Origin X"), CategoryAttribute("Origin")]
        public virtual OriginX originX { get; set; }

        [DisplayName("Origin X"), CategoryAttribute("Origin")]
        public virtual OriginY originY { get; set; }

        [DisplayName("Parent Origin X"), CategoryAttribute("Origin")]
        public virtual OriginX ParentOriginX { get; set; }

        [DisplayName("Parent Origin Y"), CategoryAttribute("Origin")]
        public virtual OriginY ParentOriginY { get; set; }

        [Browsable(false)]
        public BasePane Parent { get; set; }

        [Browsable(false)]
        public List<BasePane> Childern { get; set; } = new List<BasePane>();

        [Browsable(false)]
        public bool HasChildern
        {
            get { return Childern.Count > 0; }
        }

        public OpenTK.Matrix4 GetRotationMatrix()
        {
            var paneRotate = GetRotation();
            OpenTK.Matrix4 rotationX = OpenTK.Matrix4.CreateRotationX(OpenTK.MathHelper.DegreesToRadians(paneRotate.X));
            OpenTK.Matrix4 rotationY = OpenTK.Matrix4.CreateRotationY(OpenTK.MathHelper.DegreesToRadians(paneRotate.Y));
            OpenTK.Matrix4 rotationZ = OpenTK.Matrix4.CreateRotationZ(OpenTK.MathHelper.DegreesToRadians(paneRotate.Z));
            var rotationMatrix = rotationX * rotationY * rotationZ;

            if (Parent != null)
                rotationMatrix = Parent.GetRotationMatrix() * rotationMatrix;
            return rotationMatrix;
        }

        public Vector3F GetTranslation()
        {
            var posX = Translate.X;
            var posY = Translate.Y;
            var posZ = Translate.Z;
            if (animController.PaneSRT.ContainsKey(LPATarget.TranslateX))
                posX = animController.PaneSRT[LPATarget.TranslateX];
            if (animController.PaneSRT.ContainsKey(LPATarget.TranslateY))
                posY = animController.PaneSRT[LPATarget.TranslateY];
            if (animController.PaneSRT.ContainsKey(LPATarget.TranslateZ))
                posZ = animController.PaneSRT[LPATarget.TranslateZ];
            return new Vector3F(posX, posY, posZ);
        }

        public Vector3F GetRotation()
        {
            var rotX = Rotate.X;
            var rotY = Rotate.Y;
            var rotZ = Rotate.Z;
            if (animController.PaneSRT.ContainsKey(LPATarget.RotateX))
                rotX = animController.PaneSRT[LPATarget.RotateX];
            if (animController.PaneSRT.ContainsKey(LPATarget.RotateY))
                rotY = animController.PaneSRT[LPATarget.RotateY];
            if (animController.PaneSRT.ContainsKey(LPATarget.RotateZ))
                rotZ = animController.PaneSRT[LPATarget.RotateZ];
            return new Vector3F(rotX, rotY, rotZ);
        }

        public Vector2F GetScale()
        {
            var scaX = Scale.X;
            var scaY = Scale.Y;
            if (animController.PaneSRT.ContainsKey(LPATarget.ScaleX))
                scaX = animController.PaneSRT[LPATarget.ScaleX];
            if (animController.PaneSRT.ContainsKey(LPATarget.ScaleY))
                scaY = animController.PaneSRT[LPATarget.ScaleY];
            return new Vector2F(scaX, scaY);
        }

        public Vector2F GetSize()
        {
            var scaX = Width;
            var scaY = Height;
            if (animController.PaneSRT.ContainsKey(LPATarget.SizeX))
                scaX = animController.PaneSRT[LPATarget.SizeX];
            if (animController.PaneSRT.ContainsKey(LPATarget.SizeY))
                scaY = animController.PaneSRT[LPATarget.SizeY];
            return new Vector2F(scaX, scaY);
        }

        public BasePane()
        {
            originX = OriginX.Center;
            originY = OriginY.Center;
            ParentOriginX = OriginX.Center;
            ParentOriginY = OriginY.Center;
        }

        public virtual BasePane Copy()
        {
            return new BasePane();
        }

        public object Clone()
        {
            BasePane copiedPane = (BasePane)this.MemberwiseClone();
            if (copiedPane is IPicturePane)
                ((IPicturePane)copiedPane).CopyMaterial();
            if (copiedPane is ITextPane)
                ((ITextPane)copiedPane).CopyMaterial();
            if (copiedPane is IWindowPane)
                ((IWindowPane)copiedPane).CopyWindows();
            return copiedPane;
        }

        public virtual UserData CreateUserData()
        {
            return new UserData();
        }

        public bool IsNullPane
        {
            get
            {
                if (this is IPicturePane)
                    return false;
                else if (this is IWindowPane)
                    return false;
                else if (this is ITextPane)
                    return false;
                else if (this is IBoundryPane)
                    return false;
                else if (this is IPartPane)
                    return false;

                return true;
            }
        }

        public void ResetParentTransform(BasePane newParent)
        {
            //We need to get the difference in the parent transform and remove it to the current transform of this pane
            var transform = GetParentTransform(Parent);
            var newParentTransform = GetParentTransform(newParent);
            Translate += transform;
            Translate -= newParentTransform;

            Console.WriteLine($"newParentTransform {newParentTransform.X} {newParentTransform.Y} {newParentTransform.Z}");
            Console.WriteLine($"transform {transform.X} {transform.Y} {transform.Z}");
        }

        private Vector3F GetParentTransform(BasePane parent) {
            return GetParentTransform(parent.Translate);
        }

        private Vector3F GetParentTransform(Vector3F translate)
        {
            if (Parent != null)
                translate = Parent.Translate + Parent.GetParentTransform(translate);

            return translate;

        }

        public void KeepChildrenTransform(float newTransX, float newTransY)
        {
            Vector2F distance = new Vector2F(newTransX - Translate.X, newTransY - Translate.Y);
            KeepChildrenTransform(distance, newTransX, newTransY);
        }

        private void KeepChildrenTransform(Vector2F distance, float newTransX, float newTransY)
        {
            if (HasChildern)
            {
                foreach (var child in Childern)
                {
                    child.Translate -= new Vector3F(distance.X, distance.Y, 0);
                    child.KeepChildrenTransform(child.Translate.X, child.Translate.Y);
                }
            }
        }

        public CustomRectangle TransformParent(CustomRectangle rect)
        {
            return rect.GetTransformedRectangle(Parent, GetTranslation(),GetRotation(), GetScale());
        }

        private CustomRectangle rectangle;

        [Browsable(false)]
        public CustomRectangle Rectangle
        {
            get
            {
                UpdateRectangle();
                return rectangle;
            }
        }

        private void UpdateRectangle()
        {
            rectangle = CreateRectangle();
        }

        public CustomRectangle CreateRectangle(uint width, uint height)
        {
            //Do origin transforms
            var transformed = TransformOrientation((int)width, (int)height, originX, originY);
            var parentTransform = ParentOriginTransform(transformed);

            return new CustomRectangle(
                parentTransform.X,
                parentTransform.Y,
                parentTransform.Z,
                parentTransform.W);
        }

        public void TransformRectangle(LayoutViewer.PickAction pickAction, CustomRectangle selectionBox, float pickMouseX, float pickMouseY)
        {
            CustomRectangle currentRectangle = this.CreateRectangle();

            currentRectangle = currentRectangle.GetTransformedRectangle(Parent, GetTranslation(), GetRotation(), GetScale());

            //The selection box can have mutlile panes selected in one box
            //Scaling the edges will have different distances
            float distanceLeft = selectionBox.LeftPoint - currentRectangle.LeftPoint;
            float distanceRight = selectionBox.RightPoint - currentRectangle.RightPoint;
            float distanceTop = selectionBox.TopPoint - currentRectangle.TopPoint;
            float distanceBottom = selectionBox.BottomPoint - currentRectangle.BottomPoint;

            Console.WriteLine("distanceLeft " + distanceLeft);
            Console.WriteLine("distanceRight " + distanceRight);
            Console.WriteLine("distanceTop " + distanceTop);
            Console.WriteLine("distanceBottom " + distanceBottom);


            float posX = Translate.X;
            float posY = Translate.Y;
            float posZ = Translate.Z;


            float pickWidth = pickMouseX;
            float pickHeight = pickMouseY;

            switch (pickAction)
            {
                case LayoutViewer.PickAction.DragLeft:
                    Width += pickMouseX;

                    if (originX == OriginX.Left)
                        posX = Translate.X - pickMouseX;
                    else if (originX == OriginX.Right)
                        posX = Translate.X + pickMouseX;
                    else
                        posX = Translate.X - (pickMouseX / 2);
                    break;
                case LayoutViewer.PickAction.DragRight:

                    if (originX == OriginX.Right)
                    {
                        Width -= pickMouseX;
                        posX = Translate.X + pickMouseX;
                    }
                    else if (originX == OriginX.Left)
                    {
                        Width -= pickMouseX;
                        posX = Translate.X + pickMouseX;
                    }
                    else
                    {
                        Width -= pickMouseX;
                        posX = Translate.X - (pickMouseX / 2);
                    }
                    break;
                case LayoutViewer.PickAction.DragTop:
                    if (originY == OriginY.Top)
                    {
                        Height -= pickMouseY;
                        posY = Translate.Y - pickMouseY;
                    }
                    else if (originY == OriginY.Bottom)
                    {
                        Height -= pickMouseY;
                        posY = Translate.Y - pickMouseY;
                    }
                    else
                    {
                        Height -= pickMouseY;
                        posY = Translate.Y - (pickMouseY / 2);
                    }
                    break;
                case LayoutViewer.PickAction.DragBottom:
                    Height += pickMouseY;
                    posY = Translate.Y - (pickMouseY / 2);
                    break;
                case LayoutViewer.PickAction.DragTopLeft:
                    Width += pickMouseX;
                    Height -= pickMouseY;
                    posX = Translate.X - (pickMouseX / 2);
                    posY = Translate.Y - (pickMouseY / 2);
                    break;
                case LayoutViewer.PickAction.DragBottomLeft:
                    Width += pickMouseX;
                    Height += pickMouseY;
                    posX = Translate.X - (pickMouseX / 2);
                    posY = Translate.Y - (pickMouseY / 2);
                    break;
                case LayoutViewer.PickAction.DragBottomRight:
                    Width -= pickMouseX;
                    Height += pickMouseY;
                    posX = Translate.X - (pickMouseX / 2);
                    posY = Translate.Y - (pickMouseY / 2);
                    break;
                case LayoutViewer.PickAction.DragTopRight:
                    Width -= pickMouseX;
                    Height -= pickMouseY;
                    posX = Translate.X - (pickMouseX / 2);
                    posY = Translate.Y - (pickMouseY / 2);
                    break;
            }

            if (!Runtime.LayoutEditor.TransformChidlren)
                KeepChildrenTransform(posX, posY);

            Translate = new Vector3F(posX, posY, posZ);
        }

        private Vector2F SetOrientation(float pickWidth, float pickHeight)
        {
            float posX = 0;
            float posY = 0;
            switch (originX)
            {
                case OriginX.Left:
                    Width += pickWidth;
                    posX = pickWidth;
                    break;
                default:
                    Width += pickWidth;
                    posX = pickWidth / 2;
                    break;
            }
            switch (originY)
            {
                case OriginY.Top:
                    Height += pickHeight;
                    posY = pickHeight;
                    break;
                default:
                    Height += pickHeight;
                    posY = pickHeight / 2;
                    break;
            }
            return new Vector2F(posX, posY);
        }

        public CustomRectangle CreateRectangle()
        {
            //Do origin transforms
            var transformed = new Vector4();
            int paneWidth = (int)Width;
            int paneHeight = (int)Height;
            if (animController.PaneSRT.ContainsKey(LPATarget.SizeX))
                paneWidth = (int)animController.PaneSRT[LPATarget.SizeX];
            if (animController.PaneSRT.ContainsKey(LPATarget.SizeY))
                paneHeight = (int)animController.PaneSRT[LPATarget.SizeY];

            transformed = TransformOrientation(paneWidth, paneHeight, originX, originY);
            var parentTransform = ParentOriginTransform(transformed);

            return new CustomRectangle(
                parentTransform.X,
                parentTransform.Y,
                parentTransform.Z,
                parentTransform.W);
        }

        public static float MixColors(params float[] c)
        {
            float a = c[0];
            for (int i = 1; i < c.Length; i++)
            {
                a *= c[i];
            }
            for (int i = 1; i < c.Length; i++)
            {
                a /= 255f;
            }
            return a / 255f;
        }

        //Get the previous transform from the parent origin
        private Vector4 ParentOriginTransform(Vector4 points)
        {
            //Dont shift the root or the first child of the root
            //The parent setting shouldn't be set, but it doesn't hurt to do this
            if (IsRoot || ParentIsRoot || Parent == null)
                return points;

            var transformedPosition = TransformOrientationPosition((int)Parent.Width, (int)Parent.Height, ParentOriginX, ParentOriginY);
            var transformed = ShiftRectangle(transformedPosition, points);
            if (Parent != null)
                return Parent.ParentOriginTransform(transformed);

            return transformed;
        }

        private static Vector4 ShiftRectangle(Vector2 position, Vector4 points)
        {
            int left = points[0] + position.X;
            int right = points[1] + position.X;
            int top = points[2] + position.Y;
            int bottom = points[3] + position.Y;
            return new Vector4(left, right, top, bottom);
        }

        private static Vector2 TransformOrientationPosition(int Width, int Height, OriginX originX, OriginY originY)
        {
            int x = 0;
            int y = 0;

            if (originX == OriginX.Left)
                x = -(Width / 2);
            else if (originX == OriginX.Right)
                x = (Width / 2);

            if (originY == OriginY.Top)
                y = Height / 2;
            else if (originY == OriginY.Bottom)
                y = -(Height / 2);

            return new Vector2(x, y);
        }

        private static Vector4 TransformOrientation(int Width, int Height, OriginX originX, OriginY originY)
        {
            int left = 0;
            int right = 0;
            int top = 0;
            int bottom = 0;

            if (originX == OriginX.Left)
                right = Width;
            else if (originX == OriginX.Right)
                left = -Width;
            else //To center
            {
                left = -Width / 2;
                right = Width / 2;
            }

            if (originY == OriginY.Top)
                bottom = -Height;
            else if (originY == OriginY.Bottom)
                top = Height;
            else //To center
            {
                top = Height / 2;
                bottom = -Height / 2;
            }

            return new Vector4(left, right, top, bottom);
        }

        public bool IsHit(CustomRectangle selectionBox)
        {
            var rect = CreateRectangle();
            var transformed = rect.GetTransformedRectangle(Parent, GetTranslation(), GetRotation(), GetScale());
      
            if ((selectionBox.LeftPoint < transformed.RightPoint) &&
                (selectionBox.RightPoint > transformed.LeftPoint) &&
                (selectionBox.TopPoint > transformed.BottomPoint) &&
                (selectionBox.BottomPoint < transformed.TopPoint))
                return true;
            else
                return false;
        }

        public bool IsHit(int X, int Y)
        {
            var rect = CreateRectangle();
            var transformed = rect.GetTransformedRectangle(Parent, GetTranslation(), GetRotation(), GetScale());

            bool isInBetweenX = (X > transformed.LeftPoint) && (X < transformed.RightPoint) ||
                                (X < transformed.LeftPoint) && (X > transformed.RightPoint);

            bool isInBetweenY = (Y > transformed.BottomPoint) && (Y < transformed.TopPoint) ||
                                (Y < transformed.BottomPoint) && (Y > transformed.TopPoint);

            if (isInBetweenX && isInBetweenY)
                return true;
            else
                return false;
        }

        public BasePane SearchPane(string name)
        {
            if (Name == name)
                return this;

            foreach (var child in Childern)
            {
                var matchPane = child.SearchPane(name);
                if (matchPane != null) return matchPane;
            }

            return null;
        }
    }

    public class VertexColorHelper
    {
        public static System.Drawing.Color Mix(System.Drawing.Color colorA, System.Drawing.Color colorB, float value)
        {
            byte R = (byte)InterpolationHelper.Lerp(colorA.R, colorB.R, value);
            byte G = (byte)InterpolationHelper.Lerp(colorA.G, colorB.G, value);
            byte B = (byte)InterpolationHelper.Lerp(colorA.B, colorB.B, value);
            byte A = (byte)InterpolationHelper.Lerp(colorA.A, colorB.A, value);
            return System.Drawing.Color.FromArgb(A, R, G, B);
        }
    }


    public class PaneAnimController
    {
        public Dictionary<LPATarget, float> PaneSRT = new Dictionary<LPATarget, float>();
        public Dictionary<LVCTarget, float> PaneVertexColors = new Dictionary<LVCTarget, float>();
        public bool Visibile = true;

        public void ResetAnim()
        {
            Visibile = true;
            PaneSRT.Clear();
            PaneVertexColors.Clear();
        }
    }

    public enum FilterMode
    {
        Near = 0,
        Linear = 1
    }

    public enum WrapMode
    {
        Clamp = 0,
        Repeat = 1,
        Mirror = 2
    }

    public enum OriginX : byte
    {
        Center = 0,
        Left = 1,
        Right = 2
    };

    public enum OriginY : byte
    {
        Center = 0,
        Top = 1,
        Bottom = 2
    };

    public interface IUserDataContainer
    {
        UserData UserData { get; set; }
    }

    public class BxlytTextureRef
    {
        public short ID { get; set; }

        public string Name { get; set; }

        public virtual WrapMode WrapModeU { get; set; }
        public virtual WrapMode WrapModeV { get; set; }
        public virtual FilterMode MinFilterMode { get; set; }
        public virtual FilterMode MaxFilterMode { get; set; }
    }

    public class BxlytAlphaCompare
    {
        public GfxAlphaFunction CompareMode { get; set; }
        public float Value { get; set; }

        public BxlytAlphaCompare()
        {
            CompareMode = GfxAlphaFunction.Always;
            Value = 0;
        }

        public BxlytAlphaCompare(FileReader reader, BxlytHeader header)
        {
            CompareMode = reader.ReadEnum<GfxAlphaFunction>(false);
            reader.ReadBytes(0x3);
            Value = reader.ReadSingle();
        }

        public void Write(FileWriter writer)
        {
            writer.Write(CompareMode, false);
            writer.Seek(3);
            writer.Write(Value);
        }
    }

    public class BxlytBlendMode
    {
        public GX2BlendOp BlendOp { get; set; }
        public GX2BlendFactor SourceFactor { get; set; }
        public GX2BlendFactor DestFactor { get; set; }
        public GX2LogicOp LogicOp { get; set; }

        public BxlytBlendMode()
        {
            BlendOp = GX2BlendOp.Add;
            SourceFactor = GX2BlendFactor.SourceAlpha;
            DestFactor = GX2BlendFactor.SourceInvAlpha;
            LogicOp = GX2LogicOp.Disable;
        }

        public BxlytBlendMode(FileReader reader, BxlytHeader header)
        {
            BlendOp = (GX2BlendOp)reader.ReadByte();
            SourceFactor = (GX2BlendFactor)reader.ReadByte();
            DestFactor = (GX2BlendFactor)reader.ReadByte();
            LogicOp = (GX2LogicOp)reader.ReadByte();
        }

        public void Write(FileWriter writer)
        {
            writer.Write(BlendOp, false);
            writer.Write(SourceFactor, false);
            writer.Write(DestFactor, false);
            writer.Write(LogicOp, false);
        }

        public enum GX2BlendFactor : byte
        {
            Factor0 = 0,
            Factor1 = 1,
            DestColor = 2,
            DestInvColor = 3,
            SourceAlpha = 4,
            SourceInvAlpha = 5,
            DestAlpha = 6,
            DestInvAlpha = 7,
            SourceColor = 8,
            SourceInvColor = 9
        }

        public enum GX2BlendOp : byte
        {
            Disable = 0,
            Add = 1,
            Subtract = 2,
            ReverseSubtract = 3,
            SelectMin = 4,
            SelectMax = 5
        }

        public enum GX2LogicOp : byte
        {
            Disable = 0,
            NoOp = 1,
            Clear = 2,
            Set = 3,
            Copy = 4,
            InvCopy = 5,
            Inv = 6,
            And = 7,
            Nand = 8,
            Or = 9,
            Nor = 10,
            Xor = 11,
            Equiv = 12,
            RevAnd = 13,
            InvAd = 14,
            RevOr = 15,
            InvOr = 16
        }
    }

    public class UserData : SectionCommon
    {
        public List<UserDataEntry> Entries { get; set; }

        public UserData()
        {
            Entries = new List<UserDataEntry>();
        }

        public override void Write(FileWriter writer, LayoutHeader header)
        {
        }
    }

    public class UserDataEntry
    {
        public string Name { get; set; }
        public UserDataType Type { get; set; }
        public byte Unknown { get; set; }

        public object data;

        public string GetString()
        {
            return (string)data;
        }

        public float[] GetFloats()
        {
            return (float[])data;
        }

        public int[] GetInts()
        {
            return (int[])data;
        }

        public byte[] GetBytes()
        {
            return (byte[])data;
        }

        public void SetValue(string value)
        {
            data = value;
            Type = UserDataType.String;
        }

        public void SetValue(float[] value)
        {
            data = value;
            Type = UserDataType.Float;
        }

        public void SetValue(int[] value)
        {
            data = value;
            Type = UserDataType.Int;
        }

        public void SetValue(List<UserDataStruct> value)
        {
            data = value;
            Type = UserDataType.StructData;
        }

        public List<UserDataStruct> GetStructs()
        {
            return (List<UserDataStruct>)data;
        }

        internal long _pos;
    }

    public class UserDataStruct
    {
        public ushort Unknown { get; set; } // 0

        public List<UserDataStructEntry> Entries = new List<UserDataStructEntry>();

        public UserDataStruct(FileReader reader, BxlytHeader header)
        {
            long pos = reader.Position;

            ushort unknown = reader.ReadUInt16();
            ushort numEntries = reader.ReadUInt16();
            uint[] offsets = reader.ReadUInt32s(numEntries);


            for (int i = 0; i < numEntries; i++)
            {
                reader.SeekBegin(pos + offsets[i]);
                Entries.Add(new UserDataStructEntry(reader, header));
            }
        }

        public void Write(FileWriter writer, LayoutHeader header)
        {
            long pos = writer.Position;

            writer.Write(Unknown);
            writer.Write((ushort)Entries.Count);

            long _ofsPos = writer.Position;
            //Fill empty spaces for offsets later
            writer.Write(new uint[Entries.Count]);

            for (int i = 0; i < Entries.Count; i++)
            {
                writer.WriteUint32Offset(_ofsPos + (i * 4), pos);
                Entries[i].Write(writer, header);
            }
        }
    }

    public class UserDataStructEntry
    {
        public uint DataType { get; set; }

        public List<object> Data { get; set; }

        public UserDataStructEntry(FileReader reader, BxlytHeader header)
        {
            long pos = reader.Position;

            DataType = reader.ReadUInt32();
            Console.WriteLine($"USD1 Struct DataType " + DataType);
            if (DataType == 0)
            {
                uint numEntries = reader.ReadUInt32();
                uint[] offsets = reader.ReadUInt32s((int)numEntries);

                Data = new List<object>();
                for (int i = 0; i < numEntries; i++)
                {
                    reader.SeekBegin(pos + offsets[i]);
                    switch (DataType)
                    {
                        case 0:
                            string str = reader.ReadZeroTerminatedString();
                            Console.WriteLine($"USD1 str " + str);
                            Data.Add(str);
                            break;
                    }
                }
            }
        }

        public void Write(FileWriter writer, LayoutHeader header)
        {
            long pos = writer.Position;

            writer.Write(DataType);
            switch (DataType)
            {
                case 0:
                    writer.Write(Data.Count);
                    long _ofsPos = writer.Position;
                    //Fill empty spaces for offsets later
                    writer.Write(new uint[Data.Count]);

                    for (int i = 0; i < Data.Count; i++)
                    {
                        writer.WriteUint32Offset(_ofsPos + (i * 4), pos);
                        writer.WriteString((string)Data[i]);

                        if (i == Data.Count - 1)
                            writer.Align(64);
                    }
                    break;
            }
        }
    }

    public enum UserDataType : byte
    {
        String,
        Int,
        Float,
        StructData,
    }

    public enum AnimationTarget : byte
    {
        Pane = 0,
        Material = 1
    }

    public enum LPATarget : byte
    {
        TranslateX = 0x00,
        TranslateY = 0x01,
        TranslateZ = 0x02,
        RotateX = 0x03,
        RotateY = 0x04,
        RotateZ = 0x05,
        ScaleX = 0x06,
        ScaleY = 0x07,
        SizeX = 0x08,
        SizeY = 0x09,
    }

    public enum LTSTarget : byte
    {
        TranslateS = 0x00,
        TranslateT = 0x01,
        Rotate = 0x02,
        ScaleS = 0x03,
        ScaleT = 0x04,
    }

    public enum LVITarget : byte
    {
        Visibility = 0x00,
    }

    public enum LVCTarget : byte
    {
        LeftTopRed = 0x00,
        LeftTopGreen = 0x01,
        LeftTopBlue = 0x02,
        LeftTopAlpha = 0x03,

        RightTopRed = 0x04,
        RightTopGreen = 0x05,
        RightTopBlue = 0x06,
        RightTopAlpha = 0x07,

        LeftBottomRed = 0x08,
        LeftBottomGreen = 0x09,
        LeftBottomBlue = 0x0A,
        LeftBottomAlpha = 0x0B,

        RightBottomRed = 0x0C,
        RightBottomGreen = 0x0D,
        RightBottomBlue = 0x0E,
        RightBottomAlpha = 0x0F,

        PaneAlpha = 0x10,
    }

    public enum LTPTarget : byte
    {
        Image1 = 0x00,
        Image2 = 0x01, //Unsure if mutliple are used but just in case
        Image3 = 0x02,
    }

    public enum LMCTarget : byte
    {
        BlackColorRed,
        BlackColorGreen,
        BlackColorBlue,
        BlackColorAlpha,
        WhiteColorRed,
        WhiteColorGreen,
        WhiteColorBlue,
        WhiteColorAlpha,
        TextureColorBlendRatio,
        TexColor0Red,
        TexColor0Green,
        TexColor0Blue,
        TexColor0Alpha,
        TexColor1Red,
        TexColor1Green,
        TexColor1Blue,
        TexColor1Alpha,
        TexColor2Red,
        TexColor2Green,
        TexColor2Blue,
        TexColor2Alpha,
        TevKonstantColor0Red,
        TevKonstantColor0Green,
        TevKonstantColor0Blue,
        TevKonstantColor0Alpha,
        TevKonstantColor1Red,
        TevKonstantColor1Green,
        TevKonstantColor1Blue,
        TevKonstantColor1Alpha,
        TevKonstantColor2Red,
        TevKonstantColor2Green,
        TevKonstantColor2Blue,
        TevKonstantColor2Alpha,
    }

    public enum LIMTarget : byte
    {
        Rotation,
        ScaleU,
        ScaleV,
    }

    public enum LFSTarget : byte
    {
        FontShadowBlackColorRed,
        FontShadowBlackColorGreen,
        FontShadowBlackColorBlue,
        FontShadowBlackColorAlpha,
        FontShadowWhiteColorRed,
        FontShadowWhiteColorGreen,
        FontShadowWhiteColorBlue,
        FontShadowWhiteColorAlpha,
    }

    public enum LCTTarget : byte
    {
        FontShadowBlackColorRed,
        FontShadowBlackColorGreen,
        FontShadowBlackColorBlue,
        FontShadowBlackColorAlpha,
        FontShadowWhiteColorRed,
        FontShadowWhiteColorGreen,
        FontShadowWhiteColorBlue,
        FontShadowWhiteColorAlpha,
    }

    public enum WindowKind
    {
        Around = 0,
        Horizontal = 1,
        HorizontalNoContent = 2
    }

    public enum WindowFrameTexFlip : byte
    {
        None = 0,
        FlipH = 1,
        FlipV = 2,
        Rotate90 = 3,
        Rotate180 = 4,
        Rotate270 = 5
    }

    public enum GfxAlphaFunction : byte
    {
        Never = 0,
        Less = 1,
        LessOrEqual = 2,
        Equal = 3,
        NotEqual = 4,
        GreaterOrEqual = 5,
        Greater = 6,
        Always = 7,
    }

    public enum TevMode : byte
    {
        Replace,
        Modulate,
        Add,
        AddSigned,
        Interpolate,
        Subtract,
        AddMultiplicate,
        MultiplcateAdd,
        Overlay,
        Indirect,
        BlendIndirect,
        EachIndirect,
    }



    public enum TevScale
    {
        Scale1,
        Scale2,
        Scale4
    }

    public enum TevSource
    {
        Tex0 = 0,
        Tex1 = 1,
        Tex2 = 2,
        Tex3 = 3,
        Constant = 4,
        Primary = 5,
        Previous = 6,
        Register = 7
    }
    public enum TevColorOp
    {
        RGB = 0,
        InvRGB = 1,
        Alpha = 2,
        InvAlpha = 3,
        RRR = 4,
        InvRRR = 5,
        GGG = 6,
        InvGGG = 7,
        BBB = 8,
        InvBBB = 9
    }
    public enum TevAlphaOp
    {
        Alpha = 0,
        InvAlpha = 1,
        R = 2,
        InvR = 3,
        G = 4,
        InvG = 5,
        B = 6,
        InvB = 7
    }

    public interface IPicturePane
    {
        ushort MaterialIndex { get; set; }

        BxlytMaterial Material { get; set; }

        TexCoord[] TexCoords { get; set; }

        STColor8 ColorTopLeft { get; set; }
        STColor8 ColorTopRight { get; set; }
        STColor8 ColorBottomLeft { get; set; }
        STColor8 ColorBottomRight { get; set; }

        void CopyMaterial();
    }

    public interface IBoundryPane
    {

    }

    public interface ITextPane
    {
        ushort MaterialIndex { get; set; }

        string Text { get; set; }
        OriginX HorizontalAlignment { get; set; }
        OriginY VerticalAlignment { get; set; }

        List<string> GetFonts { get; }

        ushort TextLength { get; set; }
        ushort MaxTextLength { get; set; }
        ushort RestrictedLength { get; }

        BxlytMaterial Material { get; set; }
        Toolbox.Library.Rendering.RenderableTex RenderableFont { get; set; }

        byte TextAlignment { get; set; }
        LineAlign LineAlignment { get; set; }

        float ItalicTilt { get; set; }

        STColor8 FontTopColor { get; set; }
        STColor8 FontBottomColor { get; set; }
        Vector2F FontSize { get; set; }

        float CharacterSpace { get; set; }
        float LineSpace { get; set; }

        Vector2F ShadowXY { get; set; }
        Vector2F ShadowXYSize { get; set; }

        STColor8 ShadowForeColor { get; set; }
        STColor8 ShadowBackColor { get; set; }

        float ShadowItalic { get; set; }

        string TextBoxName { get; set; }

        bool PerCharTransform { get; set; }
        bool RestrictedTextLengthEnabled { get; set; }
        bool ShadowEnabled { get; set; }
        string FontName { get; set; }
        ushort FontIndex { get; set; }

        void CopyMaterial();
    }

    public interface IPartPane
    {
        string LayoutFileName { get; set; }
    }

    public interface IWindowPane
    {
        System.Drawing.Color[] GetVertexColors();

        void ReloadFrames();
        bool UseOneMaterialForAll { get; set; }
        bool UseVertexColorForAll { get; set; }
        WindowKind WindowKind { get; set; }
        bool NotDrawnContent { get; set; }
        ushort StretchLeft { get; set; }
        ushort StretchRight { get; set; }
        ushort StretchTop { get; set; }
        ushort StretchBottm { get; set; }
        ushort FrameElementLeft { get; set; }
        ushort FrameElementRight { get; set; }
        ushort FrameElementTop { get; set; }
        ushort FrameElementBottm { get; set; }
        byte FrameCount { get; set; }

        BxlytWindowContent Content { get; set; }

        [Browsable(false)]
        List<BxlytWindowFrame> WindowFrames { get; set; }

        void CopyWindows();
    }

    public class BXLAN
    {
        public BxlanHeader BxlanHeader;
    }

    public class BxlytWindowContent : ICloneable
    {
        public STColor8 ColorTopLeft { get; set; }
        public STColor8 ColorTopRight { get; set; }
        public STColor8 ColorBottomLeft { get; set; }
        public STColor8 ColorBottomRight { get; set; }

        public ushort MaterialIndex { get; set; }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public virtual BxlytMaterial Material { get; set; }

        public List<TexCoord> TexCoords = new List<TexCoord>();

        private BxlytHeader LayoutFile;

        public object Clone()
        {
            return this.MemberwiseClone();
        }

        public BxlytWindowContent(BxlytHeader header, string name)
        {
            LayoutFile = header;
            ColorTopLeft = STColor8.White;
            ColorTopRight = STColor8.White;
            ColorBottomLeft = STColor8.White;
            ColorBottomRight = STColor8.White;

            TexCoords.Add(new TexCoord());

            //Add new material
            Material = header.CreateNewMaterial($"{name}_C");
            MaterialIndex = (ushort)header.AddMaterial(Material);
        }

        public BxlytWindowContent(FileReader reader, BxlytHeader header)
        {
            LayoutFile = header;

            ColorTopLeft = reader.ReadColor8RGBA();
            ColorTopRight = reader.ReadColor8RGBA();
            ColorBottomLeft = reader.ReadColor8RGBA();
            ColorBottomRight = reader.ReadColor8RGBA();
            MaterialIndex = reader.ReadUInt16();
            byte UVCount = reader.ReadByte();
            reader.ReadByte(); //padding

            for (int i = 0; i < UVCount; i++)
                TexCoords.Add(new TexCoord()
                {
                    TopLeft = reader.ReadVec2SY(),
                    TopRight = reader.ReadVec2SY(),
                    BottomLeft = reader.ReadVec2SY(),
                    BottomRight = reader.ReadVec2SY(),
                });

            Material = LayoutFile.GetMaterial(MaterialIndex);
        }

        public void Write(FileWriter writer)
        {
            writer.Write(ColorTopLeft);
            writer.Write(ColorTopRight);
            writer.Write(ColorBottomLeft);
            writer.Write(ColorBottomRight);
            writer.Write(MaterialIndex);
            writer.Write((byte)TexCoords.Count);
            writer.Write((byte)0);
            foreach (var texCoord in TexCoords)
            {
                writer.Write(texCoord.TopLeft);
                writer.Write(texCoord.TopRight);
                writer.Write(texCoord.BottomLeft);
                writer.Write(texCoord.BottomRight);
            }
        }
    }

    public class BxlytWindowFrame : ICloneable
    {
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public BxlytMaterial Material { get; set; }

        public ushort MaterialIndex;
        public WindowFrameTexFlip TextureFlip { get; set; }

        public object Clone()
        {
            return this.MemberwiseClone();
        }

        public BxlytWindowFrame(BxlytHeader header, string materialName)
        {
            TextureFlip = WindowFrameTexFlip.None;

            var mat = header.CreateNewMaterial(materialName);
            MaterialIndex = (ushort)header.AddMaterial(mat);
            Material = mat;
        }

        public BxlytWindowFrame(FileReader reader, BxlytHeader header)
        {
            MaterialIndex = reader.ReadUInt16();
            TextureFlip = (WindowFrameTexFlip)reader.ReadByte();
            reader.ReadByte(); //padding

            Material = header.GetMaterial(MaterialIndex);
        }

        public void Write(FileWriter writer)
        {
            writer.Write(MaterialIndex);
            writer.Write(TextureFlip, false);
            writer.Write((byte)0);
        }
    }

    public class TexCoord
    {
        public Vector2F TopLeft { get; set; }
        public Vector2F TopRight { get; set; }
        public Vector2F BottomLeft { get; set; }
        public Vector2F BottomRight { get; set; }

        public TexCoord()
        {
            TopLeft = new Vector2F(0, 0);
            TopRight = new Vector2F(1, 0);
            BottomLeft = new Vector2F(0, 1);
            BottomRight = new Vector2F(1, 1);
        }
    }

    public class LayoutHeader : IDisposable
    {
        [Browsable(false)]
        public TextureManager TextureManager = new TextureManager();

        public PartsManager PartsManager = new PartsManager();

        [Browsable(false)]
        public string FileName
        {
            get { return FileInfo.FileName; }
        }

        [DisplayName("Use Big Endian"), CategoryAttribute("File Settings")]
        public bool IsBigEndian { get; set; }

        [Browsable(false)]
        internal IFileFormat FileInfo;

        [Browsable(false)]
        internal uint Version;

        [DisplayName("Version"), CategoryAttribute("File Settings")]
        public string VersionFull
        {
            get
            {
                return $"{VersionMajor},{VersionMinor},{VersionMicro},{VersionMicro2}";
            }
        }

        [RefreshProperties(RefreshProperties.All)]
        public uint VersionMajor { get; set; }
        [RefreshProperties(RefreshProperties.All)]
        public uint VersionMinor { get; set; }
        [RefreshProperties(RefreshProperties.All)]
        public uint VersionMicro { get; set; }
        [RefreshProperties(RefreshProperties.All)]
        public uint VersionMicro2 { get; set; }

        internal void SetVersionInfo()
        {
            VersionMajor = Version >> 24;
            VersionMinor = Version >> 16 & 0xFF;
            VersionMicro = Version >> 8 & 0xFF;
            VersionMicro2 = Version & 0xFF;
        }

        internal uint SaveVersion()
        {
            return VersionMajor << 24 | VersionMinor << 16 | VersionMicro << 8 | VersionMicro2;
        }

        public static void WriteSection(FileWriter writer, string magic, SectionCommon section, Action WriteMethod = null)
        {
            long startPos = writer.Position;
            writer.WriteSignature(magic);
            writer.Write(uint.MaxValue);
            WriteMethod?.Invoke();
            writer.Align(4);

            long endPos = writer.Position;

            using (writer.TemporarySeek(startPos + 4, System.IO.SeekOrigin.Begin))
            {
                writer.Write((uint)(endPos - startPos));
            }
        }

        public void Dispose()
        {
            PartsManager?.Dispose();
            TextureManager?.Dispose();
            FileInfo.Unload();
        }
    }

    public class BxlanHeader : LayoutHeader
    {
        public BxlanPAT1 AnimationTag;
        public BxlanPAI1 AnimationInfo;

        public static bool ContainsEntry(byte[] bxlanFile, string[] EntryNames)
        {
            using (var reader = new FileReader(new System.IO.MemoryStream(bxlanFile)))
            {
                reader.SetByteOrder(true);

                reader.ReadUInt32(); //magic
                ushort byteOrderMark = reader.ReadUInt16();
                reader.CheckByteOrderMark(byteOrderMark);
                ushort HeaderSize = reader.ReadUInt16();
                uint Version = reader.ReadUInt32();
                uint FileSize = reader.ReadUInt32();
                ushort sectionCount = reader.ReadUInt16();
                reader.ReadUInt16(); //Padding

                reader.SeekBegin(HeaderSize);
                for (int i = 0; i < sectionCount; i++)
                {
                    long pos = reader.Position;
                    string Signature = reader.ReadString(4, Encoding.ASCII);
                    uint SectionSize = reader.ReadUInt32();
                    if (Signature == "pai1")
                    {
                        reader.ReadUInt16(); //FrameSize
                        reader.ReadBoolean(); //Loop
                        reader.ReadByte(); //padding
                        var numTextures = reader.ReadUInt16();
                        var numEntries = reader.ReadUInt16();
                        var entryOffsetTbl = reader.ReadUInt32();

                        reader.SeekBegin(pos + entryOffsetTbl);
                        var entryOffsets = reader.ReadUInt32s(numEntries);
                        for (int e = 0; e < numEntries; e++)
                        {
                            reader.SeekBegin(pos + entryOffsets[e]);
                            string name = reader.ReadString(28, true);
                            if (EntryNames.Contains(name))
                                return true;
                        }
                    }

                    reader.SeekBegin(pos + SectionSize);
                }
            }
            return false;
        }

        public BxlanPaiEntry TryGetTag(string name)
        {
            for (int i = 0; i < AnimationInfo.Entries?.Count; i++)
            {
                if (AnimationInfo.Entries[i].Name == name)
                    return AnimationInfo.Entries[i];
            }
            return null;
        }

        public bool ContainsEntry(string name)
        {
            for (int i = 0; i < AnimationInfo.Entries?.Count; i++) {
                if (AnimationInfo.Entries[i].Name == name)
                    return true;
            }
            return false;
        }

        public virtual void Read(FileReader reader, BXLAN header)
        {

        }

        public virtual void Write(FileWriter writer)
        {

        }

        public LytAnimation GetGenericAnimation()
        {
            return animation;
        }

        private LytAnimation animation;
        public LytAnimation ToGenericAnimation(BxlytHeader parentLayout)
        {
            if (animation == null)
                animation = new LytAnimation(this, parentLayout);

            animation.UpdateLayout(parentLayout);
            return animation;
        }
    }

    public class BxlanPAT1 : SectionCommon
    {
        [DisplayName("Name"), CategoryAttribute("Animation")]
        public string Name { get; set; }

        [DisplayName("Groups"), CategoryAttribute("Animation")]
        public List<string> Groups { get; set; }

        [DisplayName("Start"), CategoryAttribute("Frames")]
        public short StartFrame { get; set; }

        [DisplayName("End"), CategoryAttribute("Frames")]
        public short EndFrame { get; set; }

        [DisplayName("Animation Order"), CategoryAttribute("Parameters")]
        public ushort AnimationOrder { get; set; }

        [DisplayName("Child Binding"), CategoryAttribute("Parameters")]
        public bool ChildBinding { get; set; }
    }

    public class BxlanPAI1 : SectionCommon
    {
        public ushort FrameSize;
        public bool Loop;

        public bool ContainsEntry(string name)
        {
            return Entries.Any(x => x.Name == name);
        }

        public virtual BxlanPaiEntry AddEntry(string name, byte target)
        {
            return new BxlanPaiEntry();
        }

        public List<string> Textures { get; set; } = new List<string>();
        public List<BxlanPaiEntry> Entries = new List<BxlanPaiEntry>();
    }

    public class BxlanPaiEntry
    {
        public virtual BxlanPaiTag AddEntry(string tag)
        { 
            return new BxlanPaiTag();
        }

        [DisplayName("Name"), CategoryAttribute("Animation")]
        public string Name { get; set; }

        [DisplayName("Target"), CategoryAttribute("Animation")]
        public AnimationTarget Target { get; set; }

        public List<BxlanPaiTag> Tags = new List<BxlanPaiTag>();
    }

    public class BxlanPaiTag
    {
        public BxlanPaiTag()
        {

        }

        public BxlanPaiTag(string tag)
        {
            Tag = tag;
        }

        public BxlanPaiTagEntry CreateTarget(object TargetType, byte interpolationType)
        {
            byte target = (byte)TargetType;
            string tagType = Tag.Remove(0, 1);
            switch (tagType)
            {
                case "LPA":
                    return new LPATagEntry(target, interpolationType);
                case "LTS":
                    return new LTSTagEntry(target, interpolationType);
                case "LVI":
                    return new LVITagEntry(target, interpolationType);
                case "LVC":
                    return new LVCTagEntry(target, interpolationType);
                case "LMC":
                    return new LMCTagEntry(target, interpolationType);
                case "LTP":
                    return new LTPTagEntry(target, interpolationType);
                default:
                    return new BxlanPaiTagEntry(target, interpolationType);
            }
        }

        public List<BxlanPaiTagEntry> Entries = new List<BxlanPaiTagEntry>();

        public string Tag;

        public string Type
        {
            get { return TypeDefine.ContainsKey(Tag) ? TypeDefine[Tag] : Tag; }
        }

        public static Dictionary<string, string> CafeTypeDefine = new Dictionary<string, string>()
        {
                {"FLPA","PaneSRT" },
                {"FLVI","Visibility" },
                {"FLTS","TextureSRT" },
                {"FLVC","VertexColor" },
                {"FLMC","MaterialColor" },
                {"FLTP","TexturePattern" },
                {"FLIM","IndTextureSRT" },
                {"FLAC","AlphaTest" },
                {"FLCT","FontShadow" },
                {"FLCC","PerCharacterTransformCurve" },
        };

        public static Dictionary<string, string> RevTypeDefine = new Dictionary<string, string>()
        {
                {"RLPA","PaneSRT" },
                {"RLVI","Visibility" },
                {"RLTS","TextureSRT" },
                {"RLVC","VertexColor" },
                {"RLMC","MaterialColor" },
                {"RLTP","TexturePattern" },
                {"RLIM","IndTextureSRT" },
                {"RLAC","AlphaTest" },
                {"RLCT","FontShadow" },
                {"RLCC","PerCharacterTransformCurve" },
        };

        public static Dictionary<string, string> TypeDefine = new Dictionary<string, string>()
            {
                {"FLPA","PaneSRT" },
                {"FLVI","Visibility" },
                {"FLTS","TextureSRT" },
                {"FLVC","VertexColor" },
                {"FLMC","MaterialColor" },
                {"FLTP","TexturePattern" },
                {"FLIM","IndTextureSRT" },
                {"FLAC","AlphaTest" },
                {"FLCT","FontShadow" },
                {"FLCC","PerCharacterTransformCurve" },

                {"RLPA","PaneSRT" },
                {"RLVI","Visibility" },
                {"RLTS","TextureSRT" },
                {"RLVC","VertexColor" },
                {"RLMC","MaterialColor" },
                {"RLTP","TexturePattern" },
                {"RLIM","IndTextureSRT" },
                {"RLAC","AlphaTest" },
                {"RLCT","FontShadow" },
                {"RLCC","PerCharacterTransformCurve" },

                {"CLPA","PaneSRT" },
                {"CLVI","Visibility" },
                {"CLTS","TextureSRT" },
                {"CLVC","VertexColor" },
                {"CLMC","MaterialColor" },
                {"CLTP","TexturePattern" },
                {"CLIM","IndTextureSRT" },
                {"CLAC","AlphaTest" },
                {"CLCT","FontShadow" },
                {"CLCC","PerCharacterTransformCurve" },
            };
    }


    public class LPATagEntry : BxlanPaiTagEntry
    {
        public override string TargetName => Target.ToString();
        [DisplayName("Target"), CategoryAttribute("Tag")]
        public LPATarget Target
        {
            get { return (LPATarget)AnimationTarget; }
            set { AnimationTarget = (byte)value; }
        }

        public LPATagEntry(byte target, byte curveType) : base(target, curveType) { }
        public LPATagEntry(FileReader reader, BxlanHeader header) : base(reader, header) { }
    }

    public class LTSTagEntry : BxlanPaiTagEntry
    {
        public override string TargetName => Target.ToString();
        [DisplayName("Target"), CategoryAttribute("Tag")]
        public LTSTarget Target
        {
            get { return (LTSTarget)AnimationTarget; }
            set { AnimationTarget = (byte)value; }
        }

        public LTSTagEntry(byte target, byte curveType) : base(target, curveType) { }
        public LTSTagEntry(FileReader reader, BxlanHeader header) : base(reader, header) { }
    }

    public class LVITagEntry : BxlanPaiTagEntry
    {
        public override string TargetName => Target.ToString();
        [DisplayName("Target"), CategoryAttribute("Tag")]
        public LVITarget Target
        {
            get { return (LVITarget)AnimationTarget; }
            set { AnimationTarget = (byte)value; }
        }

        public LVITagEntry(byte target, byte curveType) : base(target, curveType) { }
        public LVITagEntry(FileReader reader, BxlanHeader header) : base(reader, header) { }
    }

    public class LVCTagEntry : BxlanPaiTagEntry
    {
        public override string TargetName => Target.ToString();
        [DisplayName("Target"), CategoryAttribute("Tag")]
        public LVCTarget Target
        {
            get { return (LVCTarget)AnimationTarget; }
            set { AnimationTarget = (byte)value; }
        }

        public LVCTagEntry(byte target, byte curveType) : base(target, curveType) { }
        public LVCTagEntry(FileReader reader, BxlanHeader header) : base(reader, header) { }
    }

    public class LMCTagEntry : BxlanPaiTagEntry
    {
        public override string TargetName => Target.ToString();
        [DisplayName("Target"), CategoryAttribute("Tag")]
        public LMCTarget Target
        {
            get { return (LMCTarget)AnimationTarget; }
            set { AnimationTarget = (byte)value; }
        }

        public LMCTagEntry(byte target, byte curveType) : base(target, curveType) { }
        public LMCTagEntry(FileReader reader, BxlanHeader header) : base(reader, header) { }
    }

    public class LTPTagEntry : BxlanPaiTagEntry
    {
        public override string TargetName => Target.ToString();
        [DisplayName("Target"), CategoryAttribute("Tag")]
        public LTPTarget Target
        {
            get { return (LTPTarget)AnimationTarget; }
            set { AnimationTarget = (byte)value; }
        }

        public LTPTagEntry(byte target, byte curveType) : base(target, curveType) { }
        public LTPTagEntry(FileReader reader, BxlanHeader header) : base(reader, header) { }
    }

    public class BxlanPaiTagEntry
    {
        [Browsable(false)]
        public virtual string TargetName
        {
            get { return AnimationTarget.ToString(); }
        }

        public byte AnimationTarget;

        [DisplayName("Index"), CategoryAttribute("Tag")]
        public byte Index { get; set; }

        [DisplayName("Curve Type"), CategoryAttribute("Tag")]
        public CurveType CurveType { get; set; }

        public List<KeyFrame> KeyFrames = new List<KeyFrame>();

        public BxlanPaiTagEntry(byte target, byte curveType)
        {
            AnimationTarget = target;
            CurveType = (CurveType)curveType;
        }

        public BxlanPaiTagEntry(FileReader reader, BxlanHeader header)
        {
            long startPos = reader.Position;
            Index = reader.ReadByte();
            AnimationTarget = reader.ReadByte();
            CurveType = reader.ReadEnum<CurveType>(true);
            reader.ReadByte(); //padding
            var KeyFrameCount = reader.ReadUInt16();
            reader.ReadUInt16(); //Padding
            uint keyFrameOffset = reader.ReadUInt32();

            reader.SeekBegin(startPos + keyFrameOffset);
            for (int i = 0; i < KeyFrameCount; i++)
                KeyFrames.Add(new KeyFrame(reader, CurveType));
        }

        public void Write(FileWriter writer, LayoutHeader header)
        {
            long startPos = writer.Position;

            writer.Write(Index);
            writer.Write(AnimationTarget);
            writer.Write(CurveType, true);
            writer.Write((byte)0); //padding
            writer.Write((ushort)KeyFrames.Count);
            writer.Write((ushort)0); //padding
            writer.Write(0); //key offset

            if (KeyFrames.Count > 0)
            {
                writer.WriteUint32Offset(startPos + 8, startPos);
                for (int i = 0; i < KeyFrames.Count; i++)
                    KeyFrames[i].Write(writer, CurveType);
            }
        }
    }

    public enum BorderType : byte
    {
        Standard = 0,
        DeleteBorder = 1,
        RenderTwoCycles = 2,
    };

    public enum LineAlign : byte
    {
        Unspecified = 0,
        Left = 1,
        Center = 2,
        Right = 3,
    };

    public enum CurveType : byte
    {
        Constant,
        Step,
        Hermite,
    }

    public class KeyFrame
    {
        [DisplayName("Slope"), CategoryAttribute("Key Frame")]
        public float Slope { get; set; }
        [DisplayName("Frame"), CategoryAttribute("Key Frame")]
        public float Frame { get; set; }
        [DisplayName("Value"), CategoryAttribute("Key Frame")]
        public float Value { get; set; }

        public KeyFrame(float frame)
        {
            Frame = frame;
            Value = 0;
            Slope = 0;
        }

        public KeyFrame(FileReader reader, CurveType curveType)
        {
            switch (curveType)
            {
                case CurveType.Hermite:
                    Frame = reader.ReadSingle();
                    Value = reader.ReadSingle();
                    Slope = reader.ReadSingle();
                    break;
                default:
                    Frame = reader.ReadSingle();
                    Value = reader.ReadInt16();
                    reader.ReadInt16(); //padding
                    break;
            }
        }

        public void Write(FileWriter writer, CurveType curveType)
        {
            switch (curveType)
            {
                case CurveType.Hermite:
                    writer.Write(Frame);
                    writer.Write(Value);
                    writer.Write(Slope);
                    break;
                default:
                    writer.Write(Frame);
                    writer.Write((ushort)Value);
                    writer.Write((ushort)0);
                    break;
            }
        }
    }

    public class BxlytHeader : LayoutHeader
    {
        [Browsable(false)]
        public System.Windows.Forms.TreeNode MaterialFolder;

        [Browsable(false)]
        public System.Windows.Forms.TreeNode TextureFolder;

        [Browsable(false)]
        public System.Windows.Forms.TreeNode FontFolder;

        [Browsable(false)]
        public Dictionary<string, BasePane> PaneLookup = new Dictionary<string, BasePane>();

        [Browsable(false)]
        public BasePane RootPane { get; set; }

        [Browsable(false)]
        public BasePane RootGroup { get; set; }

        [Browsable(false)]
        public virtual Dictionary<string, STGenericTexture> GetTextures { get; }

        [Browsable(false)]
        public virtual List<string> Textures { get; } 

        [Browsable(false)]
        public virtual List<string> Fonts { get; }

        [Browsable(false)]
        public virtual List<BxlytMaterial> GetMaterials()
        {
            return new List<BxlytMaterial>();
        }

        public virtual short AddMaterial(BxlytMaterial material, ushort index) { return -1; }
        public virtual short AddMaterial(BxlytMaterial material) { return -1; }
        public virtual List<int> AddMaterial(List<BxlytMaterial> materials) { return new List<int>(); }

        public virtual void TryRemoveMaterial(BxlytMaterial material) { }
        public virtual void TryRemoveMaterial(List<BxlytMaterial> materials) { }

        public virtual BxlytMaterial CreateNewMaterial(string name)
        {
            return new BxlytMaterial();
        }

        public virtual BxlytMaterial GetMaterial(ushort index)
        {
            return new BxlytMaterial();
        }

        public void RemoveTextureReferences(string texture)
        {
            foreach (var mat in GetMaterials())
                mat.TryRemoveTexture(texture);
        }

        public BxlytMaterial SearchMaterial(string name)
        {
            var materials = GetMaterials();
            for (int i = 0; i < materials.Count; i++)
            {
                if (materials[i].Name == name)
                    return materials[i];
            }
            return null;
        }

        public virtual int AddFont(string name)
        {
            return -1;
        }

        public virtual void RemoveTexture(string name)
        {

        }

        public virtual int AddTexture(string name)
        {
            return -1;
        }

        public void AddPaneToTable(BasePane pane)
        {
            if (!PaneLookup.ContainsKey(pane.Name))
                PaneLookup.Add(pane.Name, pane);
        }

        public void AddPane(BasePane pane, string parent)
        {
            if (PaneLookup.ContainsKey(parent))
                AddPane(pane, PaneLookup[parent]);
            else
                throw new Exception($"Failed to find parent pane! [{parent}]");
        }

        public void AddPane(BasePane pane, BasePane parent)
        {
            if (!PaneLookup.ContainsKey(pane.Name))
                PaneLookup.Add(pane.Name, pane);

            pane.Parent = parent;
            parent.Childern.Add(pane);
            parent.NodeWrapper.Nodes.Add(pane.NodeWrapper);
        }

        public void RemovePanes(List<BasePane> panes, BasePane rootPane)
        {
            Console.WriteLine("RemovePanes num " + panes.Count);

            for (int i = 0; i < panes.Count; i++)
            {
                Console.WriteLine($"RemovePanes {panes[i].Name} {panes[i]}");

                //We need to remove any materials that the material referenced
                if (panes[i] is IPicturePane)
                    TryRemoveMaterial(((IPicturePane)panes[i]).Material);
                if (panes[i] is ITextPane)
                    TryRemoveMaterial(((ITextPane)panes[i]).Material);
                if (panes[i] is IWindowPane)
                {
                    var wnd = panes[i] as IWindowPane;

                    List<BxlytMaterial> materials = new List<BxlytMaterial>();
                    var matC = wnd.Content.Material;
                    materials.Add(matC);
                    foreach (var windowFrame in wnd.WindowFrames)
                        materials.Add(windowFrame.Material);

                    TryRemoveMaterial(materials);
                    materials.Clear();
                }
            }

            List<BasePane> topMostPanes = new List<BasePane>();
            GetTopMostPanes(panes, topMostPanes, rootPane);

            foreach (var pane in topMostPanes)
            {
                pane.Parent.NodeWrapper.Nodes.Remove(pane.NodeWrapper);
                pane.Parent.Childern.Remove(pane);
            }

            for (int i = 0; i < panes.Count; i++)
            {
                if (PaneLookup.ContainsKey(panes[i].Name))
                    PaneLookup.Remove(panes[i].Name);
            }
        }

        //Loop through each pane in the heiarchy until it finds the first set of panes
        //The topmost panes are only going to be removed for adding with redo to be easier
        private void GetTopMostPanes(List<BasePane> panes, List<BasePane> topMost, BasePane root)
        {
            foreach (var child in root.Childern)
            {
                if (panes.Contains(child))
                    topMost.Add(child);
            }

            if (topMost.Count == 0)
            {
                foreach (var child in root.Childern)
                    GetTopMostPanes(panes, topMost, child);
            }
        }
    }

    public class BxlytMaterial 
    {
        //Setup some enable booleans
        //These determine wether to switch to default values or not
        //While i could null out the instances of each, it's best to keep those intact
        //incase the settings get renabled, which keeps the previous data

        [Browsable(false)]
        public bool EnableAlphaCompare { get; set; }

        [Browsable(false)]
        public bool EnableBlend { get; set; }

        [Browsable(false)]
        public bool EnableBlendLogic { get; set; }

        [Browsable(false)]
        public bool EnableIndParams { get; set; }

        [Browsable(false)]
        public bool EnableFontShadowParams { get; set; }

        [Browsable(false)]
        public TreeNodeCustom NodeWrapper;

        public bool TryRemoveTexture(string name)
        {
            int removeIndex = -1;
            for (int i = 0; i < TextureMaps?.Length; i++)
            {
                if (TextureMaps[i].Name == name)
                    removeIndex = i;
            }

            if (removeIndex != -1)
            {
                TextureMaps = TextureMaps.RemoveAt(removeIndex);
                return true;
            }

            return false;
        }

        public virtual BxlytMaterial Clone()
        {
            return (BxlytMaterial)this.MemberwiseClone();
        }

        public void RemoveNodeWrapper()
        {
            if (NodeWrapper != null && NodeWrapper.Parent != null)
            {
                var parent = NodeWrapper.Parent;
                parent.Nodes.Remove(NodeWrapper);
            }
        }

        public virtual void AddTexture(string texture)
        {
            int index = ParentLayout.AddTexture(texture);
            BxlytTextureRef textureRef = new BxlytTextureRef();
            textureRef.ID = (short)index;
            textureRef.Name = texture;
            TextureMaps = TextureMaps.AddToArray(textureRef);
            TextureTransforms = TextureTransforms.AddToArray(new BxlytTextureTransform());
        }

        [DisplayName("Texture Transforms"), CategoryAttribute("Texture")]
        public BxlytTextureTransform[] TextureTransforms { get; set; }

        [Browsable(false)]
        public MaterialAnimController animController = new MaterialAnimController();

        [DisplayName("Name"), CategoryAttribute("General")]
        public virtual string Name { get; set; }

        [Browsable(false)]
        public BxlytHeader ParentLayout;

        public void SetName(string oldName, string newName)
        {
            if (Name == null) return;

            Name = Name.Replace(oldName, newName);
        }

        [DisplayName("Black Color"), CategoryAttribute("Color")]
        public virtual STColor8 WhiteColor { get; set; } = STColor8.White;

        [DisplayName("White Color"), CategoryAttribute("Color")]
        public virtual STColor8 BlackColor { get; set; } = STColor8.Black;

        [DisplayName("Blend Mode"), CategoryAttribute("Blend")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public BxlytBlendMode BlendMode { get; set; }

        [DisplayName("Blend Mode Logic"), CategoryAttribute("Blend")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public BxlytBlendMode BlendModeLogic { get; set; }

        [DisplayName("Alpha Compare"), CategoryAttribute("Alpha")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public BxlytAlphaCompare AlphaCompare { get; set; }

        [DisplayName("Thresholding Alpha Interpolation"), CategoryAttribute("Alpha")]
        public virtual bool ThresholdingAlphaInterpolation { get; set; }

        [DisplayName("Texture Maps"), CategoryAttribute("Texture")]
        public BxlytTextureRef[] TextureMaps { get; set; }

        [DisplayName("Tev Stages"), CategoryAttribute("Tev")]
        public BxlytTevStage[] TevStages { get; set; }
    }

    public class BxlytTevStage
    {
        public TevMode ColorMode { get; set; }
        public TevMode AlphaMode { get; set; }
    }

    public class MaterialAnimController
    {
        public Dictionary<LMCTarget, float> MaterialColors = new Dictionary<LMCTarget, float>();
        public Dictionary<LTPTarget, string> TexturePatterns = new Dictionary<LTPTarget, string>();
        public Dictionary<LTSTarget, float> TextureSRTS = new Dictionary<LTSTarget, float>();
        public Dictionary<LIMTarget, float> IndTextureSRTS = new Dictionary<LIMTarget, float>();

        public void ResetAnim()
        {
            MaterialColors.Clear();
            TexturePatterns.Clear();
            TextureSRTS.Clear();
            IndTextureSRTS.Clear();
        }
    }

    public class BxlytTextureTransform
    {
        public Vector2F Translate { get; set; }
        public float Rotate { get; set; }
        public Vector2F Scale { get; set; }

        public BxlytTextureTransform()
        {
            Translate = new Vector2F(0,0);
            Scale = new Vector2F(1, 1);
            Rotate = 0;
        }
    }

    public class BxlytIndTextureTransform
    {
        public float Rotation { get; set; }
        public float ScaleX { get; set; }
        public float ScaleY { get; set; }
    }

    public class SectionCommon
    {
        [Browsable(false)]
        public virtual string Signature { get; }

        internal uint SectionSize { get; set; }
        internal long StartPosition { get; set; }
        internal byte[] Data { get; set; }

        public SectionCommon()
        {

        }

        public SectionCommon(string signature)
        {
            Signature = signature;
        }

        public virtual void Write(FileWriter writer, LayoutHeader header)
        {
            if (Data != null)
                writer.Write(Data);
        }
    }

    public class CustomRectangle
    {
        public int LeftPoint;
        public int RightPoint;
        public int TopPoint;
        public int BottomPoint;

        public OpenTK.Vector2 TopLeftPoint;
        public OpenTK.Vector2 TopRightPoint;
        public OpenTK.Vector2 BottomLeftPoint;
        public OpenTK.Vector2 BottomRightPoint;

        public CustomRectangle(OpenTK.Vector2 topLeft, OpenTK.Vector2 topRight,
            OpenTK.Vector2 bottomLeft, OpenTK.Vector2 bottomRight)
        {
            TopLeftPoint = topLeft;
            TopRightPoint = topRight;
            BottomLeftPoint = bottomLeft;
            BottomRightPoint = bottomRight;

            LeftPoint = (int)topLeft.X;
            RightPoint = (int)topRight.X;
            TopPoint = (int)topLeft.Y;
            BottomPoint = (int)bottomRight.Y;
        }

        public CustomRectangle(int left, int right, int top, int bottom)
        {
            LeftPoint = left;
            RightPoint = right;
            TopPoint = top;
            BottomPoint = bottom;

            TopLeftPoint = new OpenTK.Vector2(left,top);
            TopRightPoint = new OpenTK.Vector2(right, top);
            BottomLeftPoint = new OpenTK.Vector2(left, bottom);
            BottomRightPoint = new OpenTK.Vector2(right, bottom);
        }

        public CustomRectangle RotateZ(float rotate)
        {
            var topLeft = RotateZPoint(TopLeftPoint.X, TopLeftPoint.Y, rotate).Xy;
            var topRight = RotateZPoint(TopRightPoint.X, TopRightPoint.Y, rotate).Xy;
            var bottomLeft = RotateZPoint(BottomLeftPoint.X, BottomLeftPoint.Y, rotate).Xy;
            var bottomRight = RotateZPoint(BottomRightPoint.X, BottomRightPoint.Y, rotate).Xy;

            return new CustomRectangle(
                topLeft,
                topRight,
                bottomLeft,
                bottomRight);
        }

        private OpenTK.Vector3 RotateZPoint(float p1, float p2,  float rotate)
        {
            var rotatioZ = OpenTK.Matrix4.CreateRotationZ(OpenTK.MathHelper.DegreesToRadians(rotate));
            return OpenTK.Vector3.TransformPosition(new OpenTK.Vector3(p1, p2, 0), rotatioZ);
        }

        public CustomRectangle GetTransformedRectangle(BasePane parent, Vector3F Transform, Vector3F Rotate, Vector2F scale)
        {
            var sca = new OpenTK.Vector2(scale.X, scale.Y);
            var pos = new OpenTK.Vector2(Transform.X, Transform.Y);

            var rect = this.RotateZ(Rotate.Z);
            rect = new CustomRectangle(
                (rect.TopLeftPoint * sca) + pos,
                (rect.TopRightPoint * sca) + pos,
                (rect.BottomLeftPoint * sca) + pos,
                (rect.BottomRightPoint * sca) + pos);

            if (parent != null)
                return parent.TransformParent(rect);
            else
                return rect;
        }

        public int Width
        {
            get { return LeftPoint - RightPoint; }
        }

        public int Height
        {
            get { return TopPoint - BottomPoint; }
        }
    }

    public class LayoutControlDocked : Toolbox.Library.Forms.STUserControl
    {
        public DockContent DockContent;

        public DockPane Pane => DockContent?.Pane;
    }

    public class LayoutDocked : DockContent
    {

    }
}