/**
 * Created by rockyl on 2018/11/5.
 *
 * 渲染上下文
 */

import Bounds from "../../support/Bounds";
import {dirtyFieldTrigger} from "../../tools/decorators";
import {ScillaEngine} from "../ScillaEngine";

/**
 * 缩放模式
 *
 * SHOW_ALL: 全可见
 * FIXED_WIDTH: 宽度固定
 * FIXED_HEIGHT: 高度固定
 */
export enum ScaleMode {
	SHOW_ALL = 'showAll',
	FIXED_WIDTH = 'fixedWidth',
	FIXED_HEIGHT = 'fixedHeight',
	NO_SCALE = 'noScale',
	NO_FIXED = 'noFixed',
}

interface RenderContextOption {
	/**
	 * 画布实例
	 */
	canvas: any;
	/**
	 * 设计宽度
	 */
	designWidth: number;
	/**
	 * 设计高度
	 */
	designHeight: number;
	/**
	 * 缩放模式
	 */
	scaleMode: ScaleMode;
	/**
	 * 是否修改画布尺寸
	 */
	modifyCanvasSize?: boolean;
	/**
	 * 是否自动调整舞台尺寸
	 */
	autoAdjustSize?: boolean;
	/**
	 * 当缩放模式修改时
	 */
	onUpdateScale: Function;
}

/**
 * 渲染上下文
 */
export default class RenderContext {
	protected canvas;
	protected canvasContext;

	protected stageWidth: number;
	protected stageHeight: number;

	private _scaleX: number;
	private _scaleY: number;
	private _rotation: number = 0;

	protected dirtyFieldTriggerLock: boolean = false;

	private shortcutCanvas;
	private onUpdateScale: Function;

	private engine;

	/**
	 * 设计宽度
	 */
	@dirtyFieldTrigger
	designWidth;
	/**
	 * 设计高度
	 */
	@dirtyFieldTrigger
	designHeight;
	/**
	 * 缩放模式
	 */
	@dirtyFieldTrigger
	scaleMode: ScaleMode;
	/**
	 * 是否修改画布的尺寸
	 */
	@dirtyFieldTrigger
	modifyCanvasSize;
	/**
	 * 是否自动调整舞台尺寸
	 */
	@dirtyFieldTrigger
	autoAdjustSize;

	constructor(engine: ScillaEngine) {
		this.engine = engine;
	}

	//todo

	private onModify(value, key, oldValue) {
		if (!this.dirtyFieldTriggerLock) {
			this.updateScaleModeSelf();
		}
	}

	/**
	 * 装配上下文
	 * @param options
	 */
	setup(options: RenderContextOption = <RenderContextOption>{}) {
		const {canvas, designWidth, designHeight, scaleMode = ScaleMode.SHOW_ALL, modifyCanvasSize = false, autoAdjustSize = false, onUpdateScale} = options;

		this.canvas = canvas;
		this.canvasContext = canvas.getContext('2d');

		this.dirtyFieldTriggerLock = true;

		this.designWidth = designWidth;
		this.designHeight = designHeight;
		this.scaleMode = scaleMode;
		this.modifyCanvasSize = modifyCanvasSize;
		this.autoAdjustSize = autoAdjustSize;
		this.onUpdateScale = onUpdateScale;

		this.dirtyFieldTriggerLock = false;

		this.updateScaleModeSelf();
	}

	/**
	 * 缩放x
	 */
	get scaleX(): number {
		return this._scaleX;
	}

	/**
	 * 缩放y
	 */
	get scaleY(): number {
		return this._scaleY;
	}

	/**
	 * 旋转
	 */
	get rotation(): number {
		return this._rotation;
	}

	/**
	 * 清空渲染上下文
	 */
	clear() {
		this.canvasContext.setTransform(1, 0, 0, 1, 0, 0);
		this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
	}

	/**
	 * 获取渲染上下文
	 */
	get context() {
		return this.canvasContext;
	}

	/**
	 * 获取舞台尺寸
	 */
	get stageSize() {
		return {
			width: this.stageWidth,
			height: this.stageHeight,
		}
	}

	/**
	 * 获取舞台缩放
	 */
	get stageScale() {
		return {
			x: this._scaleX,
			y: this._scaleY,
		}
	}

	/**
	 * 获取舞台中心
	 */
	get stageCenter() {
		return {
			x: this.stageWidth / 2,
			y: this.stageHeight / 2,
		}
	}

