import * as CryptoES from 'crypto-es';
import * as NumberUtil from './number.ts';

Object.assign(String.prototype, {
    firstToLowerCase: function (this: string): string {
        return this.substring(0, 1).toLowerCase() + this.substring(1);
    },
    firstToUpperCase: function (this: string): string {
        return this.substring(0, 1).toUpperCase() + this.substring(1);
    },
    format: function (this: string, args?: any): string {
        if (!(args instanceof Array)) {
            args = arguments;
        }
        let s = this as any as string;
        for (let i = 0; i < (args as any).length; i++) {
            s = s.replace('{' + i + '}', (args as any)[i]);
        }
        return s;
    },
    toCharArray: function (this: string): string[] {
        let array = [] as string[];
        for (let i = 0; i < this.length; i++) {
            array.push(this.charAt(i));
        }
        return array;
    },
    // 部分浏览器没有这个方法支持
    replaceAll: function (this: string, regex: string | RegExp, replacement: string): string {
        if (typeof regex === 'string') {
            regex = new RegExp(regex, "gm");
        }
        return (this as any as string).replace(regex as any, replacement as any);
    },
    allIndexOf: function (this: string, searchString: string, position?: number): number[] {
        let indexes = [] as number[];
        let index = this.indexOf(searchString, position as any);
        while (index >= 0) {
            indexes.push(index);
            index = this.indexOf(searchString, index + searchString.length);
        }
        return indexes;
    },
    splitToIntArray: function (this: string, separator: string | RegExp): number[] {
        const parts = this.split(separator);
        const result: number[] = new Array(parts.length);
        for (let i = 0; i < parts.length; i++) {
            result[i] = parseInt(parts[i], 10);
        }
        return result;
    },
    /**
     * 按大写字母分割
     * @param byNumber 是否按数字也分割
     * @returns {string[]}
     */
    splitByUpperCaseLetter: function (this: string, byNumber?: boolean): string[] {
        let array = [] as string[];
        let s = '';
        for (let c of this as any as string) {
            if (!s) {
                s = c;
            } else if (/^[A-Z]$/.test(c)) {
                array.push(s);
                s = c;
            } else if (byNumber && /^[0-9]$/.test(c)) {
                if (/^[0-9]$/.test(s[s.length - 1])) {
                    s += c;
                } else {
                    array.push(s);
                    s = c;
                }
            } else {
                s += c;
            }
        }
        if (s) {
            array.push(s);
        }
        return array;
    },
    wildcardMatches: function (this: string, ...patterns: string[]): boolean {
        for (let pattern of patterns) {
            const regexPattern = pattern.split('*').map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('.*');
            const regex = new RegExp(`^${regexPattern}$`);
            if (regex.test(this as any as string)) {
                return true;
            }
        }
        return false;
    },
});

export function toJson(value: any): string {
    return JSON.stringify(value);
}

export function parseJson<T = any>(json: string): T {
    return JSON.parse(json);
}

export function md5(s: string): string {
    return CryptoES.MD5(s).toString();
}

export const base64 = {
    encode: (s: string) => {
        return CryptoES.Base64.stringify(CryptoES.Utf8.parse(s));
    },
    decode: (s: string) => {
        return CryptoES.Base64.parse(s).toString(CryptoES.Utf8);
    },
}

export function uuid32(): string {
    let url = URL.createObjectURL(new Blob()).toString();
    URL.revokeObjectURL(url);
    return url.substring(url.lastIndexOf('/') + 1).replaceAll('-', '');
}

export function random(length: number, chars: string = 'abcdefghijklmnopqrstuvwxyz0123456789'): string {
    if (length >= 0) {
        let s = '';
        while (s.length < length) {
            s += chars.charAt(NumberUtil.randomInt(0, chars.length));
        }
        return s;
    }
    return '';
}

export function getCapacityCaption(capacity: number, scale: number = 0): string {
    const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    let series: number;
    for (series = 0; series < units.length; series++) {
        if (capacity >= 1024) {
            capacity = (capacity / 1024).halfUp(scale);
        } else {
            break;
        }
    }
    return capacity + units[series];
}

export function getPixelString(value: number | string, containerValue?: number | string): string {
    if (typeof value === 'number') {
        return value + 'px';
    } else if (typeof value === 'string') {
        if (value.endsWith('%')) {
            if (containerValue) {
                let percent = parseFloat(value.substring(0, value.length - 1));
                let containerNumber = getPixelNumber(containerValue);
                if (!isNaN(percent) && !isNaN(containerNumber)) {
                    return (containerNumber * percent / 100) + 'px';
                }
            }
        } else {
            return value;
        }
    }
    return undefined;
}

export function getPixelNumber(value: number | string): number {
    if (typeof value === 'number') {
        return value;
    }
    if (typeof value === 'string') {
        if (value.toLowerCase().endsWith('px')) {
            return parseInt(value.substring(0, value.length - 2));
        }
        if (value.toLowerCase().endsWith('rem')) {
            return parseInt(value.substring(0, value.length - 3)) * 16;
        }
    }
    return NaN;
}

export function idCard(idCardNo: string): { birthday: string; male: boolean; serialNo: string; } {
    let birthday: string;
    let gender: string;
    if (idCardNo.length === 15) {
        birthday = '19' + idCardNo.substring(6, 12);
        gender = idCardNo.substring(14, 15);
    } else {
        birthday = idCardNo.substring(6, 14);
        gender = idCardNo.substring(16, 17);
    }
    birthday = birthday.substring(0, 4) + '-' + birthday.substring(4, 6) + '-' + birthday.substring(6, 8);
    return {
        birthday: birthday,
        male: parseInt(gender as any) % 2 === 1,
        serialNo: idCardNo
    };
}

export function matchesForEach(content: string, keyword: string): boolean {
    if (!keyword) {
        return true;
    }
    if (!content) {
        return false;
    }
    let index = 0;
    for (let c of keyword.toCharArray()) {
        index = content.indexOf(c, index);
        if (index < 0) {
            return false;
        }
    }
    return true;
}

export function matchesWildcard(s: string, pattern: string): boolean {
    const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
    return regex.test(s);
}
