using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Bitsplash.DatePicker
{
    [ExecuteInEditMode]
    public partial class DatePickerContent : MonoBehaviour , IDatePickerSettingsItem
    {
        [SerializeField]
        [HideInInspector]
        private bool isOpen;
        const int RowCount = 6;
        const int ColumnCount = 7;
        [FormerlySerializedAs("FirstDayOfWeek")]
        [SerializeField]
        [Tooltip("the first day of the week for the content")]
        private DayOfWeek firstDayOfWeek = DayOfWeek.Sunday;
        [FormerlySerializedAs("RightToLeft")]
        [SerializeField]
        private bool rightToLeft = false;
        [FormerlySerializedAs("BottomToTop")]
        [SerializeField]
        private bool bottomToTop = false;
        [FormerlySerializedAs("CellPrefab")]
        [SerializeField]
        [Tooltip("drag a cell template here to use it with the datepicker")]
        private DatePickerCell cellPrefab = null;
        [FormerlySerializedAs("SelectionMode")]
        [SerializeField]
        [Tooltip("single,range and multiple selection types. ")]
        private SelectionType selectionMode;
        [FormerlySerializedAs("AllowEmptySelection")]
        [SerializeField]
        private bool allowEmptySelection = false;
        [SerializeField]
        private DateTime startDate = new DateTime(1960,1,1);
        [SerializeField]
        private DateTime endDate = new DateTime(2030, 12, 31);
        void ValidateYear()
        {
            if (endDate < startDate)
                endDate = startDate;
            mMonthFirst = new DateTime(mMonthFirst.Year, mMonthFirst.Month, 1);
            if(mMonthFirst > endDate)
            {
                mMonthFirst = new DateTime(endDate.Year, endDate.Month, 1);
            }
            if(mMonthFirst < startDate)
            {
                mMonthFirst = new DateTime(startDate.Year, startDate.Month, 1);
            }
        }
        public DateTime StartDate
        {
            get { return startDate; }
            set
            {
                startDate = value.Date;
                ValidateYear();
                Invalidate();
                OnSettingsChanged();
            }
        }
        public DateTime EndDate
        {
            get { return endDate; }
            set
            {
                endDate = value.Date;
                ValidateYear();
                Invalidate();
                OnSettingsChanged();
            }
        }
        /// 
        /// the first day of the week for the date picker
        /// 
        public DayOfWeek FirstDayOfWeek
        {
            get { return firstDayOfWeek; }
            set
            {
                firstDayOfWeek = value;
                OnSettingsChanged();
            }
        }
        /// 
        /// set the date picker to right to left mode
        /// 
        public bool RightToLeft
        {
            get { return rightToLeft; }
            set
            {
                rightToLeft = value;
                OnSettingsChanged();
            }
        }
        /// 
        /// show days from bottom to top instead of top to bottom
        /// 
        public bool BottomToTop
        {
            get { return bottomToTop; }
            set
            {
                bottomToTop = value;
                OnSettingsChanged();
            }
        }
        //public DatePickerCell CellPrefab
        //{
        //    get { return cellPrefab; }
        //    set
        //    {
        //        cellPrefab = value;
        //    }
        //}
            /// 
            /// set the selection mode for the date picker. Single ,Range or Multiple
            /// 
        public SelectionType SelectionMode
        {
            get { return selectionMode; }
            set
            {
                selectionMode = value;
                OnSettingsChanged();
            }
        }
        /// 
        /// allows selection of the date picker to be empty
        /// 
        public bool AllowEmptySelection
        {
            get { return allowEmptySelection; }
            set
            {
                allowEmptySelection = value;
                OnSettingsChanged();
            }
        }
        /// 
        /// used for internal purpose
        /// 
        public event Action SettingsChanged;
        /// 
        /// currently the displayed month and year
        /// 
        DateTime mMonthFirst = DateTime.Today;
        /// 
        /// genearted cells
        /// 
        DatePickerCell[] mCells;
        /// 
        /// the selection collection object for the content
        /// 
        DatePickerCollection mSelection = new DatePickerCollection();
        /// 
        /// a date to cell map for quick lookup
        /// 
        Dictionary mDateToCell = new Dictionary();
        /// 
        /// an input delegation for the date picker
        /// 
        DatePickerInput mDatePickerInput;
        /// 
        /// true if the datepicker should be recreated
        /// 
        bool mInvalidated = true;
        /// 
        /// This event triggers when the use navigates the datepicker
        /// 
        public UnityEvent OnDisplayChanged;
        /// 
        /// this event triggers when the date selection has changed
        /// 
        public UnityEvent OnSelectionChanged;
        /// 
        /// the date picker selection collection. Use this object to change and query the current date selection
        /// 
        public DatePickerCollection Selection { get { return mSelection; } }
        void EnsureInput()
        {
            mDatePickerInput = GetComponent();
            if (mDatePickerInput == null)
                mDatePickerInput = gameObject.AddComponent();
        }
        /// 
        /// the currently displayed date in the datepicker
        /// 
        public DateTime DisplayDate { get { return mMonthFirst; } }
        /// 
        /// sets the month and year being displayed in the date picker. 
        /// 
        /// 
        /// 
        public void SetMonthAndYear(int year,int month)
        {
            FillCells(new DateTime(year, month, 1));
        }
        /// 
        /// sets the year being displayed in the date picker
        /// 
        /// 
        public void SetYear(int year)
        {
            FillCells(new DateTime(year, mMonthFirst.Month, 1));
        }
        /// 
        /// sets the month being displayed in the date picker
        /// 
        /// 
        public void SetMonth(int month)
        {
            FillCells(new DateTime(mMonthFirst.Year,month, 1));
        }
        /// 
        /// used internally
        /// 
        public string EditorTitle { get {return "Board"; } }
        /// 
        /// used internally
        /// 
        public int Order { get { return 0; } }
        /// 
        /// advances the display by 1 year
        /// 
        public void NextYear()
        {
            FillCells(mMonthFirst.AddYears(1));
        }
        void OnSettingsChanged()
        {
            if (SettingsChanged != null)
                SettingsChanged();
        }
        /// 
        /// retracts the display by 1 year
        /// 
        public void PrevYear()
        {
            FillCells(mMonthFirst.AddYears(-1));
        }
        /// 
        /// advances the display by 1 month
        /// 
        public void NextMonth()
        {
            FillCells(mMonthFirst.AddMonths(1));
        }
        /// 
        /// retracts the display by 1 month
        /// 
        public void PrevMonth()
        {
            FillCells(mMonthFirst.AddMonths(-1));
        }
        public virtual string DateToString(DateTime date)
        {
            return date.Day.ToString();
        }
        void GenerateCells()
        {
            Clear();
            if (cellPrefab == null)
                return;
            mCells = new DatePickerCell[((int)RowCount) * ((int)ColumnCount)];
            float ColumnSize = 1f / ColumnCount;
            float RowSize = 1f / RowCount;
            for(float i=0; i());
                    CommonMethods.HideObject(newObj);
                    newObj.name = String.Format("day_{0}_{1}", j, i);
                    newObj.SetActive(true);
                    CommonMethods.EnsureComponent(newObj);
                    var rect = newObj.GetComponent();
                    rect.anchorMin = new Vector2(startX, startY);
                    rect.anchorMax = new Vector2(endX, endY);
                    rect.anchoredPosition = new Vector2(0f, 0f);
                    rect.sizeDelta = new Vector2(0f, 0f);
                    int childIndex = (int)(i * ColumnCount + j);
                    childIndex = (int)(i * ColumnCount + j);
                    mCells[childIndex] = newObj.GetComponent();
                    var addon = CommonMethods.EnsureComponent(newObj);
                    addon.SetParent(this, childIndex);
                }
            }
            FillCells(mMonthFirst);
        }
        DateTime MonthFromDate(DateTime date)
        {
            return new DateTime(date.Year, date.Month, 1);
        }
        DatePickerCell getCell(int day,int week)
        {
            return mCells[week * ColumnCount + day];
        }
        
        void FillCells(DateTime monthFirst)
        {
            monthFirst = monthFirst.Date;
            mMonthFirst = monthFirst;
            ValidateYear();
            if (mCells == null)
                return;
            monthFirst = mMonthFirst;
            int monthDayOfWeek = (int)monthFirst.DayOfWeek;
            int span = monthDayOfWeek - (int)FirstDayOfWeek;
            if (span < 0)
                span += 7;
            DateTime startFrom = (monthFirst - TimeSpan.FromDays(span)).Date;
            DateTime endIn = startFrom + TimeSpan.FromDays(RowCount * ColumnCount);
            DateTime monthLast = monthFirst + TimeSpan.FromDays(DateTime.DaysInMonth(monthFirst.Year, monthFirst.Month) - 1);
            DateTime current = startFrom;
            mDateToCell.Clear();
            for (int i=0; i monthLast || current < startDate || current > endDate)
                    cellenabled = false;
                mCells[i].SetInitialSettings(cellenabled, false);
                mDateToCell[current.Date] = mCells[i];
                current += TimeSpan.FromDays(1);
            }
            RefreshSelection();
            if (OnDisplayChanged != null)
                OnDisplayChanged.Invoke();
        }
        protected void Clear()
        {
            IDateTimeItem[] children = GetComponentsInChildren();
            for (int i = 0; i < children.Length; ++i)
            {
                if (children[i] != null)
                {
                    if (children[i].gameObject.GetComponentInParent() != this)
                        continue;
                    if (children[i].gameObject != gameObject)
                        CommonMethods.SafeDestroy(children[i].gameObject);
                }
            }
        }
        public void Invalidate()
        {
            mInvalidated = true;
        }
        void HookEvents()
        {
            ((IDatePickerCollectionPrivate)mSelection).SelectionModified -= DatePicker_SelectionModified;
            ((IDatePickerCollectionPrivate)mSelection).SelectionModified += DatePicker_SelectionModified;
        }
        private void DatePicker_SelectionModified()
        {
            RaiseSelectionChanged();
        }
        public void Start()
        {
            HookEvents();
            EnsureInput();
            GenerateCells();
          //  if (AllowEmptySelection == false)
          //      SelectOne(DateTime.Today);
        }
        public void Update()
        {
            if(AllowEmptySelection != ((IDatePickerCollectionPrivate)mSelection).AllowEmpty)
                ((IDatePickerCollectionPrivate)mSelection).AllowEmpty = AllowEmptySelection;
            UpdateSelection();
            if(mInvalidated)
            {
                GenerateCells();
                mInvalidated = false;
            }
        }
        public void OnValidate()
        {
            ValidateYear();
            OnSettingsChanged();
            Invalidate();
        }
        /// 
        /// retrives the underlaying gameobject specified by dateTime. If the dateTime is not currently displayed , null is returned
        /// 
        /// 
        /// 
        public DatePickerCell GetCellObjectByDate(DateTime dateTime)
        {
            DatePickerCell res = null;
            if (mDateToCell.TryGetValue(dateTime.Date, out res))
                return res;
            return null;
        }
    }
}