#ifndef CONFIG_H
#define CONFIG_H

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

#include <stdio.h>
#include <string.h>
#include "Boolean.h"
#include "Error.h"
#include "File.h"
#include "Integer.h"
#include "Process.h"
#include "String.h"
#include "Support.h"
#include "Types.h"
#include "Log.h"

#include "config.h"

const int maxNumOfReaders = 10, maxNumOfWriters = 10;

/**
 * delivery of the values of the configuration file
 *
 * @version 20030225
 * @author Jörg P. M. Haeger
 */
class Config {
	const char *cdrecordInfo;
	const char *configDir;
	const char *fileName;
	Exception *firstError;
	const char *mkisofsInfo;
	int numOfReaders, numOfWriters;
	String text;
	boolean HTML;
	const char *readerInfos[maxNumOfReaders];
	const char *writerInfos[maxNumOfWriters];

	// values
	boolean accounts;
	const char *accountsFile;
	boolean anonymousUse;
	boolean burnProof;
	boolean cdrdao;
	const char *cdrdaoArgs;
	const char *cdrdaoPath;
	const char *cdrecordArgs;
//	const char *cdrecordPath;
	const char *CDROMs[maxNumOfReaders];
	boolean copySupport;
	const char *debug;
	const char *defaultFilesDir;
	int defaultSpeed;
	int DVDplusRWSupport[maxNumOfWriters];
	boolean ejectOnDone;
	boolean ejectOnReservation;
	const char *eMailOnError;
	boolean exportArchive;
	const char *ftpPubDir;
	gid_t gid;
	const char *group;
	const char *homeDir;
	const char *imageFile[maxNumOfWriters];
	boolean imageOnTheFly;
	const char *infoFile;
	boolean laserOn;
	const char *logDir;
	const char *logoURL;
	int logLevel;
	int maxCopies;
	int maxMBytesInSpoolDir;
	int maxMBytesPerSession;
	int maxOpenConnections;
	int maxOpenSessions;
	int maxSpeed;
	const char *mkisofsArgs;
	const char *mkisofsPath;
	boolean MP3decoding;
	const char *mpg123Info;
	const char *mpg123Path;
	const char *organization;
	unsigned short int portNo;
	const char *readerRawDevices[maxNumOfReaders];
	boolean removeSessions;
	int reservedMBytes;
	const char *setSchedulerPath;
	boolean showUsers;
	const char *spoolDir;
	unsigned short int statusPortNo;
	uid_t uid;
	const char *user;
	boolean verify;
	const char *verifyPath;
	int waitForCD;
	const char *waitForCDMsgFile;
	const char *writers[maxNumOfWriters];
	const char *writerRawDevices[maxNumOfWriters];

	struct Item {
		String key;
		Object &default0, &config, &active, &example;
		boolean isConfig, isDefault;

		public: Item(const char *key, boolean value):
			key(key),
			default0(*new Boolean(value)),
			config(*new Boolean(false)),
			active(*new Boolean(value)),
			example(*new Boolean(false)) {
			isConfig = isDefault = false;
		}

		public: Item(const char *key, int value):
			key(key),
			default0(*new Integer(value)),
			config(*new Integer(-1)),
			active(*new Integer(value)),
			example(*new Integer(-1)) {
			isConfig = isDefault = false;
		}

		public: Item(const char *key, const char *value,
			const char *example = ""):
			key(key),
			default0(*new String(value)),
			config(*new String()),
			active(*new String(value)),
			example(*new String(example)) {
			isConfig = isDefault = false;
		}
	};
	Item *items[100];
	int numOfItems;

	public: Config();

/*	private: void activateDefaults() {
		for (int i = 0; i < numOfItems; i++) {
			items[i]->active = items[i]->default0;
			items[i]->isDefault = true;
		}
	}
*/
	private: int add(Item *item) {
		const char *key = item->key.getBytes();
		int index = search(key);
		if (index >= 0)
			throw new Exception(S.e + key + " already exists");

		for (index = 0; index < numOfItems; index++)
			if (items[index]->key.compareToIgnoreCase(key) > 0)
				break;

		for (int i = numOfItems; i > index; i--)
			items[i] = items[i - 1];

		numOfItems++;
		items[index] = item;
		items[index]->key = key;
		items[index]->isDefault = true;
		return index;
	}

	private: void add(const char *key, boolean value) {
		add(new Item(key, value));
	}

	private: void add(const char *key, const char *value) {
		add(new Item(key, value));
	}

	private: void add(const char *key, int value) {
		add(new Item(key, value));
	}

	private: void cdrecordInq(int writerNo);

