/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

var path = require('path');
var fs = require('fs');
var https = require('https');
var url = require('url');

function getCommitSha(repoId, repoPath) {
	var commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath;
	return download(commitInfo).then(function (content) {
		try {
			let lastCommit = JSON.parse(content)[0];
			return Promise.resolve({
				commitSha: lastCommit.sha,
				commitDate: lastCommit.commit.author.date
			});
		} catch (e) {
			return Promise.resolve(null);
		}
	}, function () {
		console.err('Failed loading ' + commitInfo);
		return Promise.resolve(null);
	});
}

function download(source) {
	if (source.startsWith('.')) {
		return readFile(source);
	}
	return new Promise((c, e) => {
		var _url = url.parse(source);
		var options = { host: _url.host, port: _url.port, path: _url.path, headers: { 'User-Agent': 'NodeJS' }};
		var content = '';
		https.get(options, function (response) {
			response.on('data', function (data) {
				content += data.toString();
			}).on('end', function () {
				c(content);
			});
		}).on('error', function (err) {
			e(err.message);
		});
	});
}

function readFile(fileName) {
	return new Promise((c, e) => {
		fs.readFile(fileName, function(err, data) {
			if (err) {
				e(err);
			} else {
				c(data.toString());
			}
		});
	});
}

function downloadBinary(source, dest) {
	if (source.startsWith('.')) {
		return copyFile(source, dest);
	}

	return new Promise((c, e) => {
		https.get(source, function (response) {
			switch(response.statusCode) {
				case 200:
					var file = fs.createWriteStream(dest);
					response.on('data', function(chunk){
						file.write(chunk);
					}).on('end', function(){
						file.end();
						c(null);
					}).on('error', function (err) {
						fs.unlink(dest);
						e(err.message);
					});
					break;
				case 301:
				case 302:
				case 303:
				case 307:
					console.log('redirect to ' + response.headers.location);
					downloadBinary(response.headers.location, dest).then(c, e);
					break;
				default:
					e(new Error('Server responded with status code ' + response.statusCode));
			}
		});
	});
}

function copyFile(fileName, dest) {
	return new Promise((c, e) => {
		var cbCalled = false;
		function handleError(err) {
			if (!cbCalled) {
				e(err);
				cbCalled = true;
			}
		}
		var rd = fs.createReadStream(fileName);
		rd.on("error", handleError);
		var wr = fs.createWriteStream(dest);
		wr.on("error", handleError);
		wr.on("close", function() {
			if (!cbCalled) {
				c();
				cbCalled = true;
			}
		});
		rd.pipe(wr);
	});
}

function darkenColor(color) {
	var res = '#';
	for (var i = 1; i < 7; i+=2) {
		var newVal = Math.round(parseInt('0x' + color.substr(i, 2), 16) * 0.9);
		var hex = newVal.toString(16);
		if (hex.length == 1) {
			res += '0';
		}
		res += hex;
	}
	return res;
}

function getLanguageMappings() {
	let langMappings = {};
	var allExtensions = fs.readdirSync('..');
	for (var i= 0; i < allExtensions.length; i++) {
		let dirPath = path.join('..', allExtensions[i], 'package.json');
		if (fs.existsSync(dirPath)) {
			let content = fs.readFileSync(dirPath).toString();
			let jsonContent = JSON.parse(content);
			let languages = jsonContent.contributes && jsonContent.contributes.languages;
			if (Array.isArray(languages)) {
				for (var k = 0; k < languages.length; k++) {
					var languageId = languages[k].id;
					if (languageId) {
						var extensions = languages[k].extensions;
						var mapping = {};
						if (Array.isArray(extensions)) {
							mapping.extensions = extensions.map(function (e) { return e.substr(1).toLowerCase(); });
						}
						var filenames = languages[k].filenames;
						if (Array.isArray(filenames)) {
							mapping.fileNames = filenames.map(function (f) { return f.toLowerCase(); });
						}
						langMappings[languageId] = mapping;
					}
				}
			}
		}
	}
	return langMappings;
}

//var font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff';
var font = '../../../seti-ui/styles/_fonts/seti/seti.woff';

exports.copyFont = function() {
	return downloadBinary(font, './icons/seti.woff');
};

//var fontMappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less';
//var mappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less';
//var colors = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less';

var fontMappings = '../../../seti-ui/styles/_fonts/seti.less';
var mappings = '../../../seti-ui/styles/components/icons/mapping.less';
var colors = '../../../seti-ui/styles/ui-variables.less';

