import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import canvasToBlob from './../../../../node_modules/blueimp-canvas-to-blob/js/canvas-to-blob.min.js';
import filesaver from './../../../../node_modules/file-saver/dist/FileSaver.min.js';
import GIF from './../../../../node_modules/gif.js.optimized/';
import CanvasToTIFF from './../../libs/canvastotiff.js';
import Tools_settings_class from "../tools/settings";

var instance = null;

/** 
 * manages files / save
 * 
 * @author ViliusL
 */
class File_save_class {
	
	constructor() {
		//singleton
		if (instance) {
			return instance;
		}
		instance = this;

		this.Base_layers = new Base_layers_class();
		this.Helper = new Helper_class();
		this.POP = new Dialog_class();
		this.Tools_settings = new Tools_settings_class();

		this.set_events();

		//save types config
		this.SAVE_TYPES = {
			PNG: "Portable Network Graphics",
			JPG: "JPG/JPEG Format",
			//AVIF: "AV1 Image File Format", //just uncomment it in future to make it work
			JSON: "Full layers data",
			WEBP: "Weppy File Format",
			GIF: "Graphics Interchange Format",
			BMP: "Windows Bitmap",
			TIFF: "Tag Image File Format",
		};

		this.default_extension = 'PNG';
	}

	set_events() {
		document.addEventListener('keydown', (event) => {
			var code = event.key.toLowerCase();
			if (this.Helper.is_input(event.target))
				return;

			if (code == "s") {
				if(event.shiftKey){
					//export
					this.save();
				}
				else{
					//save
					this.export();
				}
				event.preventDefault();
			}
		}, false);
	}

	/**
	 * saves as non destructive mode (including layers, RAW)
	 */
	save(){
		var types = JSON.parse(JSON.stringify(this.SAVE_TYPES));
		for(var i in types){
			if(i != 'JSON'){
				delete types[i];
			}
		}

		this.save_general(types, 'Save as');

	}

	/**
	 * save as encoded image
	 */
	export(){
		var types = JSON.parse(JSON.stringify(this.SAVE_TYPES));
		delete types.JSON;

		this.save_general(types, 'Export');
	}

	save_general(file_types, title) {
		var _this = this;

		//find default format
		var save_default = null;
		var save_default_cookie = this.Helper.getCookie('save_default');

		for(var i in file_types) {
			if(save_default_cookie == i){
				save_default = i;
				break;
			}
		}
		if(save_default == null){
			save_default = Object.keys(file_types)[0];
		}
		save_default = save_default + " - " + file_types[save_default];

		var calc_size_value = false;
		var calc_size = false;
		if (config.WIDTH * config.HEIGHT < 1000000) {
			calc_size_value = true;
			calc_size = true;
		}

		var file_name = config.layers[0].name;
		var parts = file_name.split('.');
		if (parts.length > 1)
			file_name = parts[parts.length - 2];
		file_name = file_name.replace(/ /g, "-");

		var save_types = [];
		for(var i in file_types) {
			save_types.push(i + " - " + file_types[i]);
		}

		var save_layers_types = [
			'All',
			'Selected',
			'Separated',
			'Separated (original types)',
		];
		var resolution = this.Tools_settings.get_setting('resolution');

		var settings = {
			title: title,
			params: [
				{name: "name", title: "File name:", value: file_name},
				{name: "type", title: "Save as type:", values: save_types, value: save_default},
				{name: "quality", title: "Quality:", value: 90, range: [1, 100]},
				{title: "File size:", html: '<span id="file_size">-</span>'},
				{title: "Resolution:",  value: resolution},
				{name: "calc_size", title: "Show file size:", value: calc_size_value},
				{name: "layers", title: "Save layers:", values: save_layers_types},
				{name: "delay", title: "Gif delay:", value: 400},
			],
			on_change: function (params, canvas_preview, w, h) {
				_this.save_dialog_onchange(true);
			},
			on_finish: function (params) {
				if (params.layers == 'Separated' || params.layers == 'Separated (original types)') {
					var active_layer = config.layer.id;
					var original_layer_type = params.layers;

					//alter params
					params.layers = 'Selected';

					for (var i in config.layers) {
						if (config.layers[i].visible == false)
							continue;

						//detect type
						if (original_layer_type == 'Separated (original types)') {
							//detect type from file name
							params.type = _this.SAVE_TYPES[_this.default_extension];
							for (var j in _this.SAVE_TYPES) {
								if (_this.Helper.strpos(config.layers[i].name.toLowerCase(), '.' + j.toLowerCase()) !== false) {
									params.type = j;
									break;
								}
							}
						}
						
						new app.Actions.Select_layer_action(config.layers[i].id, true).do();
						_this.save_action(params, true);
					}
					new app.Actions.Select_layer_action(active_layer, true).do();
				}
				else {
					_this.save_action(params);
				}
			},
		};
		this.POP.show(settings);

		document.getElementById("pop_data_name").select();

		if (calc_size == true) {
			//calc size once
			this.save_dialog_onchange(true);
		}
		else{
			this.save_dialog_onchange(false);
		}
	}

