export const DATE_PATTERNS = {
    dateTime: 'yyyy-MM-dd HH:mm:ss',
    date: 'yyyy-MM-dd',
    time: 'HH:mm:ss',
    timeMillisecond: 'HH:mm:ss.S',
    timeMinute: 'HH:mm',
    dateMinute: 'yyyy-MM-dd HH:mm',
    dateMonth: 'yyyy-MM',
}

Object.assign(Date.prototype, {
    format: function (this: Date, pattern: string): string {
        let date = {
            'M+': this.getMonth() + 1, // 月份
            'd+': this.getDate(), // 日
            'H+': this.getHours(), // 小时
            'm+': this.getMinutes(), // 分
            's+': this.getSeconds(), // 秒
            'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
            'S': this.getMilliseconds(), // 毫秒
        } as any;
        if (/(y+)/.test(pattern as any)) {
            pattern = (pattern as any).replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
        }
        for (let key in date) {
            if (new RegExp('(' + key + ')').test(pattern as any)) {
                let prefix = key === 'S' ? '000' : '00';
                pattern = (pattern as any).replace(RegExp.$1,
                    RegExp.$1.length === 1 ? date[key] : (prefix + date[key]).substr(('' + date[key]).length));
            }
        }
        return pattern;
    },
    formatDateTime: function (this: Date): string {
        return this.format(DATE_PATTERNS.dateTime);
    },
    formatDate: function (this: Date): string {
        return this.format(DATE_PATTERNS.date);
    },
    formatTime: function (this: Date): string {
        return this.format(DATE_PATTERNS.time);
    },
    formatTimeMillisecond: function (this: Date): string {
        return this.format(DATE_PATTERNS.timeMillisecond);
    },
    formatTimeMinute: function (this: Date): string {
        return this.format(DATE_PATTERNS.timeMinute);
    },
    formatDateMinute: function (this: Date): string {
        return this.format(DATE_PATTERNS.dateMinute);
    },
    formatDateMonth: function (this: Date): string {
        return this.format(DATE_PATTERNS.dateMonth);
    },
    plusMilliseconds: function (this: Date, milliseconds: number): Date {
        return new Date(this.getTime() + (milliseconds as any));
    },
    plusSeconds: function (this: Date, seconds: number): Date {
        return this.plusMilliseconds((seconds as any) * 1000);
    },
    plusMinutes: function (this: Date, minutes: number): Date {
        return this.plusSeconds((minutes as any) * 60);
    },
    plusHours: function (this: Date, hours: number): Date {
        return this.plusMinutes((hours as any) * 60);
    },
    plusDays: function (this: Date, days: number): Date {
        return this.plusHours((days as any) * 24);
    },
    plusMonths: function (this: Date, months: number): Date {
        let year = this.getFullYear();
        let month = this.getMonth();
        year += (months as any) / 12;
        month += (months as any) % 12;
        let date = new Date(this);
        date.setFullYear(year);
        date.setMonth(month);
        return date;
    },
    plusYears: function (this: Date, years: number): Date {
        let date = new Date(this);
        date.setFullYear(this.getFullYear() + (years as any));
        return date;
    },
    applyTime: function (this: Date, hours: number, minutes: number, seconds: number, milliseconds: number): void {
        this.setHours(hours as any);
        this.setMinutes(minutes as any);
        this.setSeconds(seconds as any);
        this.setMilliseconds(milliseconds as any);
    },
});

export function toDate(value: any): Date | undefined {
    if (value instanceof Date) {
        return value;
    } else if (typeof value === 'string' || typeof value === 'number') {
        return new Date(value);
    }
    return undefined;
}

export function format(date: Date | string | number, pattern: string): string | undefined {
    if (typeof date === 'number' || typeof date === 'string') {
        date = new Date(date);
    }
    if (date instanceof Date) {
        return date.format(pattern);
    }
    return undefined;
}

export function formatDate(date: Date | string | number): string | undefined {
    return format(date, DATE_PATTERNS.date);
}

