﻿using System;
using JLGames.RocketDriver.CSharp.DateTimex;
using UnityEngine;

namespace JLGames.RocketDriver.Actions.DateTimex
{
    /// <summary>
    /// Time Slicing
    /// 时间分片
    /// </summary>
    [Serializable]
    public class TimeSlice
    {
        [Tooltip("Ready time\n准备时间点")] [SerializeField] 
        private long m_ReadyTicks; //初始化时间点
        [Tooltip("Ready duration\n准备时间长\n(<0:Unlimited无限)")] [SerializeField] 
        private long m_ReadyLen; //准备时间长
        [Tooltip("Doing duration\n保持时间长\n(<0:Unlimited无限)")] [SerializeField] 
        private long m_DoingLen; //保持时间长
        [Tooltip("Death duration\n消亡时间长\n(<0:Unlimited无限)")] [SerializeField] 
        private long m_DeadthLen; //消亡时间长
        [Tooltip("CD duration\nCD时间长\n(<0:Unlimited无限)")] [SerializeField] 
        private long m_CdLen; //CD时间长

        private DateTimeUtil.FuncGetNowTicks m_GetGetNowTicks;

        public TimeSlice()
        {
            m_GetGetNowTicks = DefaultGetNowTicks;
        }

        public TimeSlice(DateTimeUtil.FuncGetNowTicks funcGetNowTicks)
        {
            m_GetGetNowTicks = funcGetNowTicks;
        }

        public TimeSlice(long readyLen, long doingLen, long deadthLen, long cdLen)
        {
            m_ReadyLen = readyLen;
            m_DoingLen = doingLen;
            m_DeadthLen = deadthLen;
            m_CdLen = cdLen;
            m_GetGetNowTicks = DefaultGetNowTicks;
        }

        public TimeSlice(long readyLen, long doingLen, long deadthLen, long cdLen, DateTimeUtil.FuncGetNowTicks funcGetNowTicks)
        {
            m_ReadyLen = readyLen;
            m_DoingLen = doingLen;
            m_DeadthLen = deadthLen;
            m_CdLen = cdLen;
            m_GetGetNowTicks = funcGetNowTicks;
        }

        public void SetGetNowTicksFunc(DateTimeUtil.FuncGetNowTicks func)
        {
            m_GetGetNowTicks = func;
        }

        public override string ToString()
        {
            return $"TimeSlice[Ready={m_ReadyTicks}, ReadyLen={m_ReadyLen}, DoingLen={m_DoingLen}, DeadthLen={m_DeadthLen}, CdLen={m_CdLen}, " +
                   $"Now={m_GetGetNowTicks()}]";
        }

        /// <summary>
        /// Whether thed ready status has been completed
        /// 准备 完成？
        /// </summary>
        public long ReadyPassed
        {
            get
            {
                if (m_ReadyTicks <= 0 || !HadReadyLen) return -1;
                return m_GetGetNowTicks() - m_ReadyTicks;
            }
        }

        /// <summary>
        /// Whether thed doing status has been completed
        /// 进行中 完成？
        /// </summary>
        public long DoingPassed
        {
            get
            {
                if (m_ReadyTicks <= 0 || m_ReadyLen < 0 || !HadDoginLen) return -1;
                return m_GetGetNowTicks() - m_ReadyTicks - m_ReadyLen;
            }
        }

        /// <summary>
        /// Whether thed death status has been completed
        /// 消亡 完成？
        /// </summary>
        public long DeadthPassed
        {
            get
            {
                if (m_ReadyTicks <= 0 || m_ReadyLen < 0 || m_DoingLen < 0 || !HadDeadthLen) return -1;
                return m_GetGetNowTicks() - m_ReadyTicks - m_ReadyLen - m_DoingLen;
            }
        }

        /// <summary>
        /// Whether thed cd status has been completed
        /// CD 完成？
        /// </summary>
        public long CdPassed
        {
            get
            {
                if (m_ReadyTicks <= 0 || m_ReadyLen < 0 || m_DoingLen < 0 || m_DeadthLen < 0 || !HadCdLen) return -1;
                return m_GetGetNowTicks() - m_ReadyTicks - m_ReadyLen - m_DoingLen - m_DeadthLen;
            }
        }

        /// <summary>
        /// Whether in ready status
        /// 是否 准备中
        /// </summary>
        public bool InReady => CheckPassedIn(ReadyPassed, m_ReadyLen);

        /// <summary>
        /// Whether in doing status
        /// 是否 进行中
        /// </summary>
        public bool InDoing => CheckPassedIn(DoingPassed, m_DoingLen);

        /// <summary>
        /// Whether in death status
        /// 是否 消亡中
        /// </summary>
        public bool InDeadth => CheckPassedIn(DeadthPassed, m_DeadthLen);

        /// <summary>
        /// Whether in cd status
        /// 是否 CD中
        /// </summary>
        public bool InCd => CheckPassedIn(CdPassed, m_CdLen);

        /// <summary>
        /// Whether to set the ready duration
        /// 是否设置 准备时长
        /// </summary>
        public bool HadReadyLen => m_ReadyLen != 0;

        /// <summary>
        /// Whether to set the doing duration
        /// 是否设置 进行时长
        /// </summary>
        public bool HadDoginLen => m_DoingLen != 0;

        /// <summary>
        /// Whether to set the death duration
        /// 是否设置 消亡时长
        /// </summary>
        public bool HadDeadthLen => m_DeadthLen != 0;

