using System;
using System.Collections.Generic;
using System.Text;
using Yanesdk.GUIParts;
using Yanesdk.Draw;
using Yamalib.Input;
using Yamalib.Util;
using Yanesdk.Input;

namespace Yamalib.GUI
{
    /// <summary>
    /// リストボックスの項目データ
    /// </summary>
    public interface ListBoxItem
    {
        void OnMove(IScreen screen);
        void OnDraw(IScreen screen);

        void SetPos(int x, int y);
        void GetPos(out int x, out int y);

        void SetSize(Size size);
        Size GetSize();

        bool IsSelect();
        void SetSelect(bool b);

        bool IsLClick();
        bool IsRClick();
    }

    /// <summary>
    /// リストボックス表示項目のシンプルな実装
    /// </summary>
    public class SimpleListBoxItem : ListBoxItem
    {

        #region メンバフィールド

        private readonly static FontRepository fontRepository = new FontRepository(YamalibConst.FONT_REP_TEXTURE_FACTORY);

        private IMouseInput m_mouse;
        private int x;
        private int y;
        private Color4ub m_fontColor;			// フォントのカラー	
        private Color4ub m_selectedBackColor;	// 選択された背景カラー	
        private float m_rate;		// フォントの文字サイズ
        private bool m_select;
        private string m_text;
        private readonly List<ITexture> m_tex = new List<ITexture>();
        private bool m_lclick;
        private bool m_rclick;
        private Size m_areaSz;

        #endregion

        public static FontRepository FontRepository
        {
            get { return SimpleListBoxItem.fontRepository; }
        }


        /// コンストラクタ
        public SimpleListBoxItem(IMouseInput mouse_)
        {
            m_mouse = mouse_;
            m_rate = 1.0f;
            m_fontColor.SetColor(255, 255, 255);
            m_selectedBackColor.SetColor(0, 0, 0);
        }

        /// 表示文字列
        public void SetText(string str)
        {
            m_tex.Clear();
            m_text = str;
            foreach (char c in m_text)
            {
                ITexture t = FontRepository.GetTexture(c, 0);
                if (t == null) continue;
                m_tex.Add(t);
            }
        }

        /// 表示テキストを取得
        public string GetText()
        {
            return m_text;
        }

        /// フォントのレートを設定
        public void SetRate(float f_)
        {
            this.m_rate = f_;
        }

        /// フォントの色を設定
        public void SetFontColor(Color4ub color_)
        {
            this.m_fontColor = color_;
        }

        /// 選択中の背景カラー	
        public void SetSelBackColor(Color4ub color_)
        {
            this.m_selectedBackColor = color_;
        }

        #region ListBoxItem メンバ

        public void OnMove(IScreen screen)
        {
            m_lclick = false;
            m_rclick = false;

            int mx, my;
            m_mouse.GetPos(out mx, out my);

            if (PtInRect(mx, my, x, y, x + (int)m_areaSz.Cx, y + (int)m_areaSz.Cy))
            {
                //Console.WriteLine("mx={0}, my={1}, left={2}, top={3}, right={4}, bottom={5}\n", mx, my, X, Y, X + m_areaSz.Cx, Y + m_areaSz.Cy);
                if (m_mouse.IsPush(MouseInput.Button.Left))
                {
                    m_lclick = true;
                }

                if (m_mouse.IsPush(MouseInput.Button.Right))
                {
                    m_rclick = true;
                }
            }
        }

        public void OnDraw(IScreen screen)
        {
            // 下地描画
            if (m_select)
            {
                DrawSelected(screen, x, y);
            }

            Color4ub colOrg = YanesdkUtil.GetNowScreenColor(screen);

            screen.SetColor(this.m_fontColor);

            // 高さは指定にならうといて、幅だけ保証
            float sumW = 0.0f;
            foreach (ITexture t in m_tex)
            {
                float w = t.Width * m_rate;
                if (m_areaSz.Cx < sumW + w) break;
                screen.BltRotate(t, (int)(x + sumW), y, 0, m_rate, 0);
                sumW += w;
            }

            screen.SetColor(colOrg);
        }