export function formatTime(date: Date | string | number): string | undefined {
    return format(date, DATE_PATTERNS.time);
}

export function formatTimeMinute(date: Date | string | number): string | undefined {
    return format(date, DATE_PATTERNS.timeMinute);
}

export function formatDateTime(date: Date | string | number): string | undefined {
    return format(date, DATE_PATTERNS.dateTime);
}

export function formatDateMinute(date: Date | string | number): string | undefined {
    return format(date, DATE_PATTERNS.dateMinute);
}

export const PERMANENT_DATE_TEXT = '永久';

export function formatPermanentableDate(date: PermanentableDate): string | undefined {
    if (date) {
        if (date.permanent) {
            return this.PERMANENT_DATE_TEXT;
        }
        if (date.value) {
            return new Date(date.value).formatDate();
        }
    }
    return undefined;
}

export function dateToMonth(date: Date | string): string | undefined {
    if (date instanceof Date) {
        return format(date, DATE_PATTERNS.dateMonth);
    }
    if (date) {
        return date.substring(0, date.lastIndexOf('-'));
    }
    return date;
}

export function createDate(year: number, month?: number, day?: number, hour?: number, minute?: number, second?: number, millis?: number): Date {
    let date = new Date();
    date.setFullYear(year, (month || 1) - 1, (day || 1));
    date.setHours(hour || 0, minute || 0, second || 0, millis || 0);
    return date;
}

export function getDaysOfMonth(year: number, month: number): number {
    if (month === 2 && ((year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0))) {
        return 29;
    }
    return [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
}

export function betweenDays(earlierDate: Date | string | number, laterDate: Date | string | number): number {
    earlierDate = new Date(earlierDate);
    laterDate = new Date(laterDate);
    return (laterDate.getTime() - earlierDate.getTime()) / 1000 / 60 / 60 / 24;
}

export function betweenDate(earlierDate: Date | string | number, laterDate: Date | string | number): {
    years: number;
    months: number;
    days: number;
    toMonthString: () => string;
    toString: () => string;
} {
    earlierDate = new Date(earlierDate);
    let year1 = earlierDate.getFullYear();
    let month1 = earlierDate.getMonth() + 1;
    let day1 = earlierDate.getDate();

    laterDate = new Date(laterDate);
    let year2 = laterDate.getFullYear();
    let month2 = laterDate.getMonth() + 1;
    let day2 = laterDate.getDate();

    let revisedMonths = 0;
    let revisedYears = 0;

    let days: number;
    if (day2 >= day1) {
        days = day2 - day1;
    } else {
        revisedMonths = -1;
        days = getDaysOfMonth(year1, month1) + day2 - day1;
    }

    let months: number;
    if (month2 + revisedMonths >= month1) {
        months = month2 + revisedMonths - month1;
    } else {
        revisedYears = -1;
        months = 12 + month2 + revisedMonths - month1;
    }

    let years = year2 + revisedYears - year1;

    return {
        years,
        months,
        days,
        toMonthString(): string {
            let s = '';
            if (this.years > 0) {
                s += this.years + '年';
            }
            s += this.months + '个月';
            return s;
        },
        toString(): string {
            let s = '';
            if (this.years > 0) {
                s += this.years + '年';
            }
            if (this.months > 0) {
                s += this.months + '个月';
            } else if (this.years > 0 && this.days > 0) {
                s += '零';
            }
            if (this.days > 0) {
                s += this.days + '天';
            }
            return s;
        }
    };
}

export function today(): Date {
    let date = new Date();
    date.setHours(0, 0, 0, 0);
    return date;
}

export function monthsToYearMonth(months: number | string): string {
    if (typeof months === 'string') {
        months = parseInt(months);
    }
    if (!isNaN(months)) {
        let year = Math.floor(months / 12);
        let month = months % 12;
        return (year >= 1 ? year + '年' : '') + month + '个月';
    }
}
