/**
 * Created by rockyl on 2018/7/11.
 *
 * 资源管理
 * 加载资源
 */

import {createTexture} from '../core/Texture'
import {FrameAnimation, getFrameAnimation, putFrameAnim} from '../core/FrameAnimation'
import {Sheet} from '../core/Sheet'
import {Texture} from "../core";
import {EngineConfig} from "../engine-config";

let resCache = {};
let resPath = '';

const resLoaderType = {
	'.json': loadJson,
	'.json5': loadJson5,
	'.txt': loadTxt,
	'.png': loadTexture,
	'.jpg': loadTexture,
	'.svg': loadTexture,
	'.bimg': loadTextureFromBlob,
	'.sht': loadSheet,
	'.sht-disperse': loadSheetDisperse,
	'.fnt': loadFont,
	'.anim': loadAnim,
};

/**
 * 设置资源根路径
 * @param path
 */
export function setResPath(path) {
	resPath = path;
}

/**
 * 加载一批资源
 * @param items 资源数组: ['aaa.png', {uuid: 'bbb', url: 'alias.png'}]
 * @param progress 进度回调，参数为加载百分比
 * @return Promise<Array<any>> 资源组
 */
export function loadResItems(items: Array<any | string>, progress?: (percentage: number) => void): Promise<Array<any>> {
	let total = items.length;
	let count = 0;

	return Promise.all(
		items.map(item => {
			let uuid, url, config, ext;
			if (typeof item === 'string') {
				url = item;
			} else {
				url = item.url;
			}
			if (!url) {
				return Promise.resolve();
			}
			if (typeof item === 'string') {
				uuid = getUUIDFromUrl(url);
			} else {
				uuid = item.uuid || getUUIDFromUrl(url);
				ext = item.ext;
				config = item.config;
			}
			let loader = getLoader(ext, url);
			return loader(url, uuid, true, config).then((res) => {
				count++;
				progress && progress(count / total);
				return res;
			})
		})
	);
}

/**
 * 加载任意网络资源
 * @param url url
 * @param uuid
 * @param type 类型(json|text|arraybuffer|blob)
 * @param cache
 * @param config
 * @param options 请求配置
 * @return Promise<any> 资源
 */
export async function loadAny(url, uuid?, cache = true, config?, options = {}, type = 'arraybuffer'): Promise<any> {
	let response = await fetch(resolveUrl(url), options);

	let result;
	switch (type) {
		case 'json':
			result = response.json();
			break;
		case 'text':
			result = response.text();
			break;
		case 'arraybuffer':
			result = response.arrayBuffer();
			break;
		case 'blob':
			result = response.blob();
			break;
	}

	return await result;
}

/**
 * 加载文本资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export function loadTxt(url, uuid?, cache = true, config?): Promise<string> {
	let p = loadAny(url, uuid, cache, config, undefined, 'text');
	if (cache) {
		p.then(data => {
			cacheRes(data, url, uuid);
			return data;
		})
	}

	return p;
}

/**
 * 加载json资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export function loadJson(url, uuid?, cache = true, config?): Promise<any> {
	let p = loadAny(url, uuid, cache, config, undefined, 'json');
	if (cache) {
		p.then(data => {
			cacheRes(data, url, uuid);
			return data;
		})
	}

	return p;
}

/**
 * 加载json5资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export async function loadJson5(url, uuid?, cache = true, config?): Promise<any> {
	let txt = await loadTxt(url, uuid);
	const jsonData = window['eval'](`(${txt})`);
	cacheRes(jsonData, url, uuid);
	return jsonData;
}

/**
 * 加载图集资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export function loadSheet(url, uuid?, cache = true, config?): Promise<Sheet> {
	let pngFile = url.substring(0, url.lastIndexOf('.')) + '.png';
	return Promise.all([
		loadJson(url, null, false),
		loadImage(pngFile, null, false),
	]).then(
		(result) => {
			let data: any = result[0];
			let img: any = result[1];

			let sheet: Sheet = new Sheet(img, data.frames);
			sheet.generateAll();

			if (cache) {
				cacheRes(sheet, url, uuid);

				if (config) {
					for (let textureConfig of config.textures) {
						const {name, uuid} = textureConfig;
						const texture = sheet.getTexture(name);
						cacheRes(texture, name, uuid);
					}
				} else {
					let textures = sheet.getAllTextures();
					for (let key in textures) {
						cacheRes(textures[key], key, key);
					}
				}
			}

			return sheet;
		}
	)
}

/**
 * 加载散列的图集
 * @param url
 * @param uuid
 * @param cache
 * @param config
 */
export async function loadSheetDisperse(url, uuid?, cache = true, config?): Promise<Sheet> {
	for(let {name, uuid} of config.textures){
		let subUrl = url.replace('-disperse', '') + '/' + name + '.png';
		await loadTexture(subUrl, uuid);
	}

	return null;
}