        public void SetPos(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public void GetPos(out int x, out int y)
        {
            x = this.x;
            y = this.y;
        }

        public void SetSize(Size size)
        {
            this.m_areaSz = size;
        }

        public Size GetSize()
        {
            return this.m_areaSz;
        }

        public bool IsSelect()
        {
            return this.m_select;
        }

        public void SetSelect(bool b)
        {
            this.m_select = b;
        }

        public bool IsLClick()
        {
            return m_lclick;
        }

        public bool IsRClick()
        {
            return m_rclick;
        }

        #endregion


        #region 非公開メンバ

        /// 点が矩形の内側にあるかどうか
        private static bool PtInRect(int px, int py, int left, int top, int right, int bottom)
        {
            return (px >= left && px < right && py >= top && py < bottom);
        }

        /// 選択背景の描画
        private void DrawSelected(IScreen screen, int x, int y)
        {
            Color4ub colOrg = YanesdkUtil.GetNowScreenColor(screen);
            screen.SetColor(m_selectedBackColor);

            screen.DrawPolygon(
                x, y,
                x + (int)m_areaSz.Cx, y,
                x + (int)m_areaSz.Cx, y + (int)m_areaSz.Cy,
                x, y + (int)m_areaSz.Cy
            );

            screen.SetColor(colOrg);
        }

        #endregion

    }

    /// <summary>
    /// WindowsのListboxをエミュレートする
    /// </summary>
    public class GUIListBox : ITextureGUI
    {

        #region メンバフィールド

        private int x;
        private int y;
        private int m_topIndex;
        private ITexture m_base;
        private readonly List<ListBoxItem> items = new List<ListBoxItem>();
        private Scrollbar m_bar = new Scrollbar();
        private Rect m_area;
        private bool m_multiSel;
        private bool m_newSelect;
        private ControlContext controlContext;
        private bool visible = true;
        private IMouseInput mouse;

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public GUIListBox()
        {
            m_bar = new Scrollbar();
        }

        #region ITextureGUI メンバ

        public void OnInit(ControlContext cc)
        {
            controlContext = cc;
        }

        public void OnPaint(IScreen scr, ControlContext cc, int x, int y)
        {
        }

        public bool Visible
        {
            get
            {
                return visible;
            }
            set
            {
                visible = value;
            }
        }

        #endregion


        /// 表示エリアの設定
        public void SetArea(Rect rc)
        {
            m_area = rc;
            m_bar.SetSize(10, (int)rc.Height);
        }

        /// リストボックスの下地となるテクスチャ
        public void SetBaseTexture(ITexture t_)
        {
            this.m_base = t_;
        }

        /// スクロールバーの設定
        public void SetScrollbar(Scrollbar bar)
        {
            m_bar = bar;
        }

        /// スクロールバーの取得
        public Scrollbar GetScrollbar()
        {
            return m_bar;
        }

        /// 複数選択可／不可
        public void SetMultiSelectable(bool b)
        {
            m_multiSel = b;
        }

        /// 位置の設定
        public void SetXY(int x_, int y_)
        {
            this.x = x_;
            this.y = y_;
            m_bar.SetXY(x_, y_);
        }

        /// マウス設定
        public void SetMouse(IMouseInput mouse_)
        {
            mouse = mouse_;
            //m_bar.SetMouse(mouse_);
        }

        /// リストボックス内にマウスカーソルがあるか
        public bool IsInBox()
        {
            int mx, my;
            mouse.GetPos(out mx, out my);
            return PtInRect(mx, my, (int)m_area.Left, (int)m_area.Top, (int)m_area.Right, (int)m_area.Bottom);
        }