	save_data_url() {
		var max = 10 * 1000 * 1000;
		if (config.WIDTH * config.WIDTH > 10 * 1000 * 1000) {
			alertify.error('Size is too big, max ' + this.Helper.number_format(max, 0) + ' pixels.');
			return;
		}

		var canvas = document.createElement('canvas');
		var ctx = canvas.getContext("2d");
		canvas.width = config.WIDTH;
		canvas.height = config.HEIGHT;

		this.disable_canvas_smooth(ctx);

		//ask data
		this.Base_layers.convert_layers_to_canvas(ctx, null, false);
		var data_url = canvas.toDataURL();

		max = 1000 * 1000;
		if (data_url.length > max) {
			alertify.error('Size is too big, max ' + this.Helper.number_format(max, 0) + ' bytes.');
			return;
		}

		var settings = {
			title: 'Data URL',
			params: [
				{name: "url", title: "URL:", type: "textarea", value: data_url},
			],
		};
		this.POP.show(settings);
	}

	update_file_size(file_size) {
		if (typeof file_size == 'string') {
			document.getElementById('file_size').innerHTML = file_size;
			return;
		}

		if (file_size > 1024 * 1024)
			file_size = this.Helper.number_format(file_size / 1024 / 1024, 2) + ' MB';
		else if (file_size > 1024)
			file_size = this.Helper.number_format(file_size / 1024, 2) + ' KB';
		else
			file_size = (file_size) + ' B';
		document.getElementById('file_size').innerHTML = file_size;
	}

