/*
 * 作者:吴隆健
 * 日期: 2019-04-23
 * 时间: 13:22
 * 
 */

using System;

namespace MrWu.Time {

    /// <summary>
    /// 
    /// </summary>
	public enum TimeStyle {
        /// <summary>
        /// 年月日时分秒
        /// </summary>
        yyyyMMddhhmmss,
        /// <summary>
        /// 年月日时分秒毫秒
        /// </summary>
        yyyyMMddhhmmssffff,
        /// <summary>
        /// 年月日
        /// </summary>
        yyyyMMdd,
        /// <summary>
        /// 月日时分秒
        /// </summary>
        MMddhhmmss,
        /// <summary>
        /// 月日时分秒毫秒
        /// </summary>
        MMddhhmmssfff,
        /// <summary>
        /// 时分秒
        /// </summary>
        hhmmss,
        /// <summary>
        /// 毫秒值
        /// </summary>
        fff
    }

    /// <summary>
    /// 自定义时间结构_为了结构体对齐
    /// </summary>
    public struct CustomTime {

        /// <summary>
        /// 年份的时间基数
        /// </summary>
        public const int weight = 1000000;

        /// <summary>  年                         今年第几天*24 + 今天的小时
        /// 小时部分 = tm.year % 100 * 100 0000 + tm.DayOfYear* 1440 + tm.hour
        /// </summary>
        public int hours;

        /// <summary>
        /// 分钟部分
        /// </summary>
        public byte minute;

        /// <summary>
        /// 秒钟部分
        /// </summary>
        public byte second;

        /// <summary>
        /// 毫秒部分
        /// </summary>
        public ushort millisecond;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hours"></param>
        /// <param name="minute"></param>
        /// <param name="second"></param>
        /// <param name="millisecond"></param>
        public CustomTime(int hours, byte minute = 0, byte second = 0, ushort millisecond = 0) {
            this.hours = hours;
            this.minute = minute;
            this.second = second;
            this.millisecond = millisecond;
        }

        /// <summary>
        /// 哪年
        /// </summary>
        public int year {
            get {
                return hours / weight + 2000;
            }
        }

        /// <summary>
        /// 这一天是这一年当中的第几天
        /// </summary>
        public int dayofyear {
            get {
                return hours % weight / 24;
            }
        }

        /// <summary>
        /// 几月
        /// </summary>
        public int month {
            get {
                return TimeConvert.GetMonth(year, dayofyear);
            }
        }

        /// <summary>
        /// 几号 
        /// </summary>
        public int day {
            get {
                return TimeConvert.GetDay(year, dayofyear);
            }
        }

        /// <summary>
        /// 几点
        /// </summary>
        public int hour {
            get {
                return hours % weight % 24;
            }
        }
    }

    /// <summary>
    /// 时间转换
    /// </summary>
    public static class TimeConvert {

        /// <summary>
        /// 闰年个个月的天数
        /// </summary>
        private readonly static byte[] _leapMonthDay = new byte[] {
             31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };

        /// <summary>
        /// 普通年个个月的天数
        /// </summary>
        private readonly static byte[] _noleapMonthDay = new byte[] {
            31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };

        /// <summary>
        /// 一年中每月的天数，润年在2月+1天
        /// </summary>
        public static byte[] MonthDay(int year) {
            bool isleap = IsLeapYear(year);
            if (isleap)
                return _leapMonthDay;
            return _noleapMonthDay;
        }

        /// <summary>
        /// 获取这一天是这一年的几月
        /// </summary>
        /// <param name="year">哪年</param>
        /// <param name="dayofyear">这一年的第几天</param>
        /// <returns></returns>
        public static int GetMonth(int year, int dayofyear) {
            bool isleap = IsLeapYear(year);
            byte[] bts = MonthDay(year);

            int allday = 0;
            int len = bts.Length;
            for (int i = 0; i < len; i++) {
                allday += bts[i];
                if (dayofyear <= allday)
                    return i + 1;
            }
            throw new ArgumentOutOfRangeException("dayofyear < 1 or dayofyear > 365 or 366");
        }

        /// <summary>
        /// 获取这一天是这年的几日
        /// </summary>
        /// <param name="year"></param>
        /// <param name="dayofyear"></param>
        /// <returns></returns>
        public static int GetDay(int year, int dayofyear) {
            //Console.WriteLine(year + "," + dayofyear);

            int month = GetMonth(year, dayofyear);
            byte[] bts = MonthDay(year);

            int allday = 0;
            for (int i = 1; i < month; i++) {
                allday += bts[i - 1];
            }
            return dayofyear - allday;
        }

        /// <summary>
        /// 某年是否是闰年
        /// </summary>
        /// <param name="year"></param>
        /// <returns></returns>
        public static bool IsLeapYear(int year) {
            return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
        }