        /// <summary>
        /// Whether to set the CD duration
        /// 是否设置 CD时长
        /// </summary>
        public bool HadCdLen => m_CdLen != 0;

        /// <summary>
        /// Start to enter the ready status
        /// 开始准备
        /// </summary>
        public void StartReady()
        {
            m_ReadyTicks = m_GetGetNowTicks();
        }

        /// <summary>
        /// Start to enter the ready status
        /// 开始准备
        /// </summary>
        /// <param name="readyLen">-1代表无限持续,0代表不持续</param>
        public void StartReady(long readyLen)
        {
            m_ReadyTicks = m_GetGetNowTicks();
            m_ReadyLen = readyLen;
        }

        /// <summary>
        /// Start to enter the ready status
        /// 开始准备
        /// </summary>
        public void StartReady(long readyLen, long doingLen, long deadthLen, long cdLen)
        {
            m_ReadyTicks = m_GetGetNowTicks();
            m_ReadyLen = readyLen;
            m_DoingLen = doingLen;
            m_DeadthLen = deadthLen;
            m_CdLen = cdLen;
        }

        /// <summary>
        /// Start to enter the ready status
        /// 开始准备
        /// </summary>
        public void StartReady(TimeSliceDefine define)
        {
            Reset(define);
            m_ReadyTicks = m_GetGetNowTicks();
        }

        /// <summary>
        /// Exit ready status
        /// 完成 准备
        /// </summary>
        public bool FinishReady()
        {
            if (!InReady) return false;
            m_ReadyLen = m_GetGetNowTicks() - m_ReadyTicks;
            return true;
        }

        /// <summary>
        /// Start to enter the doing status
        /// 准备 -> 进行中
        /// </summary>
        public void StartDoing()
        {
            FinishReady();
        }

        /// <summary>
        /// Start to enter the doing status
        /// 准备 -> 进行中
        /// </summary>
        /// <param name="doingLen">-1代表无限持续,0代表不持续</param>
        public void StartDoing(long doingLen)
        {
            if (!FinishReady()) return;
            m_DoingLen = doingLen;
        }

        /// <summary>
        /// Exit doing status
        /// 完成 进行中
        /// </summary>
        public bool FinishDoing()
        {
            if (!InDoing) return false;
            m_DoingLen = m_GetGetNowTicks() - m_ReadyLen - m_ReadyTicks;
            return true;
        }

        /// <summary>
        /// Start to enter the death status
        /// 进行中 -> 消亡
        /// </summary>
        public void StartDeadth()
        {
            FinishDoing();
        }

        /// <summary>
        /// Start to enter the death status
        /// 进行中 -> 消亡
        /// </summary>
        /// <param name="deadthLen">-1代表无限持续,0代表不持续</param>
        public void StartDeadth(long deadthLen)
        {
            if (!FinishDoing()) return;
            m_DeadthLen = deadthLen;
        }

        /// <summary>
        /// Exit death status
        /// 完成 消亡
        /// </summary>
        public bool FinishDeath()
        {
            if (!InDeadth) return false;
            m_DeadthLen = m_GetGetNowTicks() - m_DoingLen - m_ReadyLen - m_ReadyTicks;
            return true;
        }

        /// <summary>
        /// Start to enter the cd status
        /// 消亡 -> CD
        /// </summary>
        public void StartCd()
        {
            FinishDeath();
        }

        /// <summary>
        /// Start to enter the cd status
        /// 消亡 -> CD
        /// </summary>
        /// <param name="cdLen">-1代表无限持续,0代表不持续</param>
        public void StartCd(long cdLen)
        {
            if (!FinishDeath()) return;
            m_CdLen = cdLen;
        }

        /// <summary>
        /// Exit cd state
        /// 结束CD
        /// </summary>
        public bool FinishCd()
        {
            if (!InCd) return false;
            m_DoingLen = m_GetGetNowTicks() - m_DoingLen - m_ReadyLen - m_ReadyTicks;
            m_ReadyTicks = 0;
            return true;
        }

        /// <summary>
        /// Reset
        /// 重置
        /// </summary>
        public void Reset()
        {
            m_ReadyTicks = 0;
        }

        /// <summary>
        /// Reset
        /// 重置
        /// </summary>
        public void Reset(TimeSliceDefine reset)
        {
            m_ReadyTicks = 0;
            m_ReadyLen = reset.ReadyLen;
            m_DoingLen = reset.DoingLen;
            m_DeadthLen = reset.DeadthLen;
            m_CdLen = reset.CdLen;
        }

        /// <summary>
        /// Clone
        /// 克隆
        /// </summary>
        /// <returns></returns>
        public TimeSlice Clone()
        {
            var rs = new TimeSlice(m_ReadyLen, m_DoingLen, m_DeadthLen, m_CdLen, m_GetGetNowTicks) {m_ReadyTicks = m_ReadyTicks};
            return rs;
        }

        private bool CheckPassedIn(long passed, long len)
        {
            if (passed < 0) return false;
            return len < 0 || passed < len;
        }

        //static

        private static long GetNowTicks()
        {
            return DateTime.Now.Ticks;
        }

        private static DateTimeUtil.FuncGetNowTicks m_DefaultGetNowTicks;

        public static DateTimeUtil.FuncGetNowTicks DefaultGetNowTicks => m_DefaultGetNowTicks ?? GetNowTicks;

        public static void SetDefaultGetNowTicks(DateTimeUtil.FuncGetNowTicks defaultGetNowTicks)
        {
            m_DefaultGetNowTicks = defaultGetNowTicks;
        }
    }
}