/* This file is part of the KDE Linux Kernel Configurator
   Copyright (c) 2001 Malte Starostik <malte@kde.org>

   This program 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 2 of the License, or (at your option) any later version.
 
   This program 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 this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

// $Id: configparser.h,v 1.18 2003/07/26 21:15:33 mueller Exp $

#ifndef _CONFIGPARSER_H_
#define _CONFIGPARSER_H_

#include <qptrlist.h>
#include <qmap.h>
#include <qptrstack.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qvaluelist.h>
#include <qregexp.h>
#include <kdebug.h>

class QTextStream;

namespace Config
{
	// In the following, all parameters passed to the constructors
	// that are pointers will be either deleted immediately in the
	// constructor or claimed by the new object and deleted later.
	class Node;
	class VariableNode;
	class RootNode;

	typedef QPtrList<Node> NodeList;
	typedef QPtrList<VariableNode> VariableList;
	typedef QMap<QString, QString> SymbolTable;

	class RuleFile
	{
	public:
		RuleFile(const QString &file);
		virtual ~RuleFile();

		const QString &name() const { return m_name; }
		const QString &data() const { return m_data; }
		int line() const { return m_line; }
		void nextLine() { m_line++; m_column = m_tokenLength = 0; }
		int pos() const { return m_column; }
		int tokenLength() const { return m_tokenLength; }
		void addPos(int offset) { m_column += m_tokenLength = offset; }
		void *buffer() const { return m_buffer; }
		void setBuffer(void *buffer) { m_buffer = buffer; }
		QString currentLine() const;
		 
	private:
		QString m_name;
		QString m_data;
		int m_line;
		int m_column;
		int m_tokenLength;
		void *m_buffer;
	};

	class ErrorInfo
	{
	public:
		ErrorInfo() {};
		ErrorInfo(const QString &message);

		const QString &message() const { return m_message; }
		const QString &file() const { return m_file; }
		const QString &text() const { return m_text; }
		int line() const { return m_line; }
		int pos() const { return m_pos; }
		int length() const { return m_length; }

	private:
		QString m_message;
		QString m_file;
		QString m_text;
		int m_line;
		int m_pos;
		int m_length;
	};
   
	typedef QValueList<ErrorInfo> ErrorList;

	class Parser
	{
	public:
		Parser();
		virtual ~Parser();

		bool parseConfig(const QString &kernelRoot, const QString &arch);
		QStringList availableArchs(const QString &root) const;
		const QString &kernelRoot() const { return m_kernelRoot; }
		const QString &arch() const { return m_arch; }

		bool readConfig(const QString &file);
		bool writeConfig(const QString &file) const;
		bool writeHeader(const QString &file) const;
		void apply() const;
		const QString &symbol(const QString &name) const;
		void setSymbol(const QString &name, const QString &value);
		void unsetSymbol(const QString &name);

		bool pushInclude(const QString &file);
		bool popInclude();
		RuleFile *currentFile() const { return m_ruleStack.top(); }

		void addError(const ErrorInfo &info) { m_errors.append(info); }
		const ErrorList &errors() const { return m_errors; }

		QString helpText(const QString &symbol);

		RootNode *root() const { return m_root; }
		void setRoot(Config::RootNode *root);

		static Parser *self() { return s_self; }
   
	private:
		void makeHTMLLinks(QString &text, const QString &regexp,
			const QString &prefix = QString::null) const;
		 
	private:
		static Parser *s_self;

		QString m_kernelRoot;
		QString m_arch;
		RootNode *m_root;
		SymbolTable m_symbols;
		QPtrStack<RuleFile> m_ruleStack;
		ErrorList m_errors;
		QStringList m_help;
		QString m_helpTemplate;
	};

	class Node
	{
	public:
		enum Type
		{
			Variable,
			DependencyList,
			Define,
			Unset,
			Input,
			Choice,
			// ExpressionNode and subclasses
			Expression,
			// TextNodeBase derived classes
			MainMenuName,
			Comment,
			Text,
			// BranchNodeBase derived classes
			Root,
			Menu,
			If
		};
		virtual ~Node() {};
		virtual Type type() const = 0;
		virtual void initialize() {};
		virtual void apply() const {};
		virtual void write(QTextStream &) const {};
		virtual void writeHeader(QTextStream &) const {};
	};

	class VariableNode : public Node
	{
	public:
		VariableNode(QString *value) : m_value(*value) { delete value; }
		virtual ~VariableNode() {};

		virtual Type type() const { return Variable; }

		const QString &value() const;

	private:
		QString m_value;
	};

	class DependencyListNode : public Node
	{
	public:
		DependencyListNode(VariableList *values) : m_values(values) {};
		virtual ~DependencyListNode() { delete m_values; }

		virtual Type type() const { return DependencyList; }

		bool hasValue(const QString &value) const;

	private:
		VariableList *m_values;
	};

	class DefineNode : public Node
	{
	public:
		enum DefineType { Bool, Int, Hex, String, Tristate };

		DefineNode(DefineType type, QString *symbol, VariableNode *value)
			: m_type(type), m_symbol(*symbol), m_value(value) { delete symbol; }
		virtual ~DefineNode() { delete m_value; }

		virtual Type type() const { return Define; }
		virtual void apply() const
		{
			Parser::self()->setSymbol(m_symbol, m_value->value());
		}
		virtual void write(QTextStream &str) const;
		virtual void writeHeader(QTextStream &) const;

	private:
		DefineType m_type;
		QString m_symbol;
		VariableNode *m_value;
	};

	class UnsetNode : public Node
	{
	public:
		UnsetNode(QStringList *symbols)
			: m_symbols(*symbols) { delete symbols; }
		virtual ~UnsetNode() {};

		virtual Type type() const { return Unset; }
		virtual void apply() const;

	private:
		QStringList m_symbols;
	};

	class InputNode : public Node
	{
	public:
		enum InputType { Bool, Hex, Int, String, Tristate };
		InputNode(QString *prompt, QString *symbol,
			VariableNode *defValue, DependencyListNode *dependencies)
			: m_prompt(*prompt), m_symbol(*symbol),
			  m_default(defValue),
			  m_dependencies(dependencies)
		{
			delete prompt;
			delete symbol;
		}
		virtual ~InputNode() { delete m_default; delete m_dependencies; }

		virtual Type type() const { return Input; }
		virtual void initialize();
		virtual void apply() const {
			Parser::self()->setSymbol(m_symbol, value());
		}
		virtual void write(QTextStream &str) const;

		const QString &prompt() const { return m_prompt; }
		const QString &symbol() const { return m_symbol; }
		virtual bool isAvailable() const;
		virtual InputType inputType() const = 0;
		virtual QString value() const = 0;
		void setValue(const QString &value);

	protected:
		virtual void internalSetValue(const QString &value) = 0;

	private:
		QString m_prompt;
		QString m_symbol;
		QString m_value;
		VariableNode *m_default;

	protected:
		DependencyListNode *m_dependencies;
	};

	class BoolInputNode : public InputNode
	{
	public:
		BoolInputNode(QString *prompt, QString *symbol,
			DependencyListNode *dependencies = 0)
			: InputNode(prompt, symbol, 0, dependencies) {};
		virtual ~BoolInputNode() {};

		virtual void writeHeader(QTextStream &) const;

		virtual InputType inputType() const { return Bool; }
		virtual QString value() const;
		virtual void internalSetValue(const QString &value);

		void toggle();

	protected:
		bool m_value;
	};

	class RestricedBoolInputNode : public BoolInputNode
	{
	public:
		RestricedBoolInputNode(QString *prompt, QString *symbol,
			DependencyListNode *dependencies = 0)
			: BoolInputNode(prompt, symbol, dependencies) {};
		virtual ~RestricedBoolInputNode() {};

		virtual bool isAvailable() const;
	};

	class IntInputNode : public InputNode
	{
	public:
		IntInputNode(QString *prompt, QString *symbol, VariableNode *defValue,
			DependencyListNode *dependencies = 0)
			: InputNode(prompt, symbol, defValue, dependencies) {};
		virtual ~IntInputNode() { }

		virtual void writeHeader(QTextStream &) const;

		virtual InputType inputType() const { return Int; }
		virtual QString value() const;
		virtual void internalSetValue(const QString &value);

	protected:
		int m_value;
	};
   
	class HexInputNode : public IntInputNode
	{
	public:
		HexInputNode(QString *prompt, QString *symbol, VariableNode *defValue,
			DependencyListNode *dependencies = 0)
			: IntInputNode(prompt, symbol, defValue, dependencies) {};
		virtual ~HexInputNode() {};

		virtual void writeHeader(QTextStream &) const;

		virtual InputType inputType() const { return Hex; }
		virtual QString value() const;
		virtual void internalSetValue(const QString &value);
	};

	class StringInputNode : public InputNode
	{
	public:
		StringInputNode(QString *prompt, QString *symbol,
			VariableNode *defValue, DependencyListNode *dependencies = 0)
			: InputNode(prompt, symbol, defValue, dependencies) {};
		virtual ~StringInputNode() {};

		virtual void write(QTextStream &str) const;
		virtual void writeHeader(QTextStream &) const;

		virtual InputType inputType() const { return String; }
		virtual QString value() const;
		virtual void internalSetValue(const QString &value);

	private:
		QString m_value;
	};

	class TristateInputNode : public InputNode
	{
	public:
		TristateInputNode(QString *prompt, QString *symbol,
			DependencyListNode *dependencies = 0)
			: InputNode(prompt, symbol, 0, dependencies) {};
		virtual ~TristateInputNode() {};

		virtual void writeHeader(QTextStream &) const;

		virtual InputType inputType() const { return Tristate; }
		virtual QString value() const;
		virtual void internalSetValue(const QString &value);

		void advance();

	private:
		enum { No, Yes, Module } m_value;
	};
   
	class ChoiceNode : public Node
	{
	public:
		ChoiceNode(QString *prompt, const QStringList &labels,
			const QStringList &symbols, int defIndex)
			: m_prompt(*prompt), m_labels(labels), m_symbols(symbols),
			  m_defaultIndex(defIndex) { delete prompt; }
		virtual ~ChoiceNode() {};

		virtual Type type() const { return Choice; }
		virtual void initialize();
		virtual void apply() const;
		virtual void write(QTextStream &str) const;
		virtual void writeHeader(QTextStream &) const;

		const QString &prompt() const { return m_prompt; }
		const QStringList &labels() const { return m_labels; }
		const QStringList &symbols() const { return m_symbols; }
		int index() const { return m_index; }
		void setIndex(int index) { m_index = index; }

	private:
		QString m_prompt;
		QStringList m_labels;
		QStringList m_symbols;
		int m_defaultIndex;
		int m_index;
	};

	class ExpressionNode : public Node
	{
	public:
		virtual Type type() const { return Expression; }
		virtual bool evaluate() const = 0;
	};

	class NotExpressionNode : public ExpressionNode
	{
	public:
		NotExpressionNode(ExpressionNode *expression)
			: m_expression(expression) {};
		virtual ~NotExpressionNode() { delete m_expression; }

		virtual bool evaluate() const { return !m_expression->evaluate(); }

	private:
		ExpressionNode *m_expression;
	};

	class AndExpressionNode : public ExpressionNode
	{
	public:
		AndExpressionNode(ExpressionNode *exp1, ExpressionNode *exp2)
			: m_exp1(exp1), m_exp2(exp2) {};
		virtual ~AndExpressionNode() { delete m_exp1; delete m_exp2; }

		virtual bool evaluate() const { return m_exp1->evaluate() && m_exp2->evaluate(); }

	private:
		ExpressionNode *m_exp1;
		ExpressionNode *m_exp2;
	};

	class OrExpressionNode : public ExpressionNode
	{
	public:
		OrExpressionNode(ExpressionNode *exp1, ExpressionNode *exp2)
			: m_exp1(exp1), m_exp2(exp2) {};
		virtual ~OrExpressionNode() { delete m_exp1; delete m_exp2; }

		virtual bool evaluate() const { return m_exp1->evaluate() || m_exp2->evaluate(); }

	private:
		ExpressionNode *m_exp1;
		ExpressionNode *m_exp2;
	};

	class ImplicitExpressionNode : public ExpressionNode
	{
	public:
		ImplicitExpressionNode(VariableNode *v)
			: m_v(v) {};
		virtual ~ImplicitExpressionNode() { delete m_v; }

		virtual bool evaluate() const
		{
			return !m_v->value().isEmpty() && !m_v->value().isNull();
		}

	private:
		VariableNode *m_v;
	};

	class EqualityExpressionNode : public ExpressionNode
	{
	public:
		EqualityExpressionNode(VariableNode *v1, VariableNode *v2)
			: m_v1(v1), m_v2(v2) {};
		virtual ~EqualityExpressionNode() { delete m_v1; delete m_v2; }

		virtual bool evaluate() const
		{
			return m_v1->value() == m_v2->value();
		}

	private:
		VariableNode *m_v1;
		VariableNode *m_v2;
	};

	class UnequalityExpressionNode : public ExpressionNode
	{
	public:
		UnequalityExpressionNode(VariableNode *v1, VariableNode *v2)
			: m_v1(v1), m_v2(v2) {};
		virtual ~UnequalityExpressionNode() { delete m_v1; delete m_v2; }

		virtual bool evaluate() const
		{
			return m_v1->value() != m_v2->value();
		}

	private:
		VariableNode *m_v1;
		VariableNode *m_v2;
	};

	class TextNodeBase : public Node
	{
	public:
		TextNodeBase(QString *text) : m_text(*text) { delete text; }
		virtual ~TextNodeBase() {};

		const QString &text() const { return m_text; }

	private:
		QString m_text;
	};

	class MainMenuNameNode : public TextNodeBase
	{
	public:
		MainMenuNameNode(QString *text) : TextNodeBase(text) {};
		virtual ~MainMenuNameNode() {};

		virtual Type type() const { return MainMenuName; }
	};

	class CommentNode : public TextNodeBase
	{
	public:
		CommentNode(QString *text) : TextNodeBase(text) {};
		virtual ~CommentNode() {};

		virtual Type type() const { return Comment; }
	};

	class TextNode : public TextNodeBase
	{
	public:
		TextNode(QString *text) : TextNodeBase(text) {};
		virtual ~TextNode() {};

		virtual Type type() const { return Text; }
	};

	class BranchNodeBase : public Node
	{
	public:
		virtual void initialize();
		virtual NodeList *children() const = 0;

		virtual void apply() const;
		virtual void write(QTextStream &str) const;
		virtual void writeHeader(QTextStream &) const;
	};

	class RootNode : public BranchNodeBase
	{
	public:
		RootNode(MainMenuNameNode *name, NodeList *children)
			: m_name(name), m_children(children) {};
		virtual ~RootNode() { delete m_name; delete m_children; }

		virtual Type type() const { return Root; }
		virtual void write(QTextStream &str) const;
		virtual void writeHeader(QTextStream &) const;

		virtual NodeList *children() const { return m_children; }
		const MainMenuNameNode *name() const { return m_name; }

	public:
		MainMenuNameNode *m_name;
		NodeList *m_children;
	};

	class MenuNode : public BranchNodeBase
	{
	public:
		MenuNode(CommentNode *comment, NodeList *children)
			: m_comment(comment), m_children(children) {};
		virtual ~MenuNode() { delete m_comment; delete m_children; }

		virtual Type type() const { return Menu; }
		virtual void write(QTextStream &str) const;
		virtual void writeHeader(QTextStream &) const;

		virtual NodeList *children() const { return m_children; }
		const CommentNode *comment() const { return m_comment; }

	private:
		CommentNode *m_comment;
		NodeList *m_children;
	};

	class IfNode : public BranchNodeBase
	{
	public:
		IfNode(ExpressionNode *condition, NodeList *trueChildren,
			NodeList *falseChildren = 0)
			: m_condition(condition), m_trueChildren(trueChildren),
			  m_falseChildren(falseChildren) {};
		virtual ~IfNode()
		{
			delete m_condition;
			delete m_trueChildren;
			delete m_falseChildren;
		}

		virtual Type type() const { return If; }
		virtual void initialize();
		virtual NodeList *children() const
		{
			return m_condition->evaluate() ? m_trueChildren : m_falseChildren;
		}

	private:
		ExpressionNode *m_condition;
		NodeList *m_trueChildren;
		NodeList *m_falseChildren;
	};
}

#endif
// vim: ts=4 sw=4 noet
