text: /*!
   * ioBroker gulpfile
   * Date: 2019-01-28
   */
  "use strict";
  
  const gulp = require("gulp");
  const fs = require("fs");
  const pkg = require("./package.json");
  const iopackage = require("./io-package.json");
  const version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
  const fileName = "words.js";
  const EMPTY = "";
  const translate = require("./build/lib/tools").translateText;
  const languages = {
  	en: {},
  	de: {},
  	ru: {},
  	pt: {},
  	nl: {},
  	fr: {},
  	it: {},
  	es: {},
  	pl: {},
  	"zh-cn": {}
  };
  
  function sanitizeJsonString(str) {
  	return str
  		.replace(/\\/g, '\\\\')
  		.replace(/"/g, '\\"')
  	;
  }
  
  function lang2data(lang, isFlat) {
  	let str = isFlat ? "" : "{\n";
  	let count = 0;
  	for (const w in lang) {
  		if (lang.hasOwnProperty(w)) {
  			count++;
  			if (isFlat) {
  				str += (lang[w] === "" ? (isFlat[w] || w) : lang[w]) + "\n";
  			} else {
  				const key = '    "' + sanitizeJsonString(w) + '": ';
  				str += key + '"' + sanitizeJsonString(lang[w]) + '",\n';
  			}
  		}
  	}
  	if (!count)
  		return isFlat ? "" : "{\n}";
  	if (isFlat) {
  		return str;
  	} else {
  		return str.substring(0, str.length - 2) + "\n}";
  	}
  }
  
  function readWordJs(src) {
  	try {
  		let words;
  		if (fs.existsSync(src + "js/" + fileName)) {
  			words = fs.readFileSync(src + "js/" + fileName).toString();
  		} else {
  			words = fs.readFileSync(src + fileName).toString();
  		}
  		words = words.substring(words.indexOf("{"), words.length);
  		words = words.substring(0, words.lastIndexOf(";"));
  
  		const resultFunc = new Function("return " + words + ";");
  
  		return resultFunc();
  	} catch (e) {
  		return null;
  	}
  }
  
  function padRight(text, totalLength) {
  	return text + (text.length < totalLength ? new Array(totalLength - text.length).join(" ") : "");
  }
  
  function writeWordJs(data, src) {
  	let text = "";
  	text += "/*global systemDictionary:true */\n";
  	text += "'use strict';\n\n";
  	text += "systemDictionary = {\n";
  	for (const word in data) {
  		if (data.hasOwnProperty(word)) {
  			text += "    " + padRight('"' + sanitizeJsonString(word) + '": {', 50);
  			let line = "";
  			for (const lang in data[word]) {
  				if (data[word].hasOwnProperty(lang)) {
  					line += '"' + lang + '": "' + padRight(sanitizeJsonString(data[word][lang]) + '",', 50) + " ";
  				}
  			}
  			if (line) {
  				line = line.trim();
  				line = line.substring(0, line.length - 1);
  			}
  			text += line + "},\n";
  		}
  	}
  	text += "};";
  	if (fs.existsSync(src + "js/" + fileName)) {
  		fs.writeFileSync(src + "js/" + fileName, text);
  	} else {
  		fs.writeFileSync(src + "" + fileName, text);
  	}
  }
  
  function words2languages(src) {
  	const langs = Object.assign({}, languages);
  	const data = readWordJs(src);
  	if (data) {
  		for (const word in data) {
  			if (data.hasOwnProperty(word)) {
  				for (const lang in data[word]) {
  					if (data[word].hasOwnProperty(lang)) {
  						langs[lang][word] = data[word][lang];
  						//  pre-fill all other languages
  						for (const j in langs) {
  							if (langs.hasOwnProperty(j)) {
  								langs[j][word] = langs[j][word] || EMPTY;
  							}
  						}
  					}
  				}
  			}
  		}
  		if (!fs.existsSync(src + "i18n/")) {
  			fs.mkdirSync(src + "i18n/");
  		}
  		for (const l in langs) {
  			if (!langs.hasOwnProperty(l))
  				continue;
  			const keys = Object.keys(langs[l]);
  			keys.sort();
  			const obj = {};
  			for (let k = 0; k < keys.length; k++) {
  				obj[keys[k]] = langs[l][keys[k]];
  			}
  			if (!fs.existsSync(src + "i18n/" + l)) {
  				fs.mkdirSync(src + "i18n/" + l);
  			}
  
  			fs.writeFileSync(src + "i18n/" + l + "/translations.json", lang2data(obj));
  		}
  	} else {
  		console.error("Cannot read or parse " + fileName);
  	}
  }
  
  function words2languagesFlat(src) {
  	const langs = Object.assign({}, languages);
  	const data = readWordJs(src);
  	if (data) {
  		for (const word in data) {
  			if (data.hasOwnProperty(word)) {
  				for (const lang in data[word]) {
  					if (data[word].hasOwnProperty(lang)) {
  						langs[lang][word] = data[word][lang];
  						//  pre-fill all other languages
  						for (const j in langs) {
  							if (langs.hasOwnProperty(j)) {
  								langs[j][word] = langs[j][word] || EMPTY;
  							}
  						}
  					}
  				}
  			}
  		}
  		const keys = Object.keys(langs.en);
  		keys.sort();
  		for (const l in langs) {
  			if (!langs.hasOwnProperty(l))
  				continue;
  			const obj = {};
  			for (let k = 0; k < keys.length; k++) {
  				obj[keys[k]] = langs[l][keys[k]];
  			}
  			langs[l] = obj;
  		}
  		if (!fs.existsSync(src + "i18n/")) {
  			fs.mkdirSync(src + "i18n/");
  		}
  		for (const ll in langs) {
  			if (!langs.hasOwnProperty(ll))
  				continue;
  			if (!fs.existsSync(src + "i18n/" + ll)) {
  				fs.mkdirSync(src + "i18n/" + ll);
  			}
  
  			fs.writeFileSync(src + "i18n/" + ll + "/flat.txt", lang2data(langs[ll], langs.en));
  		}
  		fs.writeFileSync(src + "i18n/flat.txt", keys.join("\n"));
  	} else {
  		console.error("Cannot read or parse " + fileName);
  	}
  }
  
  function languagesFlat2words(src) {
  	const dirs = fs.readdirSync(src + "i18n/");
  	const langs = {};
  	const bigOne = {};
  	const order = Object.keys(languages);
  	dirs.sort(function (a, b) {
  		const posA = order.indexOf(a);
  		const posB = order.indexOf(b);
  		if (posA === -1 && posB === -1) {
  			if (a > b)
  				return 1;
  			if (a < b)
  				return -1;
  			return 0;
  		} else if (posA === -1) {
  			return -1;
  		} else if (posB === -1) {
  			return 1;
  		} else {
  			if (posA > posB)
  				return 1;
  			if (posA < posB)
  				return -1;
  			return 0;
  		}
  	});
  	const keys = fs.readFileSync(src + "i18n/flat.txt").toString().split("\n");
  
  	for (const lang of dirs) {
  		if (lang === "flat.txt")
  			continue;
  		const values = fs.readFileSync(src + "i18n/" + lang + "/flat.txt").toString().split("\n");
  		langs[lang] = {};
  		keys.forEach(function (word, i) {
  			langs[lang][word] = values[i];
  		});
  
  		const words = langs[lang];
  		for (const word in words) {
  			if (words.hasOwnProperty(word)) {
  				bigOne[word] = bigOne[word] || {};
  				if (words[word] !== EMPTY) {
  					bigOne[word][lang] = words[word];
  				}
  			}
  		}
  	}
  	// read actual words.js
  	const aWords = readWordJs();
  
  	const temporaryIgnore = ["flat.txt"];
  	if (aWords) {
  		// Merge words together
  		for (const w in aWords) {
  			if (aWords.hasOwnProperty(w)) {
  				if (!bigOne[w]) {
  					console.warn("Take from actual words.js: " + w);
  					bigOne[w] = aWords[w];
  				}
  				dirs.forEach(function (lang) {
  					if (temporaryIgnore.indexOf(lang) !== -1)
  						return;
  					if (!bigOne[w][lang]) {
  						console.warn('Missing "' + lang + '": ' + w);
  					}
  				});
  			}
  		}
  
  	}
  
  	writeWordJs(bigOne, src);
  }
  
  function languages2words(src) {
  	const dirs = fs.readdirSync(src + "i18n/");
  	const langs = {};
  	const bigOne = {};
  	const order = Object.keys(languages);
  	dirs.sort(function (a, b) {
  		const posA = order.indexOf(a);
  		const posB = order.indexOf(b);
  		if (posA === -1 && posB === -1) {
  			if (a > b)
  				return 1;
  			if (a < b)
  				return -1;
  			return 0;
  		} else if (posA === -1) {
  			return -1;
  		} else if (posB === -1) {
  			return 1;
  		} else {
  			if (posA > posB)
  				return 1;
  			if (posA < posB)
  				return -1;
  			return 0;
  		}
  	});
  	for (const lang of dirs) {
  		if (lang === "flat.txt")
  			continue;
  		langs[lang] = fs.readFileSync(src + "i18n/" + lang + "/translations.json").toString();
  		langs[lang] = JSON.parse(langs[lang]);
  		const words = langs[lang];
  		for (const word in words) {
  			if (words.hasOwnProperty(word)) {
  				bigOne[word] = bigOne[word] || {};
  				if (words[word] !== EMPTY) {
  					bigOne[word][lang] = words[word];
  				}
  			}
  		}
  	}
  	// read actual words.js
  	const aWords = readWordJs();
  
  	const temporaryIgnore = ["flat.txt"];
  	if (aWords) {
  		// Merge words together
  		for (const w in aWords) {
  			if (aWords.hasOwnProperty(w)) {
  				if (!bigOne[w]) {
  					console.warn("Take from actual words.js: " + w);
  					bigOne[w] = aWords[w];
  				}
  				dirs.forEach(function (lang) {
  					if (temporaryIgnore.indexOf(lang) !== -1)
  						return;
  					if (!bigOne[w][lang]) {
  						console.warn('Missing "' + lang + '": ' + w);
  					}
  				});
  			}
  		}
  
  	}
  
  	writeWordJs(bigOne, src);
  }
  
  async function translateNotExisting(obj, baseText, yandex) {
  	let t = obj["en"];
  	if (!t) {
  		t = baseText;
  	}
  
  	if (t) {
  		for (let l in languages) {
  			if (!obj[l]) {
  				const time = new Date().getTime();
  				obj[l] = await translate(t, l, yandex);
  				console.log("en -> " + l + " " + (new Date().getTime() - time) + " ms");
  			}
  		}
  	}
  }
  
  //TASKS
  
  gulp.task("adminWords2languages", function (done) {
  	words2languages("./admin/");
  	done();
  });
  
  gulp.task("adminWords2languagesFlat", function (done) {
  	words2languagesFlat("./admin/");
  	done();
  });
  
  gulp.task("adminLanguagesFlat2words", function (done) {
  	languagesFlat2words("./admin/");
  	done();
  });
  
  gulp.task("adminLanguages2words", function (done) {
  	languages2words("./admin/");
  	done();
  });
  
  gulp.task("updatePackages", function (done) {
  	iopackage.common.version = pkg.version;
  	iopackage.common.news = iopackage.common.news || {};
  	if (!iopackage.common.news[pkg.version]) {
  		const news = iopackage.common.news;
  		const newNews = {};
  
  		newNews[pkg.version] = {
  			en: "news",
  			de: "neues",
  			ru: "новое",
  			pt: "novidades",
  			nl: "nieuws",
  			fr: "nouvelles",
  			it: "notizie",
  			es: "noticias",
  			pl: "nowości",
  			"zh-cn": "新"
  		};
  		iopackage.common.news = Object.assign(newNews, news);
  	}
  	fs.writeFileSync("io-package.json", JSON.stringify(iopackage, null, 4));
  	done();
  });
  
  gulp.task("updateReadme", function (done) {
  	const readme = fs.readFileSync("README.md").toString();
  	const pos = readme.indexOf("## Changelog\n");
  	if (pos !== -1) {
  		const readmeStart = readme.substring(0, pos + "## Changelog\n".length);
  		const readmeEnd = readme.substring(pos + "## Changelog\n".length);
  
  		if (readme.indexOf(version) === -1) {
  			const timestamp = new Date();
  			const date = timestamp.getFullYear() + "-" +
  					("0" + (timestamp.getMonth() + 1).toString(10)).slice(-2) + "-" +
  					("0" + (timestamp.getDate()).toString(10)).slice(-2);
  
  			let news = "";
  			if (iopackage.common.news && iopackage.common.news[pkg.version]) {
  				news += "* " + iopackage.common.news[pkg.version].en;
  			}
  
  			fs.writeFileSync("README.md", readmeStart + "### " + version + " (" + date + ")\n" + (news ? news + "\n\n" : "\n") + readmeEnd);
  		}
  	}
  	done();
  });
  
  gulp.task("translate", async function (done) {
  
  	let yandex;
  	const i = process.argv.indexOf("--yandex");
  	if (i > -1) {
  		yandex = process.argv[i + 1];
  	}
  
  	if (iopackage && iopackage.common) {
  		if (iopackage.common.news) {
  			console.log("Translate News");
  			for (let k in iopackage.common.news) {
  				console.log("News: " + k);
  				let nw = iopackage.common.news[k];
  				await translateNotExisting(nw, null, yandex);
  			}
  		}
  		if (iopackage.common.titleLang) {
  			console.log("Translate Title");
  			await translateNotExisting(iopackage.common.titleLang, iopackage.common.title, yandex);
  		}
  		if (iopackage.common.desc) {
  			console.log("Translate Description");
  			await translateNotExisting(iopackage.common.desc, null, yandex);
  		}
  
  		if (fs.existsSync("./admin/i18n/en/translations.json")) {
  			let enTranslations = require("./admin/i18n/en/translations.json");
  			for (let l in languages) {
  				console.log("Translate Text: " + l);
  				let existing = {};
  				if (fs.existsSync("./admin/i18n/" + l + "/translations.json")) {
  					existing = require("./admin/i18n/" + l + "/translations.json");
  				}
  				for (let t in enTranslations) {
  					if (!existing[t]) {
  						existing[t] = await translate(enTranslations[t], l, yandex);
  					}
  				}
  				if (!fs.existsSync("./admin/i18n/" + l + "/")) {
  					fs.mkdirSync("./admin/i18n/" + l + "/");
  				}
  				fs.writeFileSync("./admin/i18n/" + l + "/translations.json", JSON.stringify(existing, null, 4));
  			}
  		}
  
  	}
  	fs.writeFileSync("io-package.json", JSON.stringify(iopackage, null, 4));
  });
  
  gulp.task("translateAndUpdateWordsJS", gulp.series("translate", "adminLanguages2words", "adminWords2languages"));
  
  gulp.task("default", gulp.series("updatePackages", "updateReadme"));
  