/**
 * 记录的一些方法和设置
 */

import { RENDERER_TYPE } from "../const";
import { mapPremultipliedBlendModes } from "./mapPremultipliedBlendModes";
import { createCanvas, getEnv } from "./tbminiAdapte";

export * from './twiddle';
export { default as toDisplayDataURL } from "./toDisplayDataURL";

export * from "./getTintedTexture";
export * from "./mapWebGLBlendModes";
export * from "./mapCanvasBlendModes";

// export * from "./DrawAllToCanvas";

export * from "./tbminiAdapte";

export const premultiplyBlendMode = mapPremultipliedBlendModes();

let nextUid = 0;

/**
 * 获得唯一id
 * @function uid
 * @return {number} 唯一的id
 */
export function uid(): number {
    return ++nextUid;
}

let backupCanvasCtx: CanvasRenderingContext2D
/**
 * 各种使用需要的canvas
 * 比如取canvas上下文创建对象，渐变色等
 * 像素碰撞检测时
 * 淘宝小程序内不要用任何相关的先
 */
// export const backupCanvas: HTMLCanvasElement = createCanvas() //= document.createElement("canvas")
export function getBackupCanvasCtx() {
    if (!backupCanvasCtx) {
        var canvas = createCanvas();
        canvas.width = 1;
        canvas.height = 1;
        backupCanvasCtx = canvas.getContext("2d");
    }
    return backupCanvasCtx
}
/**
 * 创建渐变色
 * getGradientColor([0, 0, 300, 0], [[0, "#ff0000", 1],[0.5, "#00ff00", 1],[1, "#0000ff", 1]])
 * @param points 四个数字表示线性渐变参考createLinearGradient，六个数字表示径向渐变参考createRadialGradient
 * @param colors  [系数, #式颜色值, 透明度]的数组，比如[[0, "#ff0000", 1],[0.5, "#00ff00", 1],[1, "#0000ff", 1]]
 */
export function getGradientColor(points: number[], colors: [number, string, number][]): any {
    let colorObj: any;
    let ctx = getBackupCanvasCtx()//backupCanvas.getContext("2d");
    if (points.length == 4) {
        colorObj = ctx.createLinearGradient(points[0], points[1], points[2], points[3]);
    } else {
        colorObj = ctx.createRadialGradient(points[0], points[1], points[2], points[3], points[4], points[5]);
    }
    for (let i = 0, l = colors.length; i < l; i++) {
        colorObj.addColorStop(colors[i][0], getRGBA(colors[i][1], colors[i][2]));
    }
    return colorObj;
}
/**
 * 创建重复纹理
 * @param image 
 */
export function getCanvasBitmapStyle(image: any): any {
    let ctx = getBackupCanvasCtx()//backupCanvas.getContext("2d");
    return ctx.createPattern(image, "repeat");
}
/**
 * 十六进制颜色转rgb数组
 * @function hex2rgb
 * @param {number} hex -转换的十六进制颜色
 * @param  {number[]} [out=[]] 输出数组，
 * @return {number[]} [R, G, B]颜色 每项范围0到1
 */
export function hex2rgb(hex: number, out?: number[] | Float32Array): number[] | Float32Array {
    out = out || [];
    out[0] = ((hex >> 16) & 0xFF) / 255;
    out[1] = ((hex >> 8) & 0xFF) / 255;
    out[2] = (hex & 0xFF) / 255;
    return out;
}

/**
 * 十六进制颜色转字符串
 * 0xffffff转"#ffffff"
 * @function hex2string
 * @param {number} hex - 转换的十六进制颜色
 * @return {string} 输出的字符串颜色.
 */
export function hex2string(hex: any): string {
    hex = hex.toString(16);
    hex = '000000'.substr(0, 6 - hex.length) + hex;
    return `#${hex}`;
}

/**
 * 字符串颜色转十六进制
 * "#ffffff"或"0xffffff"转0xffffff
 * @param {string} string 转换的字符串颜色
 * @return {number} 输出的颜色数值
 */
export function string2hex(string: string) {
    if (string.indexOf("#") == 0) {
        string = string.replace("#", "0x")
    }
    return parseInt(string)//到时改成+号
}

/**
 * 数组[R, G, B]转换为十六进制数值
 * rgb范围时0到1，和着色器中的一致
 * @function rgb2hex
 * @param {number[]} rgb -数组
 * @return {number} 十六进制颜色
 */
