using System;
using System.Collections.Generic;
using UnityEngine;

namespace FairyGUI
{
    /// <summary>
    /// Window class.
    /// 窗口使用前首先要设置窗口中需要显示的内容，这通常是在编辑器里制作好的，可以直接使用Window.contentPane进行设置。
    /// 建议把设置contentPane等初始化操作放置到Window.onInit方法中。
    /// 另外，FairyGUI还提供了一套机制用于窗口动态创建。动态创建是指初始时仅指定窗口需要使用的资源，等窗口需要显示时才实际开始构建窗口的内容。
    /// 首先需要在窗口的构造函数中调用Window.addUISource。这个方法需要一个IUISource类型的参数，而IUISource是一个接口，
    /// 用户需要自行实现载入相关UI包的逻辑。当窗口第一次显示之前，IUISource的加载方法将会被调用，并等待载入完成后才返回执行Window.OnInit，然后窗口才会显示。
    /// 
    /// 如果你需要窗口显示时播放动画效果，那么覆盖doShowAnimation编写你的动画代码，并且在动画结束后调用onShown。覆盖onShown编写其他需要在窗口显示时处理的业务逻辑。
    /// 如果你需要窗口隐藏时播放动画效果，那么覆盖doHideAnimation编写你的动画代码，并且在动画结束时调用Window.hideImmediately（注意不是直接调用onHide！）。覆盖onHide编写其他需要在窗口隐藏时处理的业务逻辑。
    /// </summary>
    public class Window : GComponent
    {
        /// <summary>
        /// 
        /// </summary>
        public bool bringToFontOnClick;

        GComponent _frame;
        GComponent _contentPane;
        GObject _modalWaitPane;
        GObject _closeButton;
        GObject _dragArea;
        GObject _contentArea;
        bool _modal;

        List<IUISource> _uiSources;
        bool _inited;
        bool _loading;

        protected int _requestingCmd;

#if FAIRYGUI_PUERTS
        public Action __onInit;
        public Action __onShown;
        public Action __onHide;
        public Action __doShowAnimation;
        public Action __doHideAnimation;
#endif

        public Window()
            : base()
        {
            _uiSources = new List<IUISource>();
            this.tabStopChildren = true;
            bringToFontOnClick = UIConfig.bringWindowToFrontOnClick;

            displayObject.onAddedToStage.Add(__addedToStage);
            displayObject.onRemovedFromStage.Add(__removeFromStage);
            displayObject.onTouchBegin.AddCapture(__touchBegin);

            this.gameObjectName = "Window";
            SetHome(GRoot.inst);
        }

        /// <summary>
        /// Set a UISource to this window. It must call before the window is shown. When the window is first time to show,
        /// UISource.Load is called. Only after all UISource is loaded, the window will continue to init.
        /// 为窗口添加一个源。这个方法建议在构造函数调用。当窗口第一次显示前，UISource的Load方法将被调用，然后只有所有的UISource
        /// 都ready后，窗口才会继续初始化和显示。
        /// </summary>
        /// <param name="source"></param>
        public void AddUISource(IUISource source)
        {
            _uiSources.Add(source);
        }