	/**
	 * /activated on save dialog parameters change - used for calculating file size
	 *
	 * @param {boolean} calculate_file_size
	 */
	save_dialog_onchange(calculate_file_size) {
		var _this = this;
		var user_response = this.POP.get_params();

		var quality = parseInt(user_response.quality);
		if (quality > 100 || quality < 1 || isNaN(quality) == true)
			quality = 90;
		quality = quality / 100;

		//detect type
		var type = user_response.type;
		var parts = type.split(" ");
		type = parts[0];

		if (type == 'JPG' || type == 'WEBP')
			document.getElementById('popup-tr-quality').style.display = '';
		else
			document.getElementById('popup-tr-quality').style.display = 'none';

		if (type == 'GIF')
			document.getElementById('popup-tr-delay').style.display = '';
		else
			document.getElementById('popup-tr-delay').style.display = 'none';

		if (type == 'JSON' || type == 'GIF')
			document.getElementById('popup-tr-layers').style.display = 'none';
		else
			document.getElementById('popup-tr-layers').style.display = '';

		if (user_response.layers == 'Separated')
			document.getElementById('pop_data_name').disabled = true;
		else
			document.getElementById('pop_data_name').disabled = false;

		if (user_response.layers == 'Separated (original types)') {
			if(document.getElementById('popup-group-type')) {
				document.getElementById('popup-group-type').style.opacity = "0.5";
			}
			document.getElementById('popup-tr-quality').style.display = '';
		}
		else {
			if(document.getElementById('popup-group-type')) {
				document.getElementById('popup-group-type').style.opacity = "1";
			}
		}

		if(calculate_file_size == false){
			return;
		}

		this.update_file_size('...');

		if (user_response.calc_size == false || user_response.layers == 'Separated'
			|| user_response.layers == 'Separated (original types)') {

			document.getElementById('file_size').innerHTML = '-';
			return;
		}

		if (type != 'JSON') {
			//create temp canvas
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext("2d");
			canvas.width = config.WIDTH;
			canvas.height = config.HEIGHT;
			this.disable_canvas_smooth(ctx);

			//ask data
			if (user_response.layers == 'Selected' && type != 'GIF' && config.layer.type != null) {
				//only current layer !!!
				var layer = config.layer;

				var initial_x = null;
				var initial_y = null;
				if (layer.x != null && layer.y != null && layer.width != null && layer.height != null) {
					//change position to top left corner
					initial_x = layer.x;
					initial_y = layer.y;
					layer.x = 0;
					layer.y = 0;

					canvas.width = layer.width;
					canvas.height = layer.height;
				}

				this.Base_layers.convert_layers_to_canvas(ctx, layer.id, false);

				if (initial_x != null) {
					//restore position
					layer.x = initial_x;
					layer.y = initial_y;
				}
			}
			else {
				this.Base_layers.convert_layers_to_canvas(ctx, null, false);
			}
		}

		if (type != 'JSON' && (type == 'JPG' || config.TRANSPARENCY == false)) {
			//add white background
			ctx.globalCompositeOperation = 'destination-over';
			this.fillCanvasBackground(ctx, '#ffffff');
			ctx.globalCompositeOperation = 'source-over';
		}

		//calc size
		if (type == 'PNG') {
			//png
			canvas.toBlob(function (blob) {
				_this.update_file_size(blob.size);
			});
		}
		else if (type == 'JPG') {
			//jpg
			canvas.toBlob(function (blob) {
				_this.update_file_size(blob.size);
			}, "image/jpeg", quality);
		}
		else if (type == 'WEBP') {
			//WEBP
			var data_header = "image/webp";

			//check support
			if (this.check_format_support(canvas, data_header, false) == false) {
				this.update_file_size('-');
				return;
			}

			canvas.toBlob(function (blob) {
				_this.update_file_size(blob.size);
			}, data_header, quality);
		}
		else if (type == 'AVIF') {
			//AVIF
			var data_header = "image/avif";

			//check support
			if (this.check_format_support(canvas, data_header, false) == false) {
				this.update_file_size('-');
				return;
			}

			canvas.toBlob(function (blob) {
				_this.update_file_size(blob.size);
			}, data_header, quality);
		}
		else if (type == 'BMP') {
			//bmp
			var data_header = "image/bmp";

			//check support
			if (this.check_format_support(canvas, data_header, false) == false) {
				this.update_file_size('-');
				return;
			}

			canvas.toBlob(function (blob) {
				_this.update_file_size(blob.size);
			}, data_header);
		}
		else if (type == 'TIFF') {
			//tiff
			var data_header = "image/tiff";

			CanvasToTIFF.toBlob(canvas, function(blob) {
				_this.update_file_size(blob.size);
			}, data_header);
		}
		else if (type == 'JSON') {
			//json
			var data_json = this.export_as_json();

			var blob = new Blob([data_json], {type: "text/plain"});
			this.update_file_size(blob.size);
		}
		else if (type == 'GIF') {
			//gif
			this.update_file_size('-');
		}
	}
	