	private: void cdrecordHelp() {
		Process p(getCdrecordPath());
		p.addArg("-help");
		p.start();

		while (1) {
			const char *lineStr = p.readLine();
			if (lineStr == NULL)
				break;

			if (strstr(lineStr, "gracetime=#") != NULL)
				set("gracetime", 2);
		}
	}

	private: void cdrecordScanbus();

	private: void cdrecordVersion() {
		Process p(getCdrecordPath());
		p.addArg("-version");
		p.start();
		const char *lineStr = p.readLine();
		if (lineStr == NULL)
			throw new Exception("can't run cdrecord");
		if (p.getResult() != 0)
			throw new Exception("error running cdrecord -version");
		char str1[strlen(lineStr) + 1], str2[strlen(lineStr) + 1];
		if (sscanf(lineStr, "%s %s", str1, str2) != 2)
			throw new Exception("can't get version of cdrecord");
		char *str3 = new char[strlen(str1) + strlen(str2) + 2];
		sprintf(str3, "%s %s", str1, str2);
		cdrecordInfo = str3;
		log.put(lineStr);
	}

	public: boolean getAccounts() {
		return accounts;
	}

	const char *getAccountsFile() {
		return accountsFile;
	}

	boolean getActiveBoolean(const char *key) {
		int index = indexOf(key);
		Object &object = items[index]->active;
		if (!object.instanceof("Boolean"))
			throw new Exception("invalid item in getActiveBoolean");
		Boolean *b = (Boolean *)&object;
		return b->booleanValue();
	}

	int getActiveInt(const char *key) {
		int index = indexOf(key);
		Object &object = items[index]->active;
		if (!object.instanceof("Integer"))
			throw new Exception("invalid item in getActiveInt");
		Integer *i = (Integer *)&object;
		return i->intValue();
	}

	String &getActiveValue(const char *key) {
		int index = indexOf(key);
		Object &object = items[index]->active;
		if (!object.instanceof("String"))
			throw new Exception("invalid item in getActiveValue");
		String *s = (String *)&object;
		return *s;
	}

	boolean getAnonymousUse() {
		return anonymousUse;
	}

	boolean getBurnProof() {
		return burnProof;
	}

	boolean getCdrdao() {
		return cdrdao;
	}

	const char *getCdrdaoArgs() {
		return cdrdaoArgs;
	}

	const char *getCdrdaoPath() {
		return cdrdaoPath;
	}

	const char *getCdrecordArgs() {
		return cdrecordArgs;
	}

	const char *getCdrecordInfo() {
		return cdrecordInfo;
	}

	const char *getCdrecordPath() {
		return getActiveValue("cdrecordPath").getBytes();
	}

	const char *getConfigDir() {
		return configDir;
	}

	private: String *getCopyName(File &binary) {
		File binDir = new File(homeDir, "bin");
		File copy = new File(binDir, binary.getName().getBytes());
		return new String(copy.getPath());
	}

	public: boolean getCopySupport() {
		return copySupport && numOfReaders > 0;
	}

	const char *getDebug() {
		return debug;
	}

	const char *getDefaultFilesDir() {
		return defaultFilesDir;
	}

	int getDefaultSpeed() {
		return defaultSpeed;
	}

	int getDVDplusRWSupport(int writerNo) {
		return DVDplusRWSupport[writerNo];
	}

	boolean getEjectOnDone() {
		return ejectOnDone;
	}

	boolean getEjectOnReservation() {
		return ejectOnReservation;
	}

	const char *getEMailOnError() {
		return eMailOnError;
	}

	boolean getExportArchive() {
		return exportArchive;
	}

	const char *getFileName() {
		return fileName;
	}

	Exception *getFirstError() {
		return firstError;
	}

	const char *getFtpPubDir() {
		return ftpPubDir;
	}

	uid_t getGid() {
		return gid;
	}

	const char *getGroup() {
		return group;
	}

	const char *getHomeDir() {
		return homeDir;
	}

	const char *getImageFile(int writerNo) {
		return imageFile[writerNo];
	}

	boolean getImageOnTheFly() {
		return imageOnTheFly;
	}

	const char *getInfoFile() {
		return infoFile;
	}

	int getLaserOn() {
		return laserOn;
	}

	const char *getLogDir() {
		return logDir;
	}

	const char *getLogoURL() {
		return logoURL;
	}

	int getLogLevel() {
		return logLevel;
	}

	int getMaxCopies() {
		return maxCopies;
	}

	int getMaxMBytesInSpoolDir() {
		return maxMBytesInSpoolDir;
	}

	int getMaxMBytesPerSession() {
		return maxMBytesPerSession;
	}

	int getMaxOpenConnections() {
		return maxOpenConnections;
	}

	int getMaxOpenSessions() {
		return maxOpenSessions;
	}

	int getMaxSpeed() {
		return maxSpeed;
	}

