/**
 * @author itorr<https://github.com/itorr>
 * @date 2022-06-01
 * @Description One Last Image
 * */



const randRange = (a, b) => Math.floor(Math.random() * (b - a) + a);

const inputImageEl = document.querySelector('#input');



let width = 640;
let height = 480;
let scale = width / height;



let lastConfigString = null;

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const canvasShade = document.createElement('canvas');
const canvasShadeMin = document.createElement('canvas');
const canvasMin = document.createElement('canvas');

const louvre = async ({img, outputCanvas, config, callback}) => {
	if (!img || !config) return;

	const configString = [
		JSON.stringify(config),
		img.src,
	].join('-');

	if (lastConfigString === configString) return;

	console.time('louvre');

	lastConfigString = configString;

	const oriWidth = img.naturalWidth;
	const oriHeight = img.naturalHeight;

	let oriScale = oriWidth / oriHeight;



	// const _width  = Math.floor( width  / config.zoom );
	// const _height = Math.floor( height / config.zoom );

	let _width  = Math.floor( oriWidth   / config.zoom );
	let _height = Math.floor( oriHeight  / config.zoom );

	const maxWidth = 1200;
	if(_width > maxWidth){
		_height = _height * maxWidth / _width
		_width = maxWidth
	}
	// const _width = 800;
	// const _height = 800;


	let cutLeft = 0;
	let cutTop = 0;

	let calcWidth = oriWidth;
	let calcHeight = oriHeight;

	if(config.cover){

		if(oriScale > 1){
			cutLeft = (oriScale - 1) * oriHeight / 2;
			calcWidth = oriHeight;
			_width = _height;
		}else{
			cutTop =  (1 - oriScale) * oriHeight / 2;
			calcHeight = oriWidth;
			_height = _width;
		}
	}


	let setLeft = 0;
	let setTop = 0;

	let setWidth = _width;
	let setHeight = _height;


	canvas.width = _width;
	canvas.height = _height;



	ctx.drawImage(
		img,
		cutLeft, cutTop,
		calcWidth, calcHeight,

		setLeft, setTop,
		setWidth, setHeight
	);
	// ctx.font = '200px sans-serif'
	// ctx.fillText('123233',50,200);

	let pixel = ctx.getImageData(0, 0, _width, _height);



	let pixelData = pixel.data;


	for (let i = 0; i < pixelData.length; i += 4) {
		// let yuv = rgb2yuv(
		// 	pixelData[i],
		// 	pixelData[i + 1],
		// 	pixelData[i + 2],
		// );
		const r = pixelData[i];
		const g = pixelData[i + 1];
		const b = pixelData[i + 2];
		
		let y = r * .299000 + g * .587000 + b * .114000;
		y = Math.floor(y);

		// if(i%10000) console.log(y);

		pixelData[i    ] = y;
		pixelData[i + 1] = y;
		pixelData[i + 2] = y;
	}
	let shadePixel;

	const { 
		shadeLimit = 80,
		shadeLight = 40 
	} = config;
	if(config.shade){
		// 处理暗面
		shadePixel = ctx.createImageData(_width, _height);

		for (let i = 0; i < pixelData.length; i += 4) {
			let y = pixelData[i];

			y = y > shadeLimit ? 0 : (shadeLight + Math.random() * 40 - 20);

			// y = Math.max(255-y) * 0.6;

			shadePixel.data[i    ] = y;
			shadePixel.data[i + 1] = 128;
			shadePixel.data[i + 2] = 128;
			shadePixel.data[i + 3] = Math.floor(Math.random() * 255)//Math.ceil( y + Math.random() * 40 - 20);
		}

		// /*
		// document.body.appendChild(canvasShade)

		const ctxShade = canvasShade.getContext('2d');
		const ctxShadeMin = canvasShadeMin.getContext('2d');
		
		canvasShade.width = _width;
		canvasShade.height = _height;

		// console.log({shadePixel})

		ctxShade.putImageData(shadePixel, 0, 0);

		// ctxShade.fillText('123233',50,50);
		const shadeZoom = 4;
		canvasShadeMin.width = Math.floor(_width/shadeZoom);
		canvasShadeMin.height = Math.floor(_height/shadeZoom);

		ctxShadeMin.drawImage(
			canvasShade,
			0,0,
			canvasShadeMin.width,canvasShadeMin.height
		);

		ctxShade.clearRect(0,0,_width,_height)
		ctxShade.drawImage(
			canvasShadeMin,
			0,0,
			_width,_height
		);
		shadePixel = ctxShade.getImageData(0,0,_width,_height);

	}
	
	const { 
		light = 0,
	} = config;
	if(light){

		
		for (let i = 0; i < pixelData.length; i += 4) {
			let y = pixelData[i];

			y = y + y * (light/100);
	
			pixelData[i    ] = y;
			pixelData[i + 1] = y;
			pixelData[i + 2] = y;
		}

		// ctx.putImageData(pixel, 0, 0);
		// pixel = ctx.getImageData(0, 0, _width, _height);
	}




	let pixel1 = config.convoluteName ? convoluteY(
		pixel,
		config.Convolutes[config.convoluteName],
		ctx
	) : pixel;

	// if(config.contrast){
	// 	for (let i = 0; i < pixel1.data.length; i +=4) {
	// 		let r = (pixel1.data[i]-128) * config.contrast + 128;
	// 		pixel1.data[i  ] = r;
	// 		pixel1.data[i+1] = r;
	// 		pixel1.data[i+2] = r;
	// 		pixel1.data[i+3] = 255;
	// 	}
	// }

	if(config.convolute1Diff){
		let pixel2 = config.convoluteName2 ? convoluteY(
			pixel,
			config.Convolutes[config.convoluteName2],
			ctx
		) : pixel;
		
		// console.log(/pixel2/,config.Convolutes[config.convoluteName2],pixel2);
		// pixelData
		for (let i = 0; i < pixel2.data.length; i +=4) {
			let r = 128 + pixel2.data[i  ] - pixel1.data[i  ];
			pixel2.data[i  ] = r;
			pixel2.data[i+1] = r;
			pixel2.data[i+2] = r;
			pixel2.data[i+3] = 255;
		}
		pixel = pixel2;
	}else{
		// 不对比
		pixel = pixel1;
	}

	pixelData = pixel.data;



	if(config.lightCut || config.darkCut){
		const scale = 255 / (255 - config.lightCut - config.darkCut);
		for (let i = 0; i < pixelData.length; i += 4) {
			let y = pixelData[i];

			y = (y - config.darkCut) * scale;
			
			y = Math.max(0,y);
			
			pixelData[i+0 ] = y
			pixelData[i+1 ] = y
			pixelData[i+2 ] = y
			pixelData[i+3 ] = 255
		}
	}

	if(config.kuma){

		const hStart = 30;
		const hEnd = -184;
	
		// const be = bezier(0.57, 0.01, 0.43, 0.99);
		// const s = config.s/100;
	
	
		const gradient = ctx.createLinearGradient(0,0, _width,_height);
	
		gradient.addColorStop(0, '#fbba30');
		gradient.addColorStop(0.3, '#fc7235');
		gradient.addColorStop(.5, '#fc354e');
		gradient.addColorStop(.65, '#cf36df');
		gradient.addColorStop(.8, '#37b5d9');
		gradient.addColorStop(1, '#3eb6da');
	
		ctx.fillStyle = gradient;
		ctx.fillRect(0, 0, _width, _height);
		let gradientPixel = ctx.getImageData(0, 0, _width, _height);
		
		for (let i = 0; i < pixelData.length; i += 4) {
			let y = pixelData[i];
			let p = Math.floor(i / 4);
	
			let _h = Math.floor(p/_width);
			let _w = p % _width;
	
			/*
			
			// const 
			// hScale = hScale * hScale;
	
			let hScale = (_h + _w)/(_width + _height);
	
			hScale = hScale * hScale;
			hScale = be(hScale);
	
			// let h = Math.floor((hStart + (hScale) * (hEnd - hStart)));
			let [h] = rgb2hsl([
				gradientPixel.data[i + 0],
				gradientPixel.data[i + 1],
				gradientPixel.data[i + 2],
			]);
			const l = y/255;
			const rgb = hsl2rgb([h, s, l * (1 - config.l/100) + (config.l/100)]);
	
			if(i%5677===0){
				// console.log(h,y,l,l * (config.l/100) + (1 - config.l/100))
				// console.log((_h + _w)/(_width + _height),hScale)
			}
	
			pixelData[i+0 ] = rgb[0];
			pixelData[i+1 ] = rgb[1];
			pixelData[i+2 ] = rgb[2];
			pixelData[i+3 ] = 255;
			*/
	
			pixelData[i+0 ] = gradientPixel.data[i + 0];
			pixelData[i+1 ] = gradientPixel.data[i + 1];
			pixelData[i+2 ] = gradientPixel.data[i + 2];
			
			y = 255 - y;
			if(config.shade){
				y = Math.max(
					y,
					shadePixel.data[i]
				);
			}
			pixelData[i+3 ] = y
		}
	
	}


	// for(let i = 0;i < pixelData.length;i += 4){

	// 	let _rgb = yuv2rgb(
	// 		pixelData[i],
	// 		pixelData[i+1],
	// 		pixelData[i+2],
	// 	);

	// 	pixelData[i   ] = _rgb[0];
	// 	pixelData[i+1 ] = _rgb[1];
	// 	pixelData[i+2 ] = _rgb[2];
	// }

	// blurC();
	ctx.putImageData(pixel, 0, 0);

	const ctxMin = canvasMin.getContext('2d');

	canvasMin.width = Math.floor(_width/1.4);
	canvasMin.height = Math.floor(_height/1.3);

	ctxMin.clearRect(0,0,canvasMin.width,canvasMin.height)
	ctxMin.drawImage(
		canvas,
		0,0,
		canvasMin.width,canvasMin.height
	);
	
	ctx.clearRect(0,0,_width,_height)
	ctx.drawImage(
		canvasMin,
		0,0,
		canvasMin.width,canvasMin.height,
		0,0,_width,_height
	);

	// one-last-image-logo-color.png
	if(config.watermark){
		// const watermarkImageEl = await loadImagePromise('one-last-image-logo2.png');

		const watermarkImageWidth = watermarkImageEl.naturalWidth;
		const watermarkImageHeight = watermarkImageEl.naturalHeight / 2;
		let setWidth = _width * 0.3;
		let setHeight = setWidth / watermarkImageWidth * watermarkImageHeight;
	
		if( _width / _height  > 1.1 ){
			setHeight = _height * 0.15;
			setWidth = setHeight / watermarkImageHeight * watermarkImageWidth;
		}
	
		let cutTop = 0;
	
		if(config.hajimei){
			cutTop = watermarkImageHeight;
		}
	
		let setLeft = _width - setWidth - setHeight * 0.2;
		let setTop = _height - setHeight - setHeight * 0.16;
		ctx.drawImage(
			watermarkImageEl,
			0,cutTop,
			watermarkImageWidth,watermarkImageHeight,
			setLeft, setTop,
			setWidth, setHeight
		);
	}

	const outputCtx = outputCanvas.getContext('2d');

	outputCanvas.width = _width;
	outputCanvas.height = _height;
	outputCtx.fillStyle = '#FFF';
	outputCtx.fillRect(0,0,_width,_height);
	outputCtx.drawImage(
		canvas,
		0,0,_width,_height
	);

	console.timeEnd('louvre');
	// return canvas.toDataURL('image/png');
	
};

