#ifndef ACCEPTLANGUAGE_H
#define ACCEPTLANGUAGE_H

/*
 * @(#)AcceptLanguage.h
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 2002-2004 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDWserver.cpp for details.
 */

#include "Config.h"
#include "File.h"
#include "FileInputStream.h"
#include "Log.h"
#include "String.h"
#include "StringBuffer.h"

const int localesMax = 20;

/**
 * Support for "Accept-Language" of HTTP.
 *
 * @version 20041013
 * @author Jörg P. M. Haeger
 */
class AcceptLanguage {
	struct {
		char l1, l2, c1, c2;
		int q;
	} locales[localesMax];
	int count;
	String webCDcreatorLanguage, webCDcreatorCountry;
	int fileLastModified;

public:
	AcceptLanguage() {
		log.debug("AcceptLanguage", "()");
		count = 0;
		webCDcreatorLanguage = "";
		webCDcreatorCountry = "";
		fileLastModified = 0;
	}

	~AcceptLanguage() {
		log.debug("AcceptLanguage", "~() count = %d", count);
	}

	void operator=(AcceptLanguage &orig) {
		log.debug("AcceptLanguage", "operator=");
		for (int i = 0; i < orig.count; i++)
			locales[i] = orig.locales[i];
		count = orig.count;
		webCDcreatorLanguage = orig.webCDcreatorLanguage;
		webCDcreatorCountry = orig.webCDcreatorCountry;
	}

	void add(String &language) {
		if (count == localesMax)
			return;

		if (language.length() < 2)
			return;

		char l1 = language.charAt(0);
		if (l1 < 'a')
			l1 += ('a' - 'A');

		char l2 = language.charAt(1);
		if (l2 < 'a')
			l2 += ('a' - 'A');

		if (l1 < 'a' || l1 > 'z'
			|| l2 < 'a' || l2 > 'z')
			return;

		char c1 = ' ', c2 = ' ';
		int i = 2;
		if (i + 2 < language.length()
				&& (language.charAt(i) == '-'
					|| language.charAt(i) == '_')) {
			c1 = language.charAt(i + 1);
			if (c1 > 'Z')
				c1 -= ('z' - 'Z');

			c2 = language.charAt(i + 2);
			if (c2 > 'Z')
				c2 -= ('z' - 'Z');

			if (c1 < 'A' || c1 > 'Z'
				|| c2 < 'A' || c2 > 'Z')
				return;

			i += 3;
		}

		int q = 100;
		if (i + 2 < language.length()
				&& language.charAt(i) == ';'
				&& language.charAt(i + 1) == 'q'
				&& language.charAt(i + 2) == '=') {
			i += 3;
			while (i < language.length() && language.charAt(i) == '0')
				i++;

			if (i + 1 < language.length()
				&& language.charAt(i) == '.'
				&& language.charAt(i + 1) >= '0'
				&& language.charAt(i + 1) <= '9')
				q = 10 * (language.charAt(i + 1) - '0');

			if (i + 2 < language.length()
				&& language.charAt(i + 2) >= '0'
				&& language.charAt(i + 2) <= '9')
				q += language.charAt(i + 2) - '0';
		}

		for (i = 0; i < count && locales[i].q >= q; i++)
			;

		for (int j = count; j > i; j--)
			locales[j] = locales[j - 1];

		locales[i].l1 = l1;
		locales[i].l2 = l2;
		locales[i].c1 = c1;
		locales[i].c2 = c2;
		locales[i].q = q;

		log.debug("AcceptLanguage", "%c%c%c%c %d",
			locales[i].l1, locales[i].l2,
			locales[i].c1, locales[i].c2,
			locales[i].q);

		count++;
	}

	int getFileLastModified() {
		return fileLastModified;
	}

	String *getLocales() {
		StringBuffer str;
		for (int i = 0; i < count; i++) {
//			str.append(i + 1);
//			str.append(": ");
			append(str, i);
			if (i < count - 1)
				str.append(", ");
		}
		return new String(str);
	}

	String &getWebCDcreatorCountry() {
		getWebCDcreatorLanguage();
		return webCDcreatorCountry;
	}

	String &getWebCDcreatorLanguage() {
		if (webCDcreatorLanguage.length() == 0)
		for (int i = 0; i < count; i++) {
			StringBuffer strBuf;
			strBuf.append("webCDcreator/i18n/Messages_");
			append(strBuf, i);
			strBuf.append(".properties");
			String name = new String(strBuf);
//			log.debug("AcceptLanguage", "%s", name.getBytes());

			File msgsFile = new File(config.getHttpDir(), name);
			if (msgsFile.exists()) {
				webCDcreatorLanguage = webCDcreatorLanguage
					+ locales[i].l1 + locales[i].l2;
				if (locales[i].c1 >= 'A')
					webCDcreatorCountry = webCDcreatorCountry
						+ locales[i].c1 + locales[i].c2;
				break;
			}
		}

		return webCDcreatorLanguage;
	}