export function rgb2hex(rgb: number[]): number {
    return (((rgb[0] * 255) << 16) + ((rgb[1] * 255) << 8) + (rgb[2] * 255 | 0));
}

/**
 * 通过字符串颜色值和一个透明度值生成RGBA值
 * 用于canvas的api，
 * @method getRGBA
 * @param {string} color 字符串的颜色值,如:#33ffee
 * @param {number} alpha 0-1区间的一个数据 0完全透明 1完全不透明
 * @return {string}
 */
export function getRGBA(color: string, alpha: number): string {
    if (color.indexOf("0x") == 0) {
        color = color.replace("0x", "#");
    }
    //暂没考虑#ff这种
    if (color.length < 7) {
        color = "#000000";
    }
    if (alpha != 1) {
        let r = parseInt("0x" + color.substr(1, 2));
        let g = parseInt("0x" + color.substr(3, 2));
        let b = parseInt("0x" + color.substr(5, 2));
        color = "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
    }
    return color;
}

/**
 * Regexp for data URI.
 * Based on: {@link https://github.com/ragingwind/data-uri-regex}
 *
 * @static
 * @constant
 * @example data:image/png;base64
 */
const DATA_URI: any = /^\s*data:(?:([\w-]+)\/([\w+.-]+))?(?:;charset=([\w-]+))?(?:;(base64))?,(.*)/i;
/**
 * Split a data URI into components. Returns undefined if
 * parameter `dataUri` is not a valid data URI.
 *
 * @memberof utils
 * @function decomposeDataUri
 * @param {string} dataUri - the data URI to check
 * @return {utils~DecomposedDataUri|undefined} The decomposed data uri or undefined
 */
export function decomposeDataUri(dataUri) {
    const dataUriMatch = DATA_URI.exec(dataUri);
    if (dataUriMatch) {
        return {
            mediaType: dataUriMatch[1] ? dataUriMatch[1].toLowerCase() : undefined,
            subType: dataUriMatch[2] ? dataUriMatch[2].toLowerCase() : undefined,
            charset: dataUriMatch[3] ? dataUriMatch[3].toLowerCase() : undefined,
            encoding: dataUriMatch[4] ? dataUriMatch[4].toLowerCase() : undefined,
            data: dataUriMatch[5],
        };
    }
    return undefined;
}

/**
 * Regexp for image type by extension.
 *
 * @static
 * @constant
 * @type {RegExp|string}
 * @example `image.png`
 */