        /// <summary>
        /// 
        /// </summary>
        public GComponent contentPane
        {
            set
            {
                if (_contentPane != value)
                {
                    if (_contentPane != null)
                        RemoveChild(_contentPane);
                    _contentPane = value;
                    if (_contentPane != null)
                    {
                        this.gameObjectName = "Window - " + _contentPane.gameObjectName;
                        _contentPane.gameObjectName = "ContentPane";

                        AddChild(_contentPane);
                        this.SetSize(_contentPane.width, _contentPane.height);
                        _contentPane.AddRelation(this, RelationType.Size);
                        _contentPane.fairyBatching = true;
                        _frame = _contentPane.GetChild("frame") as GComponent;
                        if (_frame != null)
                        {
                            this.closeButton = _frame.GetChild("closeButton");
                            this.dragArea = _frame.GetChild("dragArea");
                            this.contentArea = _frame.GetChild("contentArea");
                        }
                    }
                    else
                    {
                        _frame = null;
                        this.gameObjectName = "Window";
                    }
                }
            }
            get
            {
                return _contentPane;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public GComponent frame
        {
            get { return _frame; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GObject closeButton
        {
            get { return _closeButton; }
            set
            {
                if (_closeButton != null)
                    _closeButton.onClick.Remove(closeEventHandler);
                _closeButton = value;
                if (_closeButton != null)
                    _closeButton.onClick.Add(closeEventHandler);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public GObject dragArea
        {
            get { return _dragArea; }
            set
            {
                if (_dragArea != value)
                {
                    if (_dragArea != null)
                    {
                        _dragArea.draggable = false;
                        _dragArea.onDragStart.Remove(__dragStart);
                    }

                    _dragArea = value;
                    if (_dragArea != null)
                    {
                        GGraph graph = _dragArea as GGraph;
                        if (graph != null && graph.shape.isEmpty)
                            graph.DrawRect(_dragArea.width, _dragArea.height, 0, Color.clear, Color.clear);
                        _dragArea.draggable = true;
                        _dragArea.onDragStart.Add(__dragStart);
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public GObject contentArea
        {
            get { return _contentArea; }
            set { _contentArea = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GObject modalWaitingPane
        {
            get { return _modalWaitPane; }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Show()
        {
            GRoot.inst.ShowWindow(this);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="r"></param>
        public void ShowOn(GRoot r)
        {
            r.ShowWindow(this);
        }

        /// <summary>
        /// 
        /// </summary>
        public void Hide()
        {
            if (this.isShowing)
                DoHideAnimation();
        }

        /// <summary>
        /// Hide window immediately, no OnHide will be called.
        /// </summary>
        public void HideImmediately()
        {
            this.root.HideWindowImmediately(this);
        }

        /// <summary>
        /// Make the window be center of the screen.
        /// </summary>
        /// <param name="r"></param>
        /// <param name="restraint">Add relations to ensure keeping center on screen size changed.</param>
        public void CenterOn(GRoot r, bool restraint)
        {
            this.SetXY((int)((r.width - this.width) / 2), (int)((r.height - this.height) / 2));
            if (restraint)
            {
                this.AddRelation(r, RelationType.Center_Center);
                this.AddRelation(r, RelationType.Middle_Middle);
            }
        }

        /// <summary>
        /// Switch show and hide status.
        /// </summary>
        public void ToggleStatus()
        {
            if (isTop)
                Hide();
            else
                Show();
        }

        /// <summary>
        /// 
        /// </summary>
        public bool isShowing
        {
            get { return parent != null; }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool isTop
        {
            get { return parent != null && parent.GetChildIndex(this) == parent.numChildren - 1; }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool modal
        {
            get { return _modal; }
            set { _modal = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public void BringToFront()
        {
            this.root.BringToFront(this);
        }

        /// <summary>
        /// 
        /// </summary>
        public void ShowModalWait()
        {
            ShowModalWait(0);
        }

        /// <summary>
        /// Display a modal waiting sign in the front.
        /// 显示一个等待标志在最前面。等待标志的资源可以通过UIConfig.windowModalWaiting。等待标志组件会设置为屏幕大小，请内部做好关联。
        /// 还可以设定一个requestingCmd作为等待的命令字，在CloseModalWait里传入相同的命令字ModalWait将结束，否则CloseModalWait无效。
        /// </summary>
        /// <param name="requestingCmd"></param>
        public void ShowModalWait(int requestingCmd)
        {
            if (requestingCmd != 0)
                _requestingCmd = requestingCmd;

            if (UIConfig.windowModalWaiting != null)
            {
                if (_modalWaitPane == null)
                {
                    _modalWaitPane = UIPackage.CreateObjectFromURL(UIConfig.windowModalWaiting);
                    _modalWaitPane.SetHome(this);
                }

                LayoutModalWaitPane();

                AddChild(_modalWaitPane);
            }
        }

        virtual protected void LayoutModalWaitPane()
        {
            if (_contentArea != null)
            {
                Vector2 pt = _frame.LocalToGlobal(Vector2.zero);
                pt = this.GlobalToLocal(pt);
                _modalWaitPane.SetXY((int)pt.x + _contentArea.x, (int)pt.y + _contentArea.y);
                _modalWaitPane.SetSize(_contentArea.width, _contentArea.height);
            }
            else
                _modalWaitPane.SetSize(this.width, this.height);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool CloseModalWait()
        {
            return CloseModalWait(0);
        }

        /// <summary>
        /// Close modal waiting. If rquestingCmd is equal to the value you transfer in ShowModalWait, mowal wait will be closed.
        /// Otherwise, this function has no effect.
        /// 关闭模式等待。如果requestingCmd和ShowModalWait传入的不相同，则这个函数没有任何动作，立即返回。
        /// </summary>
        /// <param name="requestingCmd"></param>
        /// <returns></returns>
        public bool CloseModalWait(int requestingCmd)
        {
            if (requestingCmd != 0)
            {
                if (_requestingCmd != requestingCmd)
                    return false;
            }
            _requestingCmd = 0;

            if (_modalWaitPane != null && _modalWaitPane.parent != null)
                RemoveChild(_modalWaitPane);

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        public bool modalWaiting
        {
            get { return (_modalWaitPane != null) && _modalWaitPane.inContainer; }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Init()
        {
            if (_inited || _loading)
                return;

            if (_uiSources.Count > 0)
            {
                _loading = false;
                int cnt = _uiSources.Count;
                for (int i = 0; i < cnt; i++)
                {
                    IUISource lib = _uiSources[i];
                    if (!lib.loaded)
                    {
                        lib.Load(__uiLoadComplete);
                        _loading = true;
                    }
                }

                if (!_loading)
                    _init();
            }
            else
                _init();
        }

        /// <summary>
        /// 
        /// </summary>
        virtual protected void OnInit()
        {
#if FAIRYGUI_TOLUA
            CallLua("OnInit");
#endif
#if FAIRYGUI_PUERTS
            if (__onInit != null)
                __onInit();
#endif
        }

        /// <summary>
        /// 
        /// </summary>
        virtual protected void OnShown()
        {
#if FAIRYGUI_TOLUA
            CallLua("OnShown");
#endif
#if FAIRYGUI_PUERTS
            if (__onShown != null)
                __onShown();
#endif
        }

        /// <summary>
        /// 
        /// </summary>
        virtual protected void OnHide()
        {
#if FAIRYGUI_TOLUA
            CallLua("OnHide");
#endif
#if FAIRYGUI_PUERTS
            if (__onHide != null)
                __onHide();
#endif
        }

        /// <summary>
        /// 
        /// </summary>
        virtual protected void DoShowAnimation()
        {
#if FAIRYGUI_TOLUA
            if (!CallLua("DoShowAnimation"))
                OnShown();
#elif FAIRYGUI_PUERTS
            if (__doShowAnimation != null)
                __doShowAnimation();
            else
                OnShown();
#else
            OnShown();
#endif
        }

        /// <summary>
        /// 
        /// </summary>
        virtual protected void DoHideAnimation()
        {
#if FAIRYGUI_TOLUA
            if (!CallLua("DoHideAnimation"))
                HideImmediately();
#elif FAIRYGUI_PUERTS
            if (__doHideAnimation != null)
                __doHideAnimation();
            else
                HideImmediately();
#else
            HideImmediately();
#endif
        }

        void __uiLoadComplete()
        {
            int cnt = _uiSources.Count;
            for (int i = 0; i < cnt; i++)
            {
                IUISource lib = _uiSources[i];
                if (!lib.loaded)
                    return;
            }

            _loading = false;
            _init();
        }

        void _init()
        {
            _inited = true;
            OnInit();

            if (this.isShowing)
                DoShowAnimation();
        }

        override public void Dispose()
        {
            if (_modalWaitPane != null && _modalWaitPane.parent == null)
                _modalWaitPane.Dispose();


            //正在加载资源的异步过程中发生意外关闭 应该取消正在加载的load
            if (_loading)
            {
                for (int i = 0; i < _uiSources.Count; ++i)
                {
                    _uiSources[i].Cancel();
                }
            }
            

#if FAIRYGUI_PUERTS
            __onInit = null;
            __onShown = null;
            __onHide = null;
            __doShowAnimation = null;
            __doHideAnimation = null;
#endif

            base.Dispose();
        }

        virtual protected void closeEventHandler(EventContext context)
        {
            Hide();
        }

        void __addedToStage()
        {
            if (!_inited)
                Init();
            else
                DoShowAnimation();
        }

        void __removeFromStage()
        {
            CloseModalWait();
            OnHide();
        }

        private void __touchBegin(EventContext context)
        {
            if (this.isShowing && bringToFontOnClick)
            {
                BringToFront();
            }
        }

        private void __dragStart(EventContext context)
        {
            context.PreventDefault();

            this.StartDrag((int)context.data);
        }
    }
}