        /// 選択状態にする
        public void SetSelect(int n)
        {
            if (n < 0) return;
            if (n >= this.items.Count) return;
            this.items[n].SetSelect(true);
            int i = 0;
            foreach (ListBoxItem item in items)
            {
                if (i != n)
                {
                    item.SetSelect(false);
                }
                ++i;
            }
        }

        /// 一個だけ選択されていれば０以上を返却する。でなければ-1;
        public int GetSelectedNum()
        {
            int i = 0;
            foreach (ListBoxItem item in this.items)
            {
                if (item.IsSelect())
                {
                    return i;
                }
                ++i;
            }
            return -1;
        }


        /// 新規選択されたか
        public bool IsNewSelect()
        {
            return this.m_newSelect;
        }

        /// 仮想高さの取得
        public int GetVHieght()
        {
            int size = 0;
            foreach (ListBoxItem item in items)
            {
                size += (int)item.GetSize().Cy;
            }
            return size;
        }

        /// 項目を最後に追加する
        public void AddItem(ListBoxItem item)
        {
            this.items.Add(item);
            m_bar.SetScrollSize(GetVHieght());
        }

        /// 項目を追加する
        public void AddItemAll(List<ListBoxItem> items_)
        {
            foreach (ListBoxItem item in items_)
            {
                this.items.Add(item);
            }
        }

        /// リストボックス項目の取得
        public List<ListBoxItem> GetItemAll()
        {
            return this.items;
        }

        /// 選択されているアイテム配列を取得する
        public ListBoxItem GetSelectedItem()
        {
            int selectedIndex = GetSelectedNum();
            if (-1 == selectedIndex)
            {
                return null;
            }
            return GetItem(selectedIndex);
        }

        /// リストボックス項目の取得
        public ListBoxItem GetItem(int i)
        {
            if (i < 0 || i >= items.Count)
            {
                return null;
            }
            return items[i];
        }

        /// 毎回呼び出すなり
        public void OnMove(IScreen screen)
        {
            m_newSelect = false;
            int newSel = -1;

            int i = 0;
            foreach (ListBoxItem item in items)
            {
                // 動かす前の状態として保持
                bool select = item.IsSelect();
                item.OnMove(screen);
                // 動作後、動かされたかどうかの判定
                if (item.IsLClick())
                {
                    if (!select)
                    {
                        newSel = i;
                        item.SetSelect(true);
                    }
                }
                ++i;
            }

            if (-1 != newSel && !m_multiSel)
            {
                m_newSelect = true;
                // 複数選択が不可で新規選択されれば、全項目フラグを再設定
                int j = 0;
                foreach (ListBoxItem item in items)
                {
                    if (j != newSel)
                    {
                        item.SetSelect(false);
                    }
                    ++j;
                }
            }

            int scrollx, scrolly;
            m_bar.GetXY(out scrollx, out scrolly);
            m_bar.SetXY(scrollx, this.y);
            m_bar.OnMove(screen);

            float pos = m_bar.GetScrollPos();
            m_topIndex = (int)(pos * items.Count);
        }

        /// 毎回呼び出すなり
        public void OnDraw(IScreen screen)
        {

            if (m_base != null)
            {
                screen.Blt(m_base, (int)m_area.Left, (int)m_area.Top);
            }

            int oy = 0;
            // アイテム描画
            for (int i = m_topIndex; i < items.Count; ++i)
            {
                Size sz = items[i].GetSize();

                if (sz.Cy + oy > m_area.Bottom)
                {
                    break;
                }

                items[i].SetPos(x, y + oy);
                items[i].OnDraw(screen);
                oy += (int)sz.Cy;
            }

            m_bar.OnDraw(screen);
        }

        #region 非公開メンバ

        private static bool PtInRect(int px, int py, int left, int top, int right, int bottom)
        {
            return px >= left && px < right && py >= top && py < bottom;
        }

        #endregion


    }
}
