using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using RecrownedAthenaeum.Input;
using RecrownedAthenaeum.Render;
using RecrownedAthenaeum.SpecialTypes;
using RecrownedAthenaeum.UI.SkinSystem;
using RecrownedAthenaeum.UI.SkinSystem.Definitions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RecrownedAthenaeum.UI.Modular.Modules
{
    public class UIScrollable : UIModule
    {
        UIModuleGroup group;

        Color scrollBarColor;
        float opacityOfBar = 1;
        bool showingBars;
        private bool mouseWasPressed;
        private bool horizontalSelected;
        private Vector2 mouseRelativePosition;
        float shiftX, shiftY;
        int furthestX;
        int furthestY;
        UIModule furthestXModule, furthestYMod;

        bool horScrollAvailable, vertScrollAvailable;

        Rectangle horizontalScrollBarBounds, verticalScrollBarBounds;
        /// <summary>
        /// How fast the bars fade away in opacity (0 to 254) per second.
        /// </summary>
        public float barFadeSpeed = 250;

        ISpecialDrawable horizontalScrollBar, verticalScrollBar;
        ISpecialDrawable background, horizontalBarTrack, verticalBarTrack;

        private int topPadding, bottomPadding, leftPadding, rightPadding;
        public int PadTop { get { return topPadding; } set { topPadding = value; HeightOrYChange(); } }
        public int PadBottom { get { return bottomPadding; } set { bottomPadding = value; HeightOrYChange(); } }
        public int PadLeft { get { return leftPadding; } set { leftPadding = value; WidthOrXChange(); } }
        public int PadRight { get { return rightPadding; } set { rightPadding = value; WidthOrXChange(); } }

        /// <summary>
        /// The minimum bar length for the scroll bar.
        /// </summary>
        public int minimumBarLength = 16;

        public int HorizontalBarThickness { get { return horizontalScrollBarBounds.Height; } set { horizontalScrollBarBounds.Height = value; HeightOrYChange(); } }
        public int VerticalBarThickness { get { return verticalScrollBarBounds.Width; } set { verticalScrollBarBounds.Width = value; WidthOrXChange(); } }

        /// <summary>
        /// Whether or not to hide scroll bars.
        /// </summary>
        public bool HideScrollBars
        {
            get { return hideScrollBars; }
            set
            {
                hideScrollBars = value;
                WidthOrXChange(true);
                HeightOrYChange(true);
                if (!value)
                {
                    opacityOfBar = 1f;
                }
            }
        }

        bool hideScrollBars = true;

        /// <summary>
        /// Set to true to change from the normal position to the new position.
        /// </summary>
        public bool verticalBarLeftPosition, horizontalBarTopPosition;


        public override int Width
        {
            get
            {
                return base.Width;
            }
            set
            {
                base.Width = value;
                WidthOrXChange();
            }
        }

        public override int Height
        {
            get
            {
                return base.Height;
            }
            set
            {
                base.Height = value;
                HeightOrYChange();
            }
        }

        public override int X
        {
            get
            {
                return base.X;
            }
            set
            {
                WidthOrXChange(true);
                base.X = value;
            }
        }

        public override int Y
        {
            get
            {
                return base.Y;
            }
            set
            {
                HeightOrYChange(true);
                base.Y = value;
            }
        }

        public UIScrollable(ISpecialDrawable horizontalScrollBar, ISpecialDrawable verticalScrollBar, ISpecialDrawable horizontalBarTrack = null, ISpecialDrawable verticalBarTrack = null, ISpecialDrawable background = null)
        {
            this.horizontalScrollBar = horizontalScrollBar;
            this.verticalScrollBar = verticalScrollBar;
            this.horizontalBarTrack = horizontalBarTrack;
            this.verticalBarTrack = verticalBarTrack;
            this.background = background;

            group = new UIModuleGroup(new BasicScissor());
            HorizontalBarThickness = 12;
            VerticalBarThickness = 12;
        }

        public UIScrollable(ISkin skin, string definition = null) :
            this(skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().horizontalBar, true),
                skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().verticalBar, true),
                skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().horizontalBarTrack),
                skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().verticalBarTrack),
                skin.GetTextureAtlasRegion(skin.ObtainDefinition<UIScrollableSkinDefinition>().background))
        {
        }

        public override void Update(GameTime gameTime)
        {
            if (hideScrollBars)
            {
                if (!showingBars && !mouseWasPressed)
                {
                    if (opacityOfBar > 0f)
                    {
                        opacityOfBar -= (barFadeSpeed / 255f) * (float)gameTime.ElapsedGameTime.TotalSeconds;
                        scrollBarColor = color * opacityOfBar;
                    }
                }
                else
                {
                    opacityOfBar = 1f;
                    scrollBarColor = color * opacityOfBar;
                }
            }

            if (horScrollAvailable)
            {
                horizontalScrollBarBounds.X = (int)(((float)-shiftX / furthestX) * (group.Width - horizontalScrollBarBounds.Width));
            }

            if (vertScrollAvailable)
            {
                verticalScrollBarBounds.Y = (int)(((float)-shiftY / furthestY) * (group.Height - verticalScrollBarBounds.Height));
            }

            base.Update(gameTime);
        }

        public override void Draw(ConsistentSpriteBatch spriteBatch)
        {
            background?.Draw(spriteBatch, Boundaries, color, origin: origin);

            group.Draw(spriteBatch);

            if (horScrollAvailable)
            {
                horizontalScrollBar.Draw(spriteBatch, horizontalScrollBarBounds, scrollBarColor);
            }
            if (vertScrollAvailable)
            {
                horizontalScrollBar.Draw(spriteBatch, verticalScrollBarBounds, scrollBarColor);
            }
            base.Draw(spriteBatch);
        }

        public void AddModules(params UIModule[] addModules)
        {
            group.AddModules(addModules);

            for (int i = 0; i < addModules.Length; i++)
            {
                UIModule m = addModules[i];
                int mFurthestX = m.Boundaries.X + m.Boundaries.Width;
                int mFurthestY = m.Boundaries.Y + m.Boundaries.Height;
                if (mFurthestX > furthestX)
                {
                    furthestXModule = m;
                    furthestX = mFurthestX;
                }
                if (mFurthestY > furthestY)
                {
                    furthestYMod = m;
                    furthestY = mFurthestY;
                }
            }

            WidthOrXChange();
            HeightOrYChange();
        }

        public void RemoveModule(UIModule module)
        {
            group.RemoveModule(module);

            if (module == furthestXModule)
            {
                furthestX = 0;
                UIModule[] modules = group.GetCopyOfModules();
                for (int i = 0; i < modules.Length; i++)
                {
                    UIModule m = modules[i];
                    int mFurthestX = m.Boundaries.X + m.Boundaries.Width;
                    if (mFurthestX > furthestX)
                    {
                        furthestXModule = m;
                        furthestX = mFurthestX;
                    }
                }
            }

            if (module == furthestYMod)
            {
                furthestY = 0;
                UIModule[] modules = group.GetCopyOfModules();

                for (int i = 0; i < modules.Length; i++)
                {
                    UIModule m = modules[i];
                    int mFurthestY = m.Boundaries.Y + m.Boundaries.Height;
                    if (mFurthestY > furthestY)
                    {
                        furthestYMod = m;
                        furthestY = mFurthestY;
                    }
                }
            }
        }

        private void WidthOrXChange(bool onlyXChange = false)
        {
            group.X = X + leftPadding;
            if (!onlyXChange)
            {
                group.Width = Width - rightPadding - leftPadding;
                if (Width < furthestX)
                {
                    horScrollAvailable = true;
                    horizontalScrollBarBounds.Width = (int)(Width * ((float)Width / furthestX));
                    horizontalScrollBarBounds.Width = Math.Max(horizontalScrollBarBounds.Width, minimumBarLength);
                }
                else { horScrollAvailable = false; }

                verticalScrollBarBounds.X = X;
                if (!hideScrollBars)
                {
                    group.Width -= VerticalBarThickness;
                    if (!verticalBarLeftPosition)
                    {
                        verticalScrollBarBounds.X += group.Width;
                    }
                }
                else
                {
                    if (!verticalBarLeftPosition)
                    {
                        verticalScrollBarBounds.X += group.Width - verticalScrollBarBounds.Width;
                    }
                }
            }
        }

        private void HeightOrYChange(bool onlyYChange = false)
        {
            group.Y = Y + bottomPadding;
            if (!onlyYChange)
            {
                group.Height = Height - bottomPadding - topPadding;
                if (Height < furthestY)
                {
                    vertScrollAvailable = true;
                    verticalScrollBarBounds.Height = (int)(Height * ((float)Height / furthestY));
                    verticalScrollBarBounds.Height = Math.Max(verticalScrollBarBounds.Height, minimumBarLength);
                }
                else { vertScrollAvailable = false; }

                horizontalScrollBarBounds.Y = Y;
                if (!hideScrollBars)
                {
                    group.Height -= HorizontalBarThickness;
                    if (!horizontalBarTopPosition)
                    {
                        horizontalScrollBarBounds.Y += group.Height;
                    }
                    else
                    {
                        group.Y += horizontalScrollBarBounds.Height;
                    }
                }
                else
                {
                    if (!horizontalBarTopPosition)
                    {
                        horizontalScrollBarBounds.Y += group.Height - horizontalScrollBarBounds.Height;
                    }
                }
            }
        }


        public override bool KeyboardStateChanged(KeyboardState state)
        {
            if (state.IsKeyDown(Keys.Right))
            {
                shiftX -= 20;
            }

            return base.KeyboardStateChanged(state);
        }

        public override bool MouseStateChanged(MouseState state)
        {
            if (InputUtilities.MouseWithinBoundries(Boundaries))
            {
                showingBars = true;
            }
            else
            {
                showingBars = false;
            }

            if (InputUtilities.MouseWithinBoundries(horizontalScrollBarBounds))
            {
                if (state.LeftButton == ButtonState.Pressed)
                {
                    mouseWasPressed = true;
                    horizontalSelected = true;
                }
                if (!mouseWasPressed)
                {
                    mouseRelativePosition.X = state.X - horizontalScrollBarBounds.X;
                }
            }

            if (InputUtilities.MouseWithinBoundries(verticalScrollBarBounds))
            {
                if (state.LeftButton == ButtonState.Pressed)
                {
                    mouseWasPressed = true;
                    horizontalSelected = false;
                }
                if (!mouseWasPressed)
                {
                    mouseRelativePosition.Y = state.Y - verticalScrollBarBounds.Y;
                }
            }

            if (mouseWasPressed)
            {
                if (horizontalSelected)
                {
                    float latestPosition = state.X - mouseRelativePosition.X;
                    shiftX = -(latestPosition / (Width - horizontalScrollBarBounds.Width)) * furthestX;
                }
                else
                {
                    float latestPosition = state.Y - mouseRelativePosition.Y;
                    shiftY = -(latestPosition / (Height - verticalScrollBarBounds.Height)) * furthestY;
                }

                if (state.LeftButton == ButtonState.Released)
                {
                    mouseWasPressed = false;
                }
            }
            return base.MouseStateChanged(state);
        }

        private void MoveHorizontalScrollBar(int position)
        {
            shiftX = -(int)(furthestX * ((float)position / Width));
        }

        private void MoveVerticalScrollBar(int position)
        {
            shiftY = (int)(furthestY * ((float)position / Height));
        }
    }
}