	const char *getMkisofsArgs() {
		return mkisofsArgs;
	}

	const char *getMkisofsInfo() {
		return mkisofsInfo;
	}

	const char *getMkisofsPath() {
		return mkisofsPath;
	}

	boolean getMP3decoding() {
		return MP3decoding;
	}

	const char *getMpg123Info() {
		return mpg123Info;
	}

	const char *getMpg123Path() {
		return mpg123Path;
	}

	int getNumOfReaders() {
		return numOfReaders;
	}

	int getNumOfWriters() {
		return numOfWriters;
	}

	const char *getOrganization() {
		return organization;
	}

	unsigned short int getPortNo() {
		return getActiveInt("portNo");
	}

	const char *getReader(int no) {
		return CDROMs[no];
	}

	const char *getReaderInfo(int no) {
		return readerInfos[no];
	}

	const char *getReaderRawDevice(int no) {
		return readerRawDevices[no];
	}

	boolean getRemoveSessions() {
		return removeSessions;
	}

	int getReservedMBytes() {
		return reservedMBytes;
	}

	const char *getSetSchedulerPath() {
		return setSchedulerPath;
	}

	boolean getShowUsers() {
		return showUsers;
	}

	const char *getSpoolDir() {
		return spoolDir;
	}

	unsigned short int getStatusPortNo() {
		return statusPortNo;
	}

	uid_t getUid() {
		return uid;
	}

	const char *getUser() {
		return user;
	}

	boolean getVerify() {
		return verify;
	}

	const char *getVerifyPath() {
		return verifyPath;
	}

	int getWaitForCD() {
		return waitForCD;
	}

	const char *getWaitForCDMsgFile() {
		return waitForCDMsgFile;
	}

	const char *getWriter(int no) {
		return writers[no];
	}

	const char *getWriterInfo(int writerNo) {
		return writerInfos[writerNo];
	}

	const char *getWriterRawDevice(int writerNo) {
		return writerRawDevices[writerNo];
	}

	int isDefault(const char *key) {
		int index = search(key);
		if (index < 0)
			throw new Exception(S.e + key + " does not exist");
		return items[index]->isDefault;
	}

	static void parseLine(
		const char *lineStr, char *str1, char *str2);
	void prepareBinaries();
	void reload();

	private: int search(const char *key) {
		return search(key, 0, numOfItems - 1);
	}

	private: int search(const char *key, int a, int b) {
		if (a > b)
			return -1;

		int m = (a + b) / 2;
		int diff = items[m]->key.compareToIgnoreCase(key);
		if (diff > 0)
			return search(key, a, m - 1);
		else
			if (diff < 0)
				return search(key, m + 1, b);
			else
				return m;
	}

	private: void set(const char *key, boolean value) {
		int index = indexOf(key);
		*(Boolean *)&items[index]->active = new Boolean(value);
		items[index]->isConfig = false;
		items[index]->isDefault = false;
	}

	private: void set(const char *key, int value) {
		int index = indexOf(key);
		*(Integer *)&items[index]->active = new Integer(value);
		items[index]->isConfig = false;
		items[index]->isDefault = false;
	}

	private: void set(const char *key, const char *value) {
		int index = indexOf(key);
		*(String *)&items[index]->active = new String(value);
		items[index]->isConfig = false;
		items[index]->isDefault = false;
	}

	private: void setConfig(const char *key, const char *value) {
		int index = indexOf(key);
		if (items[index]->active.instanceof("Boolean"))
			set(key, Boolean::getBoolean(value));
		else
		if (items[index]->active.instanceof("Integer"))
			set(key, Integer::getInt(value));
		else
			set(key, value);
		items[index]->isConfig = true;
	}

	public: void setPermissions();
	public: String &toHTML();

	private: void checkExecAsRoot(const char *fileName);
	private: void evaluate(const char *str1, const char *str2);
	private: const char *getRawDevice(
		int bus, int ID, int LUN, int no);

	private: int indexOf(const char *key) {
		int index = search(key);
		if (index < 0)
			throw new Exception(S.e + key + " does not exist");
		return index;
	}

	private: void parseCDROMs(
		const char *str, const char **list, int &count);
	private: void prepareBinary(const char *&path);
	private: void readConfigFile();
	private: void testMkisofs();
	private: void testMpg123();
	static gid_t toGid(const char *groupName);
	static const char *toGroup(gid_t gid);
	static const char *toString(boolean b);
	static uid_t toUid(const char *userName);
	static const char *toUser(uid_t uid);
	void write();
	void write(const char *str);
	void write(const char *str1, const char *str2);
	void write(const char *str, int number);
	void writeConfigFile();
	void writeInfoFile();
	void writeWaitForCDMsgFile();
};

extern Config config;

#endif
