/*
    Copyright 1991-2020 Amebis
    Copyright 2016 GÉANT

    This file is part of MSIBuild.

    MSIBuild is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MSIBuild is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MSIBuild. If not, see <http://www.gnu.org/licenses/>.
*/

/*@cc_on @*/
/*@if (! @_escapePO_JS__) @*/
/*@set @_escapePO_JS__ = true @*/

var escapePO_stat = null;
function escapePO(str)
{
	if (!escapePO_stat) {
		escapePO_stat = {
			"re_bslash": new RegExp("\\\\", "g"),
			"re_bs":     new RegExp("\b",   "g"),
			"re_ff":     new RegExp("\f",   "g"),
			"re_lf":     new RegExp("\n",   "g"),
			"re_cr":     new RegExp("\r",   "g"),
			"re_tab":    new RegExp("\t",   "g"),
			"re_quot":   new RegExp("\"",   "g")
		};
	}

	if (str == null) return null;
	switch (typeof(str)) {
		case "string":    break;
		case "undefined": return null;
		default:          try { str = str.toString(); } catch (err) { return null; }
	}

	return str.replace(escapePO_stat.re_bslash, "\\\\").replace(escapePO_stat.re_bs, "\\b").replace(escapePO_stat.re_ff, "\\f").replace(escapePO_stat.re_lf, "\\n").replace(escapePO_stat.re_cr, "\\r").replace(escapePO_stat.re_tab, "\\t").replace(escapePO_stat.re_quot, "\\\"");
}


var unescapePO_stat = null;
function unescapePO(str)
{
	if (!unescapePO_stat) {
		unescapePO_stat = {
			"re_esc": new RegExp("\\\\(.)", "g"),
			"fn_esc": function($0, $1) {
				switch ($1) {
					case "\\": return "\\";
					case "b" : return "\b";
					case "f" : return "\f";
					case "n" : return "\n";
					case "r" : return "\r";
					case "t" : return "\t";
					case "\'": return "\'";
					case "\"": return "\"";
					default  : return $0;
				}
			}
		};
	}

	if (str == null) return null;
	switch (typeof(str)) {
		case "string":    break;
		case "undefined": return null;
		default:          try { str = str.toString(); } catch (err) { return null; }
	}

	return str.replace(unescapePO_stat.re_esc, unescapePO_stat.fn_esc);
}


function POCatalog()
{
	this.charset = "utf-8";
	this.data = new Array();

	if (arguments.length) {
		var
			path = arguments[0];

		// Open file.
		var dat = new ActiveXObject("ADODB.Stream");
		dat.Open();
		try {
			// PO catalogue is text file, uses Unix line breaks and UTF-8.
			dat.Type = adTypeText;
			dat.LineSeparator = adLF;
			dat.Charset = this.charset;

			// Load file.
			dat.LoadFromFile(path);

			// Prepare regular expressions for catalogue parsing.
			var regexp = {
				"fuzzy":   new RegExp("^\\s*#,\\s*fuzzy", "i"),
				"comment": new RegExp("^\\s*#", ""),
				"record":  new RegExp("^\\s*(\\w*)\\s*\"(.*)\"\\s*$", "")
			};

			var getNext = function() {
				var
					id = null,
					rec = new Array();

				while (!dat.EOS) {
					var
						s = Trim(dat.ReadText(adReadLine)),
						m;

					// The line is empty => end of record.
					if (s == "" && "msgid" in rec)
						break;

					// The record is marked as "fuzzy".
					if (s.search(regexp.fuzzy) != -1) {
						rec["fuzzy"] = true;
						continue;
					}

					// Concatenate comments.
					if (s.search(regexp.comment) != -1) {
						if ("#" in rec)
							rec["#"] += s + "\n";
						else
							rec["#"]  = s + "\n";
						continue;
					}

					m = s.match(regexp.record);
					if (m) {
						// Record found.
						if (m[1] != "")
							id = new String(m[1]);
						if (id in rec)
							rec[id] += new String(m[2]);
						else
							rec[id]  = new String(m[2]);
						continue;
					}
				}

				// Unescape messages now that they are concatenated.
				for (id in rec)
					rec[id] = unescapePO(rec[id]);
				return "msgid" in rec ? rec : null;
			}

			// Read header record.
			var rec = getNext();
			if (rec == null)
				return;
			if (rec.msgid == "") {
				// Parse charset.
				m = rec.msgstr.match(new RegExp("^\\s*Content-Type\\s*:\\s*([-\\w]+/[-\\w]+)\\s*(;\\s*charset\\s*=\\s*([-\\w]+))?$", "im"));
				if (m && m.length >= 4) {
					this.charset = m[3];

					// Rewind and reconfigure code page.
					dat.Position = 0;
					dat.Charset = this.charset;

					// Re-read header.
					rec = getNext();
				}
			}

			// Load header and all the records.
			this.push(rec.msgid, rec.msgstr, rec.fuzzy, "#" in rec ? rec["#"] : null);
			while ((rec = getNext()) != null)
				this.push(rec.msgid, rec.msgstr, rec.fuzzy, "#" in rec ? rec["#"] : null);
		} finally {
			dat.Close();
		}
	}
}


POCatalog.prototype.push = function(key, trans, fuzzy, comment)
{
	var rec = {
		"msgstr" : trans,
		"fuzzy"  : fuzzy ? true : false
	};
	if (comment)
		rec["#"] = comment;

	this.data[key] = rec;
}


POCatalog.prototype.search = function(key)
{
	return (key in this.data) ? this.data[key] : null;
}


POCatalog.prototype.translate = function(key)
{
	if (!(key in this.data)) {
		// No translation found.
		return key;
	}

	var rec = this.data[key];
	if (rec.msgstr == "" || rec.fuzzy) {
		// Translation is blank (untranslated) or fuzzy.
		return key;
	}

	return rec.msgstr;
}


POCatalog.prototype.save = function(path)
{
	// Open PO file in memory.
	var dat = new ActiveXObject("ADODB.Stream");
	dat.Open();
	try {
		// PO is text file, uses Unix line breaks and given encoding.
		dat.Type = adTypeText;
		dat.LineSeparator = adLF;
		dat.Charset = this.charset;

		var writeRec = function(key, rec) {
			if (rec.fuzzy)
				dat.WriteText("#, fuzzy", adWriteLine);

			if ("#" in rec)
				dat.WriteText(rec["#"], adWriteLine);

			dat.WriteText("msgid \""  + escapePO(key       ) + "\"", adWriteLine);
			dat.WriteText("msgstr \"" + escapePO(rec.msgstr) + "\"", adWriteLine);
			dat.WriteText("", adWriteLine);
		}

		// Write header first.
		if ("" in this.data)
			writeRec("", this.data[""]);

		// Write records, skip header.
		for (var key in this.data) {
			if (key == "") continue;
			writeRec(key, this.data[key]);
		}

		if (this.charset.toLowerCase() == "utf-8") {
			// Write to file without UTF-8 BOM.
			var dat_nobom = new ActiveXObject("ADODB.Stream");
			dat_nobom.Type = adTypeBinary;
			dat_nobom.Mode = adModeReadWrite;
			dat_nobom.Open();
			try {
				// Skip BOM (first three bytes) and copy the rest.
				dat.Position = 3;
				dat.CopyTo(dat_nobom);

				dat_nobom.SaveToFile(path, adSaveCreateOverWrite);
			} finally {
				dat_nobom.Close();
			}
		} else {
			dat.SaveToFile(path, adSaveCreateOverWrite);
		}
	} finally {
		dat.Close();
	}
}

/*@end @*/
