using System;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Barotrauma
{
    public class GUIButton : GUIComponent
    {
        protected GUITextBlock textBlock;
        public GUITextBlock TextBlock { get { return textBlock; } }
        protected GUIFrame frame;
        public GUIFrame Frame { get { return frame; } }

        public delegate bool OnClickedHandler(GUIButton button, object obj);
        public OnClickedHandler OnClicked;

        public delegate bool OnPressedHandler();
        public OnPressedHandler OnPressed;

        public delegate bool OnButtonDownHandler();
        public OnButtonDownHandler OnButtonDown;

        public bool CanBeSelected = true;
                
        public override bool Enabled 
        { 
            get
            {
                return enabled;
            }

            set
            {
                if (value == enabled) { return; }
                enabled = frame.Enabled = textBlock.Enabled = value;
            }
        }

        public override Color Color
        {
            get { return base.Color; }
            set
            {
                base.Color = value;
                frame.Color = value;
            }
        }

        public override Color HoverColor
        {
            get { return base.HoverColor; }
            set
            {
                base.HoverColor = value;
                frame.HoverColor = value;
            }
        }

        public override Color SelectedColor
        {
            get
            {
                return base.SelectedColor;
            }
            set
            {
                base.SelectedColor = value;
                frame.SelectedColor = value;
            }
        }

        public override Color PressedColor
        {
            get
            {
                return base.PressedColor;
            }
            set
            {
                base.PressedColor = value;
                frame.PressedColor = value;
            }
        }

        public override Color OutlineColor
        {
            get { return base.OutlineColor; }
            set
            {
                base.OutlineColor = value;
                if (frame != null) frame.OutlineColor = value;
            }
        }

        public Color TextColor
        {
            get { return textBlock.TextColor; }
            set { textBlock.TextColor = value; }
        }

        public Color HoverTextColor
        {
            get { return textBlock.HoverTextColor; }
            set { textBlock.HoverTextColor = value; }
        }

        public Color SelectedTextColor
        {
            get { return textBlock.SelectedTextColor; }
            set { textBlock.SelectedTextColor = value; }
        }

        public Color DisabledTextColor
        {
            get { return textBlock.DisabledTextColor; }
        }

        public override float FlashTimer
        {
            get { return Frame.FlashTimer; }
        }

        public override GUIFont Font
        {
            get
            {
                return (textBlock == null) ? GUIStyle.Font : textBlock.Font;
            }
            set
            {
                base.Font = value;
                if (textBlock != null) { textBlock.Font = value; }
            }
        }
        
        public LocalizedString Text
        {
            get { return textBlock.Text; }
            set { textBlock.Text = value; }
        }

        public ForceUpperCase ForceUpperCase
        {
            get { return textBlock.ForceUpperCase; }
            set { textBlock.ForceUpperCase = value; }
        }

        public override RichString ToolTip
        {
            get
            {
                return base.ToolTip;
            }
            set
            {
                base.ToolTip = value;
                textBlock.ToolTip = value;                
            }
        }

        private GUIComponent holdOverlay;

        private bool requireHold;
        public bool RequireHold
        {
            get => requireHold;
            set
            {
                requireHold = value;
                if (value)
                {
                    holdOverlay ??= new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), Frame.RectTransform, Anchor.CenterLeft), style: null)
                    {
                        Color = GUIStyle.Yellow * 0.33f,
                        CanBeFocused = false,
                        IgnoreLayoutGroups = true,
                        Visible = true
                    };
                }
                else if (holdOverlay != null)
                {
                    holdOverlay.Visible = false;
                }
            }
        }
        public float HoldDurationSeconds { get; set; } = 5f;
        private float holdTimer;

        public bool Pulse { get; set; }
        private float pulseTimer;
        private float pulseExpand;
        private bool flashed;

        public GUISoundType ClickSound { get; set; } = GUISoundType.Select;

        public override bool PlaySoundOnSelect { get; set; } = true;

        public GUIButton(RectTransform rectT, Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : this(rectT, LocalizedString.EmptyString, textAlignment, style, color) { }

        public GUIButton(RectTransform rectT, LocalizedString text, Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
        {
            CanBeFocused = true;
            HoverCursor = CursorState.Hand;

            frame = new GUIFrame(new RectTransform(Vector2.One, rectT), style) { CanBeFocused = false };
            if (style != null) { GUIStyle.Apply(frame, style == "" ? "GUIButton" : style); }
            if (color.HasValue)
            {
                this.color = frame.Color = color.Value;
            }

            var selfStyle = Style;
            textBlock = new GUITextBlock(new RectTransform(Vector2.One, rectT, Anchor.Center), RichString.Rich(text), textAlignment: textAlignment, style: null)
            {
                TextColor = selfStyle?.TextColor ?? Color.Black,
                HoverTextColor = selfStyle?.HoverTextColor ?? Color.Black,
                SelectedTextColor = selfStyle?.SelectedTextColor ?? Color.Black,
                CanBeFocused = false
            };
            if (rectT.Rect.Height == 0 && !text.IsNullOrEmpty())
            {
                RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(textBlock.Text).Y));
                RectTransform.MinSize = textBlock.RectTransform.MinSize = new Point(0, System.Math.Max(rectT.MinSize.Y, Rect.Height));
                TextBlock.SetTextPos();
            }
            GUIStyle.Apply(textBlock, "", this);

            Enabled = true;
        }

        public override void ApplyStyle(GUIComponentStyle style)
        {
            base.ApplyStyle(style);

            frame?.ApplyStyle(style);
        }

        public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
        {
            Frame.Flash(color, flashDuration, useRectangleFlash, useCircularFlash, flashRectInflate);
        }

        protected override void Draw(SpriteBatch spriteBatch)
        {
            if (Pulse && pulseTimer > 1.0f)
            {
                Rectangle expandRect = Rect;
                float expand = (pulseExpand * 20.0f) * GUI.Scale;
                expandRect.Inflate(expand, expand);

                GUIStyle.EndRoundButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
            }
        }

        protected override void Update(float deltaTime)
        {
            if (!Visible) return;
            base.Update(deltaTime);
            if (Rect.Contains(PlayerInput.MousePosition) && CanBeSelected && CanBeFocused && Enabled && GUI.IsMouseOn(this))
            {
                State = Selected ?
                    ComponentState.HoverSelected :
                    ComponentState.Hover;
                if (PlayerInput.PrimaryMouseButtonDown())
                {
                    OnButtonDown?.Invoke();
                }
                if (PlayerInput.PrimaryMouseButtonHeld())
                {
                    if (RequireHold)
                    {
                        holdTimer += deltaTime;
                    }

                    if (OnPressed != null)
                    {
                        if (OnPressed())
                        {
                            State = ComponentState.Pressed;
                        }
                    }
                    else
                    {
                        State = ComponentState.Pressed;
                    }
                }
                else if (PlayerInput.PrimaryMouseButtonClicked())
                {
                    if (!RequireHold || holdTimer > HoldDurationSeconds)
                    {
                        if (PlaySoundOnSelect)
                        {
                            SoundPlayer.PlayUISound(ClickSound);
                        }

                        if (OnClicked != null)
                        {
                            if (OnClicked(this, UserData))
                            {
                                State = ComponentState.Selected;
                            }
                        }
                        else
                        {
                            Selected = !Selected;
                        }
                    }
                }
                else
                {
                    holdTimer = 0.0f;
                }
            }
            else
            {
                holdTimer = 0.0f;
                if (!ExternalHighlight)
                {
                    State = Selected ? ComponentState.Selected : ComponentState.None;
                }
                else
                {
                    State = ComponentState.Hover;
                }
            }

            if (RequireHold)
            {
                float width = MathHelper.Clamp(holdTimer / HoldDurationSeconds, 0f, 1f);
                if (!MathUtils.NearlyEqual(width, holdOverlay.RectTransform.RelativeSize.X))
                {
                    holdOverlay.RectTransform.RelativeSize = new Vector2(width, 1f);
                }

                holdOverlay.Color =
                    holdTimer >= HoldDurationSeconds
                        ? Color.Green * 0.33f
                        : Color.Red * 0.33f;
            }

            foreach (GUIComponent child in Children)
            {
                child.State = State;
            }

            if (Pulse)
            {
                pulseTimer += deltaTime;
                if (pulseTimer > 1.0f)
                {
                    if (!flashed)
                    {
                        flashed = true;
                        Frame.Flash(Color.White * 0.2f, 0.8f, true);
                    }

                    pulseExpand += deltaTime;
                    if (pulseExpand > 1.0f)
                    {
                        pulseTimer = 0.0f;
                        pulseExpand = 0.0f;
                        flashed = false;
                    }
                }
            }
        }
    }
}