        /// <summary>
        /// 时间戳起始时间
        /// </summary>
        private static readonly DateTime _startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, 0));

        /// <summary>
        /// 时间转13位时间戳
        /// </summary>
        /// <param name="time">时间</param>
        /// <param name="bflag">true 表示10位时间戳 false 表示13位时间戳</param>
        /// <returns>时间戳</returns>
        public static long DateTimeToTimeStamp(DateTime time, bool bflag = true) {
            TimeSpan ts = time - _startTime;
            if (bflag)
                return Convert.ToInt64(ts.TotalSeconds);
            else
                return Convert.ToInt64(ts.TotalMilliseconds);
        }

        /// <summary>
        /// 时间戳转时间
        /// </summary>
        /// <param name="timeStamp">时间戳字符串</param>
        /// <returns>时间</returns>
        public static DateTime TimeStampToDateTime(string timeStamp) {

            if (timeStamp.Length == 10)
                timeStamp += "0000000";
            else if (timeStamp.Length == 13)
                timeStamp += "0000";

            long ltime = long.Parse(timeStamp);

            //Console.WriteLine(ltime);

            TimeSpan toNow = new TimeSpan(ltime);

            return _startTime.Add(toNow);
        }

        /// <summary>
        /// 自定义时间格式转DateTime
        /// </summary>
        /// <param name="ct">自定义时间格式</param>
        /// <returns></returns>
        public static DateTime CustomTimeToDateTime(CustomTime ct) {
            int year = ct.year;
            int month = ct.month;
            int day = ct.day;
            int hour = ct.hour;
            return new DateTime(year, month, day, hour, ct.minute, ct.second, ct.millisecond);
        }

        /// <summary>
        /// 时间 转自定义时间格式
        /// </summary>
        /// <param name="time">时间</param>
        /// <returns></returns>
        public static CustomTime DateTimeToCustomTime(DateTime time) {
            int year = time.Year;
            int dayofyear = time.DayOfYear;
            int hour = time.Hour;
            byte minute = (byte)time.Minute;
            byte second = (byte)time.Second;
            ushort millisecond = (ushort)time.Millisecond;

            hour = year % 100 * CustomTime.weight + dayofyear * 24 + hour;
            return new CustomTime(hour, minute, second, millisecond);
        }

        /// <summary>
        /// 时间转字符串
        /// </summary>
        /// <param name="time">时间</param>
        /// <param name="style">字符串格式</param>
        /// <param name="dateChar">年月日以什么字符串链接</param>
        /// <param name="timeChar">时分秒以什么字符串链接</param>
        /// <param name="datetimelink">日期部分与时间部分以什么字符链接</param>
        /// <returns></returns>
        public static string TimeToString(this DateTime time, TimeStyle style = TimeStyle.yyyyMMddhhmmss, string dateChar = "-", string timeChar = ":", string datetimelink = " ") {
            string format = string.Empty;
            string dateCharlink = string.Empty;
            if (dateChar != string.Empty)
                dateCharlink += dateChar;
            string timeCharlink = string.Empty;
            if (timeChar != string.Empty)
                timeCharlink += timeChar;
            string dtlink = string.Empty;
            if (datetimelink != string.Empty)
                dtlink += datetimelink;

            switch (style) {
                case TimeStyle.yyyyMMddhhmmssffff:
                    format = string.Format("yyyy{0}MM{0}dd{2}HH{1}mm{1}ss{1}fff", dateCharlink, timeCharlink, dtlink);
                    break;
                case TimeStyle.yyyyMMddhhmmss:
                    format = string.Format("yyyy{0}MM{0}dd{2}HH{1}mm{1}ss", dateCharlink, timeCharlink, dtlink);
                    break;
                case TimeStyle.yyyyMMdd:
                    format = string.Format("yyyy{0}MM{0}dd", dateCharlink);
                    break;
                case TimeStyle.MMddhhmmss:
                    format = string.Format("MM{0}dd{1}HH{2}mm{2}ss", dateCharlink, dtlink, timeCharlink);
                    break;
                case TimeStyle.MMddhhmmssfff:
                    format = string.Format("MM{0}dd{1}HH{2}mm{2}ss{2}fff", dateCharlink, dtlink, timeCharlink);
                    break;
                case TimeStyle.hhmmss:
                    format = string.Format("HH{0}mm{0}ss", timeCharlink);
                    break;
                case TimeStyle.fff:
                    format = string.Format("fff");
                    break;
            }
            return time.ToString(format);
        }

        /// <summary>
        /// 获取到第二天的时间差
        /// </summary>
        /// <returns></returns>
        public static TimeSpan GetTomorrowDifference()
        {
            var tom = DateTime.Now.Date.AddDays(1);
            return tom - DateTime.Now;
        }
    }
}
