//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transformations;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework;

namespace osu.Game.GameModes.Menu
{
    public partial class ButtonSystem : Container, IStateful<MenuState>
    {
        public Action OnEdit;
        public Action OnExit;
        public Action OnDirect;
        public Action OnSolo;
        public Action OnSettings;
        public Action OnMulti;
        public Action OnChart;
        public Action OnTest;

        private FlowContainerWithOrigin buttonFlow;

        //todo: make these non-internal somehow.
        internal const float button_area_height = 100;
        internal const float button_width = 140f;
        internal const float wedge_width = 20;

        public const int EXIT_DELAY = 3000;

        private OsuLogo osuLogo;
        private Drawable iconFacade;
        private Container buttonArea;
        private Box buttonAreaBackground;

        private Button backButton;
        private Button settingsButton;

        List<Button> buttonsTopLevel = new List<Button>();
        List<Button> buttonsPlay = new List<Button>();

        public ButtonSystem()
        {
            RelativeSizeAxes = Axes.Both;
        }

        public override void Load(BaseGame game)
        {
            base.Load(game);

            Children = new Drawable[]
            {
                buttonArea = new Container
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.Centre,
                    RelativeSizeAxes = Axes.X,
                    Size = new Vector2(1, button_area_height),
                    Alpha = 0,
                    Children = new Drawable[]
                    {
                        buttonAreaBackground = new Box
                        {
                            RelativeSizeAxes = Axes.Both,
                            Size = new Vector2(2, 1),
                            Colour = new Color4(50, 50, 50, 255),
                            Anchor = Anchor.Centre,
                            Origin = Anchor.Centre,
                        },
                        buttonFlow = new FlowContainerWithOrigin
                        {
                            Direction = FlowDirection.HorizontalOnly,
                            Anchor = Anchor.Centre,
                            Spacing = new Vector2(-wedge_width, 0),
                            Children = new Drawable[]
                            {
                                settingsButton = new Button(@"settings", @"options", FontAwesome.gear, new Color4(85, 85, 85, 255), OnSettings, -wedge_width, Key.O),
                                backButton = new Button(@"back", @"back", FontAwesome.fa_osu_left_o, new Color4(51, 58, 94, 255), onBack, -wedge_width, Key.Escape),
                                iconFacade = new Container //need a container to make the osu! icon flow properly.
								{
                                    Size = new Vector2(0, button_area_height)
                                }
                            },
                            CentreTarget = iconFacade
                        }
                    }
                },
                osuLogo = new OsuLogo
                {
                    Action = onOsuLogo,
                    Origin = Anchor.Centre,
                    Anchor = Anchor.Centre
                }
            };

            buttonFlow.Position = new Vector2(wedge_width * 2 - (button_width + osuLogo.SizeForFlow / 4), 0);

            buttonsPlay.Add(new Button(@"solo", @"freeplay", FontAwesome.user, new Color4(102, 68, 204, 255), OnSolo, wedge_width, Key.P));
            buttonsPlay.Add(new Button(@"multi", @"multiplayer", FontAwesome.users, new Color4(94, 63, 186, 255), OnMulti, 0, Key.M));
            buttonsPlay.Add(new Button(@"chart", @"charts", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), OnChart));