/**
 * 加载文图字资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export function loadFont(url, uuid?, cache = true, config?): Promise<Sheet> {
	return loadSheet(url, null, false).then(
		sheet => {
			if (cache) {
				cacheRes(sheet, url, uuid);
			}

			return sheet;
		}
	)
}

function findAnimConfig(animations, name) {
	let result;
	animations.some((item: any) => {
		if (item.name === name) {
			result = item;
			return true;
		}
	});
	return result;
}

/**
 * 加载帧动画资源(多个)
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export function loadAnim(url, uuid?, cache = true, config?): Promise<FrameAnimation[]> {
	let pngFile = url.substring(0, url.lastIndexOf('.')) + '.png';
	return Promise.all([
		loadJson(url, null, false),
		loadImage(pngFile, null, false),
	]).then(
		(result) => {
			let data: any = result[0];
			let img: any = result[1];

			putFrameAnim(img, data);

			const animations = [];
			for (let name in data.mc) {
				const animation = getFrameAnimation(name);
				if (cache) {
					let uuid = name;
					if (config) {
						const cfg = findAnimConfig(config.animations, name);
						uuid = cfg.uuid;
					}
					cacheRes(animation, name, uuid);
				}
				animations.push(animation);
			}

			return animations;
		}
	)
}

/**
 * 加载图片资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export function loadImage(url, uuid?, cache = true, config?): Promise<any> {
	return new Promise((resolve, reject) => {
		let img = new Image();
		if (EngineConfig.imgCrossOrigin) {
			img.setAttribute('crossOrigin', 'anonymous');
		}
		img.onload = function (e) {
			resolve(img);
		};
		img.onerror = function (e) {
			reject(e);
		};
		img.src = resolveUrl(url);
	});
}

/**
 * 加载blob图片资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export async function loadImageFromBlob(url, uuid?, cache = true, config?): Promise<any> {
	try {
		const imgBlob = await loadAny(url, uuid, false, config, undefined, 'blob');
		return await blobToImage(imgBlob);
	} catch (e) {
		console.log(e);
	}
}

/**
 * 加载纹理资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export async function loadTexture(url, uuid?, cache = true, config?): Promise<Texture> {
	const img: any = await loadImage(url, uuid, false);
	const texture = createTexture(img);
	if (cache) {
		cacheRes(texture, url, uuid);
	}
	return texture;
}

/**
 * 加载blob纹理资源
 * @param url url
 * @param uuid 唯一名
 * @param cache 是否缓存
 * @param config
 * @return Promise<any> 资源
 */
export async function loadTextureFromBlob(url, uuid?, cache = true, config?): Promise<Texture> {
	const img: any = await loadImageFromBlob(url, uuid, false);
	const texture = createTexture(img);
	if (cache) {
		cacheRes(texture, url, uuid);
	}
	return texture;
}

/**
 * 处理url
 * @param url url
 * @return string 处理后的url
 */
function resolveUrl(url): string {
	if (url.indexOf('//') === 0 || url.indexOf('http:') === 0 || url.indexOf('https:') === 0) {
		return url;
	}
	return resPath + url;
}

/**
 * 获取一个加载器
 * @param ext
 * @param url
 */
function getLoader(ext, url) {
	ext = ext || url.substr(url.lastIndexOf('.'));
	return resLoaderType[ext] || loadAny;
}

/**
 * 根据url推断名称，只是获取短文件名
 * @param url
 */
function getUUIDFromUrl(url) {
	return url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.'))
}

/**
 * 缓存资源
 * @param res
 * @param url
 * @param uuid
 */
export function cacheRes(res, url, uuid?) {
	uuid = uuid || getUUIDFromUrl(url);
	resCache[uuid] = res;
}

/**
 * 增加加载器
 * @param ext 需加载的文件后缀
 * @param loader 加载方法，返回携带最终资源的Promise
 */
export function addLoader(ext, loader) {
	resLoaderType[ext] = loader;
}

/**
 * 获取资源
 * @param uuid
 */
export function getRes(uuid) {
	return resCache[uuid];
}

/**
 * 销毁资源
 * @param uuidOrUuids
 */
export function destroyRes(uuidOrUuids) {
	if (Array.isArray(uuidOrUuids)) {
		while (uuidOrUuids.length > 0) {
			delete resCache[uuidOrUuids.pop()];
		}
	} else {
		delete resCache[uuidOrUuids];
	}
}

/**
 * 销毁全部资源
 */
export function destroyAllRes() {
	for (let key in resCache) {
		destroyRes(key);
	}
}

/**
 * 获取所有uuid值
 */
export function getAllResUuids() {
	return Object.keys(resCache);
}

function fileOrBlobToDataURL(obj): Promise<string> {
	return new Promise((resolve, reject) => {
		let a = new FileReader();
		a.readAsDataURL(obj);
		a.onload = function (e) {
			resolve(e.target['result']);
		};
		a.onerror = function (e) {
			reject(e);
		}
	});
}

async function blobToImage(blob) {
	const dataUrl = await fileOrBlobToDataURL(blob);

	return new Promise((resolve, reject) => {
		let img = new Image();
		if (EngineConfig.imgCrossOrigin) {
			img.setAttribute('crossOrigin', 'anonymous');
		}
		img.onload = function () {
			resolve(img);
		};
		img.onerror = function (e) {
			reject(e);
		};
		img.src = dataUrl;
	})
}