	/**
	 * saves data in requested way
	 * 
	 * @param {object} user_response parameters
	 * @param {boolean} autoname if use name from layer, false by default
	 */
	save_action(user_response, autoname) {
		var fname = user_response.name;
		if(autoname === true && user_response.layers == 'Selected'){
			fname = config.layer.name;
		}

		var quality = parseInt(user_response.quality);
		if (quality > 100 || quality < 1 || isNaN(quality) == true)
			quality = 90;
		quality = quality / 100;

		var delay = parseInt(user_response.delay);
		if (delay < 0 || isNaN(delay) == true)
			delay = 400;

		//detect type
		var type = user_response.type;
		var parts = type.split(" ");
		type = parts[0];

		//detect type from file name
		for(var i in this.SAVE_TYPES) {
			if (this.Helper.strpos(fname, '.' + i.toLowerCase()) !== false) {
				type = i;
			}
		}

		//save default type as cookie
		if(this.Helper.getCookie('save_default') == '' || this.Helper.getCookie('save_default') != type){
			this.Helper.setCookie('save_default', type);
		}

		if (type != 'JSON') {
			//temp canvas
			var canvas;
			var ctx;
			
			//get data
			if (user_response.layers == 'Selected' && type != 'GIF') {
				canvas = this.Base_layers.convert_layer_to_canvas();
				ctx = canvas.getContext("2d");
			}
			else {
				canvas = document.createElement('canvas');
				ctx = canvas.getContext("2d");
				canvas.width = config.WIDTH;
				canvas.height = config.HEIGHT;
				this.disable_canvas_smooth(ctx);
				
				this.Base_layers.convert_layers_to_canvas(ctx, null, false);
			}
		}

		if (type != 'JSON' && (type == 'JPG' || config.TRANSPARENCY == false)) {
			//add white background
			ctx.globalCompositeOperation = 'destination-over';
			this.fillCanvasBackground(ctx, '#ffffff');
			ctx.globalCompositeOperation = 'source-over';
		}

		if (type == 'PNG') {
			//png - default format
			if (this.Helper.strpos(fname, '.png') == false)
				fname = fname + ".png";

			//simple save example
			//var link = document.createElement('a');
			//link.download = fname;
			//link.href = canvas.toDataURL();
			//link.click();

			//save using lib
			canvas.toBlob(function (blob) {
				filesaver.saveAs(blob, fname);
			});
		}
		else if (type == 'JPG') {
			//jpg
			if (this.Helper.strpos(fname, '.jpg') == false)
				fname = fname + ".jpg";

			canvas.toBlob(function (blob) {
				filesaver.saveAs(blob, fname);
			}, "image/jpeg", quality);
		}
		else if (type == 'WEBP') {
			//WEBP
			if (this.Helper.strpos(fname, '.webp') == false)
				fname = fname + ".webp";
			var data_header = "image/webp";

			//check support
			if (this.check_format_support(canvas, data_header) == false)
				return false;

			canvas.toBlob(function (blob) {
				filesaver.saveAs(blob, fname);
			}, data_header, quality);
		}
		else if (type == 'AVIF') {
			//AVIF
			if (this.Helper.strpos(fname, '.avif') == false)
				fname = fname + ".avif";
			var data_header = "image/avif";

			//check support
			if (this.check_format_support(canvas, data_header) == false)
				return false;

			canvas.toBlob(function (blob) {
				filesaver.saveAs(blob, fname);
			}, data_header, quality);
		}
		else if (type == 'BMP') {
			//bmp
			if (this.Helper.strpos(fname, '.bmp') == false)
				fname = fname + ".bmp";
			var data_header = "image/bmp";

			//check support
			if (this.check_format_support(canvas, data_header) == false)
				return false;

			canvas.toBlob(function (blob) {
				filesaver.saveAs(blob, fname);
			}, data_header);
		}
		else if (type == 'TIFF') {
			//tiff
			if (this.Helper.strpos(fname, '.tiff') == false)
				fname = fname + ".tiff";
			var data_header = "image/tiff";

			CanvasToTIFF.toBlob(canvas, function(blob) {
				filesaver.saveAs(blob, fname);
			}, data_header);
		}
		else if (type == 'JSON') {
			//json - full data with layers
			if (this.Helper.strpos(fname, '.json') == false)
				fname = fname + ".json";

			var data_json = this.export_as_json();

			var blob = new Blob([data_json], {type: "text/plain"});
			//var data = window.URL.createObjectURL(blob); //html5
			filesaver.saveAs(blob, fname);
		}
		else if (type == 'GIF') {
			//gif
			var cores = navigator.hardwareConcurrency || 4;
			var gif_settings = {
				workers: cores,
				quality: 10, //1-30, lower is better
				repeat: 0,
				width: config.WIDTH,
				height: config.HEIGHT,
				dither: 'FloydSteinberg-serpentine',
				workerScript: './src/js/libs/gifjs/gif.worker.js',
			};
			if (config.TRANSPARENCY == true) {
				gif_settings.transparent = 'rgba(0,0,0,0)';
			}
			var gif = new GIF(gif_settings);

			//add frames
			for (var i = 0; i < config.layers.length; i++) {
				if (config.layers[i].visible == false)
					continue;

				ctx.clearRect(0, 0, config.WIDTH, config.HEIGHT);
				if (config.TRANSPARENCY == false) {
					this.fillCanvasBackground(ctx, '#ffffff');
				}
				this.Base_layers.convert_layers_to_canvas(ctx, config.layers[i].id, false);

				gif.addFrame(ctx, {copy: true, delay: delay});
			}
			gif.render();
			gif.on('finished', function (blob) {
				filesaver.saveAs(blob, fname);
			});
		}
	}
	