            buttonsTopLevel.Add(new Button(@"play", @"play", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, wedge_width, Key.P));
            buttonsTopLevel.Add(new Button(@"osu!editor", @"edit", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), OnEdit, 0, Key.E));
            buttonsTopLevel.Add(new Button(@"osu!direct", @"direct", FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), OnDirect, 0, Key.D));
            buttonsTopLevel.Add(new Button(@"exit", @"exit", FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), onExit, 0, Key.Q));

            buttonFlow.Add(buttonsPlay);
            buttonFlow.Add(buttonsTopLevel);
        }

        protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
        {
            switch (args.Key)
            {
                case Key.Space:
                    osuLogo.TriggerClick(state);
                    return true;
                case Key.Escape:
                    if (State == MenuState.Initial)
                        return false;

                    State = MenuState.Initial;
                    return true;
            }

            return true;
        }

        private void onPlay()
        {
            State = MenuState.Play;
        }

        private void onExit()
        {
            State = MenuState.Exit;
            OnExit?.Invoke();
        }

        private void onBack()
        {
            State = MenuState.TopLevel;
        }

        private void onOsuLogo()
        {
            switch (state)
            {
                case MenuState.Initial:
                    //Game.Audio.PlaySamplePositional(@"menuhit");
                    State = MenuState.TopLevel;
                    return;
                case MenuState.TopLevel:
                    buttonsTopLevel.First().TriggerMouseDown();
                    return;
                case MenuState.Play:
                    buttonsPlay.First().TriggerMouseDown();
                    return;
            }
        }

        MenuState state;

        public override bool HandleInput => state != MenuState.Exit;

        public MenuState State
        {
            get
            {
                return state;
            }
            set
            {
                if (state == value) return;

                MenuState lastState = state;
                state = value;

                //todo: figure a more elegant way of doing this.
                buttonsTopLevel.ForEach(b => b.ContractStyle = 0);
                buttonsPlay.ForEach(b => b.ContractStyle = 0);
                backButton.ContractStyle = 0;
                settingsButton.ContractStyle = 0;

                switch (state)
                {
                    case MenuState.Initial:
                        buttonAreaBackground.ScaleTo(Vector2.One, 500, EasingTypes.Out);
                        buttonArea.FadeOut(500);

                        osuLogo.Delay(150);
                        osuLogo.MoveTo(Vector2.Zero, 800, EasingTypes.OutExpo);
                        osuLogo.ScaleTo(1, 800, EasingTypes.OutExpo);

                        foreach (Button b in buttonsTopLevel)
                            b.State = ButtonState.Contracted;

                        foreach (Button b in buttonsPlay)
                            b.State = ButtonState.Contracted;
                        break;
                    case MenuState.TopLevel:
                        buttonAreaBackground.ScaleTo(Vector2.One, 200, EasingTypes.Out);

                        osuLogo.MoveTo(buttonFlow.DrawPosition, 200, EasingTypes.In);
                        osuLogo.ScaleTo(0.5f, 200, EasingTypes.In);

                        buttonArea.FadeIn(300);

                        if (lastState == MenuState.Initial)
                            //todo: this propagates to invisible children and causes delays later down the track (on first MenuState.Play)
                            buttonArea.Delay(150, true);

                        foreach (Button b in buttonsTopLevel)
                            b.State = ButtonState.Expanded;

                        foreach (Button b in buttonsPlay)
                            b.State = ButtonState.Contracted;
                        break;
                    case MenuState.Play:
                        foreach (Button b in buttonsTopLevel)
                            b.State = ButtonState.Exploded;

                        foreach (Button b in buttonsPlay)
                            b.State = ButtonState.Expanded;
                        break;
                    case MenuState.EnteringMode:
                        buttonAreaBackground.ScaleTo(new Vector2(2, 0), 300, EasingTypes.InSine);

                        buttonsTopLevel.ForEach(b => b.ContractStyle = 1);
                        buttonsPlay.ForEach(b => b.ContractStyle = 1);
                        backButton.ContractStyle = 1;
                        settingsButton.ContractStyle = 1;

                        foreach (Button b in buttonsTopLevel)
                            b.State = ButtonState.Contracted;

                        foreach (Button b in buttonsPlay)
                            b.State = ButtonState.Contracted;
                        break;
                    case MenuState.Exit:
                        buttonArea.FadeOut(200);

                        foreach (Button b in buttonsTopLevel)
                            b.State = ButtonState.Contracted;

                        foreach (Button b in buttonsPlay)
                            b.State = ButtonState.Contracted;

                        osuLogo.Delay(150);

                        osuLogo.ScaleTo(1f, EXIT_DELAY * 1.5f);
                        osuLogo.RotateTo(20, EXIT_DELAY * 1.5f);
                        osuLogo.FadeOut(EXIT_DELAY);
                        break;
                }

                backButton.State = state == MenuState.Play ? ButtonState.Expanded : ButtonState.Contracted;
                settingsButton.State = state == MenuState.TopLevel ? ButtonState.Expanded : ButtonState.Contracted;

                if (lastState == MenuState.Initial)
                    buttonArea.DelayReset();
            }
        }

        protected override void Update()
        {
            //if (OsuGame.IdleTime > 6000 && State != MenuState.Exit)
            //    State = MenuState.Initial;

            iconFacade.Width = osuLogo.SizeForFlow * 0.5f;
            base.Update();
        }
    }

    public enum MenuState
    {
        Initial,
        TopLevel,
        Play,
        EnteringMode,
        Exit,
    }
}