let loadImage = (url,onOver)=>{
	const el = new Image();
	el.onload = _=>onOver(el);
	el.src = url;
};
let loadImagePromise = async url=>{
	return new Promise(function(resolve, reject){
		setTimeout(function(){
			const el = new Image();
			el.onload = _=>resolve(el);
			el.onerror = e=>reject(e);
			el.src = url;
		}, 2000);
	});
}

let watermarkImageEl;

const louvreInit = onOver=>{
	loadImage('one-last-image-logo2.png',el=>{
		watermarkImageEl = el;
		onOver();
	});
}


let convolute = (pixels, weights, ctx) => {
	const side = Math.round(Math.sqrt(weights.length));
	const halfSide = Math.floor(side / 2);

	const src = pixels.data;
	const sw = pixels.width;
	const sh = pixels.height;

	const w = sw;
	const h = sh;
	const output = ctx.createImageData(w, h);
	const dst = output.data;


	for (let y = 0; y < h; y++) {
		for (let x = 0; x < w; x++) {
			const sy = y;
			const sx = x;
			const dstOff = (y * w + x) * 4;
			let r = 0, g = 0, b = 0;
			for (let cy = 0; cy < side; cy++) {
				for (let cx = 0; cx < side; cx++) {
					const scy = Math.min(sh - 1, Math.max(0, sy + cy - halfSide));
					const scx = Math.min(sw - 1, Math.max(0, sx + cx - halfSide));
					const srcOff = (scy * sw + scx) * 4;
					const wt = weights[cy * side + cx];
					r += src[srcOff] * wt;
					g += src[srcOff + 1] * wt;
					b += src[srcOff + 2] * wt;
				}
			}
			dst[dstOff] = r;
			dst[dstOff + 1] = g;
			dst[dstOff + 2] = b;
			dst[dstOff + 3] = 255;
		}
	}


	// for (let y=0; y<h; y++) {
	// 	for (let x=0; x<w; x++) {
	// 		const srcOff = (y*w+x)*4;
	// 		src[srcOff] = dst[srcOff];
	// 	}
	// }
	return output;
};




 const convoluteY = (pixels, weights, ctx) => {
	const side = Math.round( Math.sqrt( weights.length ) );
	const halfSide = Math.floor(side / 2);

	const src = pixels.data;

	const w = pixels.width;
	const h = pixels.height;
	const output = ctx.createImageData(w, h);
	const dst = output.data;

	for (let sy = 0; sy < h; sy++) {
		for (let sx = 0; sx < w; sx++) {
			const dstOff = (sy * w + sx) * 4;
			let r = 0, g = 0, b = 0;

			for (let cy = 0; cy < side; cy++) {
				for (let cx = 0; cx < side; cx++) {

					const scy = Math.min(h - 1, Math.max(0, sy + cy - halfSide));
					const scx = Math.min(w - 1, Math.max(0, sx + cx - halfSide));

					const srcOff = (scy * w + scx) * 4;
					const wt = weights[cy * side + cx];

					r += src[srcOff] * wt;
					// g += src[srcOff + 1] * wt;
					// b += src[srcOff + 2] * wt;
				}
			}
			dst[dstOff] = r;
			dst[dstOff + 1] = r;
			dst[dstOff + 2] = r;
			dst[dstOff + 3] = 255;
		}
	}


	// for (let y=0; y<h; y++) {
	// 	for (let x=0; x<w; x++) {
	// 		const srcOff = (y*w+x)*4;
	// 		src[srcOff] = dst[srcOff];
	// 	}
	// }
	return output;
};