	fillCanvasBackground(ctx, color, width = config.WIDTH, height = config.HEIGHT) {
		ctx.beginPath();
		ctx.rect(0, 0, width, height);
		ctx.fillStyle = color;
		ctx.fill();
	}
	
	check_format_support(canvas, data_header, show_error) {
		var data = canvas.toDataURL(data_header);
		var actualType = data.replace(/^data:([^;]*).*/, '$1');

		if (data_header != actualType && data_header != "text/plain") {
			if (show_error == undefined || show_error == true) {
				//error - no support
				alertify.error('Your browser does not support this format.');
			}
			return false;
		}
		return true;
	}
	
	/**
	 * exports all layers to JSON
	 */
	export_as_json() {
		//get date
		var today = new Date();
		var yyyy = today.getFullYear();
		var mm = today.getMonth() + 1; //January is 0!
		var dd = today.getDate();
		if (dd < 10)
			dd = '0' + dd;
		if (mm < 10)
			mm = '0' + mm;
		var today = yyyy + '-' + mm + '-' + dd;

		//data
		var export_data = {};
		export_data.info = {
			width: config.WIDTH,
			height: config.HEIGHT,
			about: 'Image data with multi-layers. Can be opened using miniPaint - '
				+ 'https://github.com/viliusle/miniPaint',
			date: today,
			version: VERSION,
			layer_active: config.layer.id,
			guides: config.guides,
		};

		//fonts
		export_data.user_fonts = config.user_fonts;

		//layers
		export_data.layers = [];
		for (var i in config.layers) {
			var layer = {};
			for (var j in config.layers[i]) {
				if (j[0] == '_' || j == 'link_canvas') {
					//private data
					continue;
				}

				layer[j] = config.layers[i][j];
			}
			export_data.layers.push(layer);
		}

		//image data
		export_data.data = [];
		for (var i in config.layers) {
			if (config.layers[i].type != 'image')
				continue;

			var canvas = document.createElement('canvas');
			canvas.width = config.layers[i].width_original;
			canvas.height = config.layers[i].height_original;
			this.disable_canvas_smooth(canvas.getContext("2d"));

			canvas.getContext('2d').drawImage(config.layers[i].link, 0, 0);

			var data_tmp = canvas.toDataURL("image/png");
			export_data.data.push(
				{
					id: config.layers[i].id,
					data: data_tmp,
				}
			);
			canvas.width = 1;
			canvas.height = 1;
		}

		return JSON.stringify(export_data, null, "\t");
	}
	
	/**
	 * removes smoothing, because it look ugly during zoom
	 * 
	 * @param {ctx} ctx
	 */
	disable_canvas_smooth(ctx) {
		ctx.webkitImageSmoothingEnabled = false;
		ctx.oImageSmoothingEnabled = false;
		ctx.msImageSmoothingEnabled = false;
		ctx.imageSmoothingEnabled = false;
	}
	async export_data_url() {
		var canvas = document.createElement('canvas');
		var ctx = canvas.getContext("2d");
		canvas.width = config.WIDTH;
		canvas.height = config.HEIGHT;
		this.disable_canvas_smooth(ctx);
		this.Base_layers.convert_layers_to_canvas(ctx, null, false);
		return canvas.toDataURL();
	}

}

export default File_save_class;