

//#region ******************************** 类型声明 ********************************
export type Callback<T = any> = (err: Error | null, data: T) => void;
export type Constructor<T = unknown> = new (...args: any[]) => T;
export type ObjectKeyAsValue<T> = { readonly [K in keyof T]: K; }
export type KeyArrayAsValue<T extends string> = { readonly [V in T]: V; }
//#endregion

/**
 * 销毁接口
 */
export interface IDispose {
    Dispose(): void;
}

/** 自动销毁附着对象 */
export interface IDisposeAttachTarget {
    AutoDispose(toDispose: IDispose): void;
}

//#region ******************************** 辅助函数 ********************************
export function CreateObjectKeyAsValueMap<T extends Object>(obj: T): ObjectKeyAsValue<T> {
    let keys = Object.keys(obj);
    let ret: ObjectKeyAsValue<T> = Object.create(null);
    keys.forEach(key => {
        ret[key] = key;
    })
    keys = undefined;
    return ret;
}

export function CreateIntEnum<T extends string>(states: Array<T>, stateVal: number = 1): { [V in T]: number } {
    let ret = Object.create(null);
    states.forEach(state => {
        ret[state] = stateVal;
        stateVal = stateVal + 1;
    })
    return ret;
}



export function MaxValueOf<T extends string>(intEnum: { [k: string]: number }) {
    let init = false;
    let maxVal = 0;
    for (let k in intEnum) {
        if (!init) {
            maxVal = intEnum[k];
        } else {
            maxVal = (intEnum[k] > maxVal) ? intEnum[k] : maxVal;
        }
    }
    return maxVal;
}

export function onceCall(cb: () => void, cnt: number = 1) {
    return () => {
        if (cnt <= 0) {
            return;
        };
        --cnt;
        if (cnt === 0) {
            cb()
        }
    };
}

export function onceArgCall<T>(cb: (p: T) => void, cnt: number) {
    return (p: T) => {
        if (cnt <= 0) {
            return;
        };
        --cnt;
        if (cnt === 0) {
            cb(p)
        }
    }
}
//#endregion

/**
 * 尾插入排序（从小到大）
 * @param list 
 * @param compare 
 * @returns 
 */
export function insertionSort<T>(list: T[], compare: (a: T, b: T) => number) {
    for (let length = list.length, i = 1; i < length; i++) {
        let j, value: T = list[i]
        for (j = i - 1; j >= 0; j--) {
            if (compare(list[j], value) <= 0) break
            list[j + 1] = list[j]
        }
        list[j + 1] = value
    }
    return list
}

/**
 * 已排好序(从小到大)的数组，获得 value 应该排在哪个位置（有相同值的情况下，排最左边）
 * @param list 
 * @param value 
 * @param compare 
 * @returns 
 */
export function sortedIndex<T, V>(list: T[], value: V, compare: (a: T, b: V) => number): number {
    let low = 0, high = list.length
    while (low < high) {
        let mid = (low + high) >>> 1
        if (compare(list[mid], value) < 0) low = mid + 1
        else high = mid
    }
    return low
}

/**
 * 已排好序(从小到大)的数组，获得 value 应该排在哪个位置（有相同值的情况下，排最右边）
 * @param list 
 * @param value 
 * @param compare 
 * @returns 
 */
export function sortedLastIndex<T>(list: T[], value: T, compare: (a: T, b: T) => number): number {
    let low = 0, high = list.length
    while (low < high) {
        let mid = (low + high) >>> 1
        if (compare(list[mid], value) <= 0) low = mid + 1
        else high = mid
    }
    return high
}

/**
 * 快速删除数组元素（不保证删除后的顺序）
 * @param arr 
 * @param idx 
 */
export function fastRemoveIndex(arr: any[], idx: number) {
    if (idx >= 0 && idx < arr.length) {
        arr[idx] = arr[arr.length - 1];
        arr.length--;
    }
}

/**
 * 拷贝内容到剪切板
 * @param copyStr 内容
 */
export function copyToClipboardForWeb(copyStr: string, callback: (err?: Error) => void): void {
    const el = document.createElement('textarea');
    el.value = copyStr;

    // Prevent keyboard from showing on mobile
    el.setAttribute('readonly', '');
    //el.style.contain = 'strict';
    el.style.position = 'absolute';
    el.style.left = '-9999px';
    el.style.fontSize = '12pt'; // Prevent zooming on iOS

    const selection = getSelection()!;
    let originalRange;
    if (selection.rangeCount > 0) {
        originalRange = selection.getRangeAt(0);
    }

    document.body.appendChild(el);
    el.select();

    // Explicit selection workaround for iOS
    el.selectionStart = 0;
    el.selectionEnd = copyStr.length;

    let isSuccess = false;
    try {
        isSuccess = document.execCommand('copy');
    } catch (err) {
        isSuccess = false;
    } finally {
        document.body.removeChild(el);
        if (originalRange) {
            selection.removeAllRanges();
            selection.addRange(originalRange);
        }
    }

    /** 返回结果 */
    if (isSuccess) {
        callback();
    } else {
        callback(new Error("执行剪切板拷贝命令出错"))
    }
}