exports.update = function () {

	console.log('Reading from ' + fontMappings);
	var def2Content = {};
	var ext2Def = {};
	var fileName2Def = {};
	var def2ColorId = {};
	var colorId2Value = {};
	var lang2Def = {};

	function writeFileIconContent(info) {
		var iconDefinitions = {};

		for (var def in def2Content) {
			var entry = { fontCharacter: def2Content[def] };
			var colorId = def2ColorId[def];
			if (colorId) {
				var colorValue = colorId2Value[colorId];
				if (colorValue) {
					entry.fontColor = colorValue;

					var entryInverse = { fontCharacter: entry.fontCharacter, fontColor: darkenColor(colorValue) };
					iconDefinitions[def + '_light'] = entryInverse;
				}
			}
			iconDefinitions[def] = entry;
		}

		function getInvertSet(input) {
			var result = {};
			for (var assoc in input) {
				let invertDef = input[assoc] + '_light';
				if (iconDefinitions[invertDef]) {
					result[assoc] = invertDef;
				}
			}
			return result;
		}

		var res = {
			information_for_contributors: [
				'This file has been generated from data in https://github.com/jesseweed/seti-ui',
				'- icon definitions: https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti.less',
				'- icon colors: https://github.com/jesseweed/seti-ui/blob/master/styles/ui-variables.less',
				'- file associations: https://github.com/jesseweed/seti-ui/blob/master/styles/components/icons/mapping.less',
				'If you want to provide a fix or improvement, please create a pull request against the jesseweed/seti-ui repository.',
				'Once accepted there, we are happy to receive an update request.',
			],
			fonts: [{
				id: "seti",
				src: [{ "path": "./seti.woff", "format": "woff" }],
				weight: "normal",
				style: "normal",
				size: "150%"
			}],
			iconDefinitions: iconDefinitions,
		//	folder: "_folder",
			file: "_default",
			fileExtensions: ext2Def,
			fileNames: fileName2Def,
			languageIds: lang2Def,
			light: {
				file: "_default_light",
				fileExtensions: getInvertSet(ext2Def),
				languageIds: getInvertSet(lang2Def),
				fileNames: getInvertSet(fileName2Def)
			},
			version: 'https://github.com/jesseweed/seti-ui/commit/' + info.commitSha,
		};

		var path = './icons/vs-seti-icon-theme.json';
		fs.writeFileSync(path, JSON.stringify(res, null, '\t'));
		console.log('written ' + path);
	}


	var match;

	return download(fontMappings).then(function (content) {
		var regex = /@([\w-]+):\s*'(\\E[0-9A-F]+)';/g;
		while ((match = regex.exec(content)) !== null) {
			def2Content['_' + match[1]] = match[2];
		}

		return download(mappings).then(function (content) {
			var regex2 = /\.icon-(?:set|partial)\('([\w-\.]+)',\s*'([\w-]+)',\s*(@[\w-]+)\)/g;
			while ((match = regex2.exec(content)) !== null) {
				let pattern = match[1];
				let def = '_' + match[2];
				let colorId = match[3];
				if (pattern[0] === '.') {
					ext2Def[pattern.substr(1).toLowerCase()] = def;
				} else {
					fileName2Def[pattern.toLowerCase()] = def;
				}
				def2ColorId[def] = colorId;
			}
			// replace extensions for languageId
			var langMappings = getLanguageMappings();
			for (var lang in langMappings) {
				var mappings = langMappings[lang];
				var exts = mappings.extensions || [];
				var fileNames = mappings.fileNames || [];
				var preferredDef = null;
				// use the first file association for the preferred definition
				for (let i1 = 0; i1 < exts.length && !preferredDef; i1++) {
					preferredDef = ext2Def[exts[i1]];
				}
				// use the first file association for the preferred definition
				for (let i1 = 0; i1 < fileNames.length && !preferredDef; i1++) {
					preferredDef = fileName2Def[fileNames[i1]];
				}
				if (preferredDef) {
					lang2Def[lang] = preferredDef;
					for (let i2 = 0; i2 < exts.length; i2++) {
						// remove the extension association, unless it is different from the preferred
						if (ext2Def[exts[i2]] === preferredDef) {
							delete ext2Def[exts[i2]];
						}
					}
					for (let i2 = 0; i2 < fileNames.length; i2++) {
						// remove the fileName association, unless it is different from the preferred
						if (fileName2Def[fileNames[i2]] === preferredDef) {
							delete fileName2Def[fileNames[i2]];
						}
					}
				}
			}


			return download(colors).then(function (content) {
				var regex3 = /(@[\w-]+):\s*(#[0-9a-z]+)/g;
				while ((match = regex3.exec(content)) !== null) {
					colorId2Value[match[1]] =  match[2];
				}
				return getCommitSha('jesseweed/seti-ui', 'styles/_fonts/seti.less').then(function (info) {
					try {
						writeFileIconContent(info);
						if (info) {
							console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')');
						}

					} catch (e) {
						console.error(e);
					}
				});
			});
		});
	}, console.error);
};

if (path.basename(process.argv[1]) === 'update-icon-theme.js') {
	exports.copyFont().then(() => exports.update());
}