	/**
	 * 更新缩放模式
	 */
	private updateScaleModeSelf() {
		let {canvas, designWidth, designHeight, scaleMode, _rotation, modifyCanvasSize} = this;

		let parent = canvas.parentElement;
		let containerWidth = parent.clientWidth;
		let containerHeight = parent.clientHeight;

		const dWidth = designWidth || containerWidth;
		const dHeight = designHeight || containerHeight;

		let width, stageWidth;
		let height, stageHeight;

		let scaleX = containerWidth / dWidth;
		let scaleY = containerHeight / dHeight;

		let styleWidth;
		let styleHeight;

		//scale
		switch (scaleMode) {
			case ScaleMode.SHOW_ALL:

				break;
			case ScaleMode.NO_SCALE:
				scaleX = scaleY = 1;
				break;
			case ScaleMode.NO_FIXED:
				scaleX = scaleY = 1;
				break;
			case ScaleMode.FIXED_WIDTH:
			case ScaleMode.FIXED_HEIGHT:
				break;
		}

		//size
		switch (scaleMode) {
			case ScaleMode.SHOW_ALL:
				width = dWidth;
				height = dHeight;

				stageWidth = dWidth;
				stageHeight = dHeight;
				break;
			case ScaleMode.NO_SCALE:
				width = dWidth;
				height = dHeight;

				stageWidth = dWidth;
				stageHeight = dHeight;
				break;
			case ScaleMode.NO_FIXED:
				width = containerWidth;
				height = containerHeight;

				stageWidth = dWidth;
				stageHeight = dHeight;
				break;
			case ScaleMode.FIXED_WIDTH:
				width = dWidth;
				if (modifyCanvasSize) {
					height = dHeight;
				} else {
					height = containerHeight / scaleX;
				}
				scaleY = scaleX;

				stageWidth = width;
				stageHeight = height;
				break;
			case ScaleMode.FIXED_HEIGHT:
				if (modifyCanvasSize) {
					width = dWidth;
				} else {
					width = containerWidth / scaleY;
				}
				height = dHeight;
				scaleX = scaleY;

				stageWidth = width;
				stageHeight = height;
				break;
		}

		//styleSize
		switch (scaleMode) {
			case ScaleMode.SHOW_ALL:
				styleWidth = containerWidth;
				styleHeight = containerHeight;
				break;
			case ScaleMode.NO_FIXED:
				styleWidth = containerWidth;
				styleHeight = containerHeight;
				break;
			case ScaleMode.NO_SCALE:
				styleWidth = designWidth;
				styleHeight = designHeight;
				break;
			case ScaleMode.FIXED_WIDTH:
			case ScaleMode.FIXED_HEIGHT:
				styleWidth = modifyCanvasSize ? designWidth * scaleX : containerWidth;
				styleHeight = modifyCanvasSize ? designHeight * scaleY : containerHeight;
				break;
		}

		this.onUpdateScale(scaleX, scaleY, _rotation);

		this.stageWidth = stageWidth;
		this.stageHeight = stageHeight;
		this._scaleX = scaleX;
		this._scaleY = scaleY;

		canvas.width = width;
		canvas.height = height;
		canvas.style.display = 'block';
		canvas.style.width = styleWidth + 'px';
		canvas.style.height = styleHeight + 'px';
	}

	/**
	 * 截图
	 * @param type 目标格式 0:Image, 1:DataURL
	 * @param params
	 */
	async shortcut(type: number = 0, params: ShortcutParams) {
		let targetImg;

		if (params.bounds || params.zoomToDom) {
			const dataUrl = this.canvas.toDataURL('image/png');
			const img = await dataUrlToImage(dataUrl);
			targetImg = await this.shortcutWithSize(img, type, params.imgType, params.quality, params.bounds, params.zoomToDom ? this._scaleX : 1);
		} else {
			targetImg = this.canvas.toDataURL(params.imgType, params.quality);
		}

		return targetImg;
	}

	private async shortcutWithSize(img, type, imgType, quality?, bounds?: Bounds, scale = 1) {
		let {shortcutCanvas, stageWidth, stageHeight} = this;

		if (!shortcutCanvas) {
			this.shortcutCanvas = createCanvas();
		}
		const sx = bounds ? bounds.x || 0 : 0;
		const sy = bounds ? bounds.y || 0 : 0;
		const sw = bounds ? (bounds.width ? Math.min(bounds.width, stageWidth) : stageWidth) : stageWidth;
		const sh = bounds ? (bounds.height ? Math.min(bounds.height, stageHeight) : stageHeight) : stageHeight;
		const dw = sw * scale;
		const dh = sh * scale;
		shortcutCanvas.width = dw;
		shortcutCanvas.height = dh;
		const canvasContext = shortcutCanvas.getContext('2d');
		canvasContext.drawImage(img, sx, sy, sw, sh, 0, 0, dw, dh);

		const dataUrl = shortcutCanvas.toDataURL('image/' + imgType, quality);
		switch (type) {
			case 0:
				return await dataUrlToImage(dataUrl);
			case 1:
				return dataUrl;
		}
	}
}

interface ShortcutParams {
	imgType: string;
	zoomToDom?: boolean;
	quality?: number;
	bounds?: Bounds;
}

function dataUrlToImage(dataUrl) {
	return new Promise((resolve, reject) => {
		const image = new Image();
		image.onload = function () {
			resolve(image);
		};
		image.onerror = function (e) {
			reject(e);
		};
		image.src = dataUrl;
	})
}

/**
 * 创建canvas
 */
export function createCanvas() {
	return document.createElement('canvas');
}