const URL_FILE_EXTENSION: any = /\.(\w{3,4})(?:$|\?|#)/i;
/**
 * 根据图片路径获取图片扩展名
 * @memberof utils
 * @function getUrlFileExtension
 * @param {string} url - the image path
 * @return {string|undefined} image extension
 */
export function getUrlFileExtension(url: string): string {
    const extension = URL_FILE_EXTENSION.exec(url);
    if (extension) {
        return extension[1].toLowerCase();
    }
    return undefined;
}

/**
 * 获取正负号标记
 * 0 +1 -1
 * @memberof utils
 * @function sign
 * @param {number} n 
 * @returns {number} 
 */
export function sign(n: number): number {
    if (n === 0) return 0;
    return n < 0 ? -1 : 1;
}

/**
 * 预乘十六进制颜色与透明度
 * 着色器只需传一个参数
 * @param tint 
 * @param alpha 
 */
export function premultiplyTint(tint: number, alpha: number): number {
    if (alpha === 1.0) {
        return (alpha * 255 << 24) + tint;
    }
    if (alpha === 0.0) {
        return 0;
    }
    var R = ((tint >> 16) & 0xFF);
    var G = ((tint >> 8) & 0xFF);
    var B = (tint & 0xFF);

    R = ((R * alpha) + 0.5) | 0;
    G = ((G * alpha) + 0.5) | 0;
    B = ((B * alpha) + 0.5) | 0;

    return (alpha * 255 << 24) + (R << 16) + (G << 8) + B;
}
/**
 * 贴图缓存 
 * 对于链接网络图片，直接以url作为key，自动缓存
 * 对于图集，直接以小图命名作为key（所以避免重复名字，不是同一张图集里的命名也不能一致，否则覆盖）
 * 不自动缓存base64图片
 * @private
 */
export const TextureCache = Object.create(null);

/**
 * 基础贴图缓存
 * 基本不会直接用，不缓存base64
 * @private
 */
export const BaseTextureCache = Object.create(null);

/**
 * 记录TextureSheet，便于帧动画贴图数据查找，暂时不用
 */
export const TextureSheetCache = Object.create(null);

/**
 * 销毁所有缓存的纹理和基础纹理
 * @function destroyTextureCache
 */
export function destroyTextureCache() {
    let key;

    for (key in TextureCache) {
        TextureCache[key].destroy();
    }
    for (key in BaseTextureCache) {
        BaseTextureCache[key].destroy();
    }
}

/**
 * 移除所有缓存的纹理和基础纹理，但不销毁
 * @function clearTextureCache
 */
export function clearTextureCache() {
    let key;

    for (key in TextureCache) {
        delete TextureCache[key];
    }
    for (key in BaseTextureCache) {
        delete BaseTextureCache[key];
    }
}

/**
 * 用于记录引擎全局属性记录，暂时没用到
 * 除了引擎内部修改赋值，外部仅供读取
 */
export const GlobalPro = {
    /**
     * 舞台渲染类型，
     */
    stageRenderType: RENDERER_TYPE,
    /**
     * 实际渲染分辨率
     */
    dpi: 1,
    /**
     * 图集间隙
     */
    padding: 2,
}

let webglSupported: boolean;
/**
 * 判断是否支持webgl
 * @function isWebGLSupported
 * @return {boolean}
 */
export function isWebGLSupported(): boolean {
    //淘宝环境直接返回true，否则找淘宝小程序解决
    if (getEnv() == "tb") return true;
    //已经判断过了，直接返回
    if (webglSupported !== undefined) return webglSupported;
    //进入判断，加上低性能下是否使用webgl标识，pc上因为failIfMajorPerformanceCaveat设为true获取不到webgl一般是驱动问题
    const contextOptions = { stencil: true, failIfMajorPerformanceCaveat: true };
    try {
        if (!window["WebGLRenderingContext"]) {
            return false;
        }
        const canvas = createCanvas()// document.createElement('canvas');
        let gl = canvas.getContext('webgl', contextOptions) || canvas.getContext('experimental-webgl', contextOptions);
        const success = !!(gl && gl["getContextAttributes"]().stencil);
        if (gl) {
            const loseContext = gl["getExtension"]('WEBGL_lose_context');
            if (loseContext) {
                loseContext.loseContext();
            }
        }
        gl = null;
        return webglSupported = success;
    }
    catch (e) {
        return webglSupported = false;
    }
}

/**
 * 从数组中移除元素
 * @param arr 原数组
 * @param startIdx 开始下标
 * @param removeCount 移除数量
 */
export function removeItems(arr: Array<any>, startIdx: number, removeCount: number) {
    var i, length = arr.length

    if (startIdx >= length || removeCount === 0) {
        return
    }
    removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount)

    var len = length - removeCount

    for (i = startIdx; i < len; ++i) {
        arr[i] = arr[i + removeCount]
    }
    arr.length = len
}

/**
 * 取符合范围的值
 * @param value 
 * @param min 
 * @param max 
 */
export function clamp(value: number, min: number, max: number) {
    return Math.max(min, Math.min(max, value));
}

/**
 * 将二进制数组转成Base64字符串
 * @param buff 前端的二进制缓冲区数组
 */
export function ArrayBufferToBase64(buff: ArrayBuffer): string {
    var alph =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
        enc = "",
        n,
        p,
        bits,
        d = new Uint8Array(buff);
    var len = buff.byteLength * 8;
    for (var offset = 0; offset < len; offset += 6) {
        n = (offset / 8) | 0;
        p = offset % 8;
        bits = ((d[n] || 0) << p) >> 2;
        if (p > 2) {
            bits |= (d[n + 1] || 0) >> (10 - p);
        }
        enc += alph.charAt(bits & 63);
    }
    enc += p == 4 ? "=" : p == 6 ? "==" : "";
    return enc;
}

/**
 * 解析八进制数组为字符串
 * @param array 
 */
export function decodeText(array: Uint8Array): string {
    if (typeof TextDecoder !== 'undefined') {
        return new TextDecoder().decode(array);
    }
    // Avoid the String.fromCharCode.apply(null, array) shortcut, which
    // throws a "maximum call stack size exceeded" error for large arrays.
    var s = '';
    for (var i = 0, il = array.length; i < il; i++) {
        // Implicitly assumes little-endian.
        s += String.fromCharCode(array[i]);
    }
    // Merges multi-byte utf-8 characters.
    return decodeURIComponent(escape(s));
}
