/**
 * Created by rockyl on 2019-12-08.
 */

import MaxRectsBinPack from "./MaxRectsBinPack";
import {clonePureObj} from "./index";
import sha256 from "crypto-js/sha256";
import generateUUID from "uuid/v4";

const packExts = ['.png']; //, '.jpg', '.jpeg', '.bmp'

let canvas = document.createElement('canvas');
let canvasFinal = document.createElement('canvas');

let context = canvas.getContext('2d');
let contextFinal = canvasFinal.getContext('2d');

export async function packImages(assets, options = {}) {
	const padding = options.padding || 1;
	const maxSize = options.maxSize || 2048;
	const mode = options.mode || 0;

	const {getSheetUrlByUUID} = options;

	const images = await preProcessing(assets);

	let rects = [], singles = [];
	for (let item of images) {
		const {image, assets, image: {width, height}} = item;
		if (width < maxSize && height < maxSize) {
			const rectWidth = width + padding * 2;
			const rectHeight = height + padding * 2;
			rects.push({
				image,
				assets,
				width: rectWidth,
				height: rectHeight,
				area: rectWidth * rectHeight,
				sourceW: width,
				sourceH: height,
				offX: 0,
				offY: 0,
			})
		} else {
			assets.push({
				name: assets[0].name,
				ext: assets[0].ext,
				url: assets[0].url,
				uuids: assets.map(asset => asset.uuid),
			})
		}
	}

	rects.sort((a, b) => {
		return b.area - a.area;
	});

	let remainRects = rects.concat();
	let index = 0;
	let sheetPacks = [];
	while (remainRects.length > 0) {
		let name = 'sheet' + index;

		let pack = new MaxRectsBinPack(maxSize, maxSize, false);
		let packedRects = pack.insertRects(remainRects, mode);

		canvas.width = canvas.height = maxSize;
		let maxWidth = 0, maxHeight = 0;
		for (let rect of packedRects) {
			if (rect.x + rect.width > maxWidth) {
				maxWidth = rect.x + rect.width;
			}
			if (rect.y + rect.height > maxHeight) {
				maxHeight = rect.y + rect.height;
			}
			context.drawImage(rect.image, rect.x + padding, rect.y + padding);
		}

		canvasFinal.width = maxWidth;
		canvasFinal.height = maxHeight;
		contextFinal.drawImage(canvas, 0, 0);

		let blob = await new Promise(resolve => {
			canvasFinal.toBlob(function (blob) {
				resolve(blob);
			}, 'image/png');
		});

		let frames = {};
		let i = 0, urls = [];
		for (let rect of packedRects) {
			let sprite = {
				x: rect.x + padding,
				y: rect.y + padding,
				w: rect.width - padding * 2,
				h: rect.height - padding * 2,
				ox: rect.offX,
				oy: rect.offY,
				sw: rect.sourceW,
				sh: rect.sourceH,
			};

			urls.push(rect.assets[0].url);

			for (let asset of rect.assets) {
				frames[asset.uuid] = Object.assign({}, sprite, {name: asset.name});
				i++;
			}
		}

		/*const sheetUUID = sha256(urls.sort().join()).toString();

		let url;
		if (getSheetUrlByUUID) {
			url = await getSheetUrlByUUID(sheetUUID);
		}

		if(url){
			assets.push({
				ext: '.sht',
				url,
				uuid: generateUUID(),
			});
		}else{
			assets.push({
				ext: '.sht',
				file: new File([blob], name + '.png'),
				frames,
				sheetUUID,
			});
		}*/

		sheetPacks.push([urls, name, blob, frames]);

		index++;
	}

	await Promise.all(sheetPacks.map(([urls, name, blob, frames]) => {
		return deal(assets, urls, getSheetUrlByUUID, name, blob, frames);
	}))
}

async function deal(assets, urls, getSheetUrlByUUID, name, blob, frames) {
	//const sheetUUID = sha256(urls.sort().join()).toString();
	const sheetUUID = sha256(urls.sort().join() + Object.keys(frames).sort().join()).toString();

	let url;
	if (getSheetUrlByUUID) {
		url = await getSheetUrlByUUID(sheetUUID);
	}

	if (url) {
		assets.push({
			ext: '.sht',
			url,
			uuid: generateUUID(),
		});
	} else {
		assets.push({
			ext: '.sht',
			file: new File([blob], name + '.png'),
			frames,
			sheetUUID,
		});
	}
}

function loadImage(url, assets) {
	return new Promise((resolve, reject) => {
		const img = new Image();
		img.crossOrigin = 'anonymous';
		img.onload = function () {
			resolve({
				image: img,
				assets,
			})
		};
		img.onerror = reject;
		img.src = url;
	})
}

export async function preProcessing(assets) {
	let targetAssets = [];
	for (let i = 0, li = assets.length; i < li; i++) {
		const asset = assets[i];
		if (packExts.includes(asset.ext)) {
			targetAssets.push(assets.splice(i, 1)[0]);
			i--;
			li--;
		}
	}

	let groups = {};
	for (let asset of targetAssets) {
		let group = groups[asset.url];
		if (!group) {
			group = groups[asset.url] = [];
		}
		group.push(asset);
	}

	let ps = [];

	for (let url in groups) {
		ps.push(loadImage(url, groups[url]))
	}

	return await Promise.all(ps);
}
