/*
  Copyright (c) 2000 Caldera Systems

  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; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __parser_h__
#define __parser_h__

#include <qvaluelist.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qtextstream.h>

#include <ksharedptr.h>

class QIODevice;

struct RangeValue
{
    float m_from;
    float m_to;
};

/**
 * A node value consists of the value itself and formatting attributes,
 * like if the value is quoted ("Foo" vs. Bar) and the spacing attribute
 * specifies the whitespace string which is meant to be appended to the
 * string'ifed version of the value when writing back to the config
 * file. The reason why the spacing is stored in a QString is because
 * it can consist of both tabs and spaces.
 *
 * Example:
 * "Foo"    "NextValue"
 * ^ ^  ^^^^
 * | |  |
 * | |  m_spacing = "    ";
 * | |
 * | m_value = Value( "Foo" ); ( Value::Type == String)
 * |
 * m_quoted = true
 */
class NodeValue
{
public:
    typedef QValueList<NodeValue> List;

    NodeValue() { m_quoted = false; }
//    NodeValue( const Value &val, const QString &spacing, bool quoted )
//        { m_value = val; m_spacing = spacing; m_quoted = quoted; }
    NodeValue( const QString &val, bool quoted=true, const QString &spacing=QString::null );
    NodeValue( int val, bool quoted=false, const QString &spacing=QString::null );
    NodeValue( float val, bool quoted=false, const QString &spacing=QString::null );
    NodeValue( RangeValue val, bool quoted=true, const QString &spacing=QString::null );

    QString stringValue() const;
    int intValue() const;
    float floatValue() const;
    RangeValue rangeValue() const;

    bool quoted() const { return m_quoted; }

    void setSpacing( const QString &spacing ) { m_spacing = spacing; }
    QString spacing() const { return m_spacing; }

private:
    QString m_value;
    QString m_spacing;
    bool m_quoted;
};

/**
 * The XFree config file is parsed into a (not very deep) tree of Node objects.
 * In order to be able to completely preserve the content/structure of an
 * existing file this node class contains some additional ugly properties,
 * mostly for text formatting. The interesting attributes are name and values.
 */
class Node : public KShared
{
public:
    typedef KSharedPtr<Node> Ptr;

    class List : public QValueList<Ptr>
    {
    public:
        List() {}
        ~List() {}

        Ptr namedItem( const QString &name, bool caseSensitive = true );
    };

    Node();
    ~Node();

    Ptr parent() const { return Ptr( m_parent ); }

    void appendChild( const Ptr &child );
    void removeChild( const Ptr &child );
    List children() const { return m_children; }

    // name, either section name or keyword
    void setName( const QString &name ) { m_name = name; }
    QString name() const { return m_name; }

    // parsed Values
    void setValues( const NodeValue::List &vals ) { m_values = vals; }
    NodeValue::List values() const { return m_values; }

    // convenience method
    void setValue( const NodeValue &val ) { m_values.clear(); m_values << val; }
    // convenience method
    NodeValue value() const { return m_values.first(); }

    // comment text, including '#'
    void setComment( const QString &comment ) { m_comment = comment; }
    QString comment() const { return m_comment; }

    // spacing text between value keyword and value string
    void setMidSpacing( const QString &midSpacing ) { m_midSpacing = midSpacing; }
    QString midSpacing() const { return m_midSpacing; }

    // leading spacing text, before the value keyword
    void setLead( const QString &lead ) { m_lead = lead; }
    QString lead() const { return m_lead; }

    void save( QTextStream &stream ) const;

private:
    List m_children;
    Node *m_parent; // must be a plain pointer, to avoid circular reference!!

    QString m_name;
    NodeValue::List m_values;
    QString m_comment;
    QString m_lead;
    QString m_midSpacing;
};

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

    // flatNodes is a list of the names of nodes which can never have
    // child sections. Instead all contained nodes are meant to be
    // inserted completely flat.
    Node::Ptr parse( QIODevice *source, const QStringList &flatNodes );

private:
    Node::Ptr parseLine( QString line );

    void parseValueString( QString str, QString &key, QString &midSpacing, QString &val );

    NodeValue::List parseValues( QString str );

    QIODevice *m_dev;
    QTextStream *m_stream;
    Node::Ptr m_root;
    Node::Ptr m_current;
    QStringList m_flatNodes;
};

#endif