	static int i18n(StringBuffer &buf,
			int p0, int p1, StringBuffer &msgs) {
		int i = 0, n = msgs.length();
		for (; i < n; i++) {
			int j = 0;
			for (; p0 + j < p1; i++, j++)
				if (buf.charAt(p0 + j) != msgs.charAt(i))
					break;

			if (p0 + j == p1) {
				while (i < n && msgs.charAt(i) == ' ')
					i++;
				if (i < n && msgs.charAt(i) == '=')
					break;
			}

			// go to the end of the line
			while (i < n && msgs.charAt(i) != '\n')
				i++;
		}
//		printf("n = %d, i = %d, p0 = %d, p1 = %d\n", n, i, p0, p1);

		if (i < n) {
			buf.delete_(p0, p1);
			i++;
			while (i < n && msgs.charAt(i) == ' ')
				i++;
			for (; i < n && msgs.charAt(i) != '\n'; i++, p0++) {
				if (msgs.charAt(i) == '\\'
					&& i + 1 < n && msgs.charAt(i + 1) == '\n') {
					// skip \ + LF
					i += 2;
					if (i == n || msgs.charAt(i) == '\n')
						break;
				}
				buf.insert(p0, (char)msgs.charAt(i));
			}
		}

		return i;
	}

	StringBuffer &i18n(StringBuffer &buf) {
		StringBuffer msgsBufs[count + 1];
		int n = 0;
		for (int i = 0; i < count; i++) {
			String name = "messages";
			name = name + '_' + locales[i].l1 + locales[i].l2;
			if (locales[i].c1 >= 'A')
				name = name + '_' + locales[i].c1 + locales[i].c2;

			File msgsFile = new File(config.getHttpDir(), name);
			if (msgsFile.exists())
				msgsBufs[n++] = read(msgsFile);

			if (name.equals("messages_en"))
				break;
		}

		// add the default messages
		File msgsFile = new File(config.getHttpDir(), "messages");
		if (msgsFile.exists())
			msgsBufs[n++] = read(msgsFile);

		for (int i = 0; i < 1000; i++) {
			int p0 = buf.indexOf("<i18n>");
			if (p0 < 0)
				break;
			buf.delete_(p0, p0 + 6);

			int p1 = buf.indexOf("</i18n>", p0);
			if (p1 < 0)
				break;
			buf.delete_(p1, p1 + 7);

			// look for a translation
			for (int j = 0; j < n; j++)
				if (i18n(buf, p0, p1, msgsBufs[j])
						< msgsBufs[j].length())
					break;
		}
		return buf;
	}

	StringBuffer *load(File &file) {
		String str = file.getPath();

		int dot = str.length();
		for (int i = str.length() - 1; i >= 0; i--)
			if (str.charAt(i) == '.') {
				dot = i;
				break;
			}

		if (dot > 2 && str.charAt(dot - 3) == '_') {
			if (!file.exists()) {
				String path = file.getPath();
				int p0 = path.indexOf("_en.");
				if (p0 > 0) {
					String path2 = "";
					path2 = path2
						+ path.substring(0, p0)
						+ path.substring(p0 + 3);
					file = new File(path2);
				}
			}
			fileLastModified = file.lastModified();
			return read(file);
		}

		for (int i = 0; i < count; i++) {
			StringBuffer buf;
			for (int j = 0; j < dot; j++)
				buf.append((char)str.charAt(j));
			buf.append('_');
			append(buf, i);
			for (int j = dot; j < str.length(); j++)
				buf.append((char)str.charAt(j));
//			log.debug("AcceptLanguage", "%s", String(buf).getBytes());

			File f = new File(new String(buf));
			if (f.exists()) {
				fileLastModified = f.lastModified();
				return read(f);
			}

			if (locales[i].l1 == 'e'
					&& locales[i].l2 == 'n'
					&& locales[i].c1 < 'A'
					&& file.exists()) {
				fileLastModified = file.lastModified();
				return read(file);
			}
		}

		fileLastModified = file.lastModified();
		return read(file);
	}

private:
	StringBuffer &append(StringBuffer &strBuf, int i) {
		strBuf.append(locales[i].l1);
		strBuf.append(locales[i].l2);
		if (locales[i].c1 >= 'A') {
			strBuf.append('_');
			strBuf.append(locales[i].c1);
			strBuf.append(locales[i].c2);
		}
		return strBuf;
	}

	static StringBuffer *read(File &file) {
		StringBuffer *str = new StringBuffer(file.getSize());

		FileInputStream in = new FileInputStream(file);
		while (1) {
			char buf[4094];
			int n = in.read(buf, 0, sizeof buf);
			if (n <= 0)
				break;
			str->append(buf, 0, n);
		}

		return str;
	}
};

#endif
