/***************************************************************************
 *   Copyright (C) 2003 by Sylvain Joyeux                                  *
 *   sylvain.joyeux@m4x.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.                                   *
 ***************************************************************************/
#ifndef QHtmlStream_H
#define QHtmlStream_H

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

class QHtmlStream;
class QHtmlStreamManip;

/**
@author Sylvain Joyeux
*/

class QHtmlStreamManip
{
protected:
  virtual void apply(QHtmlStream& stream) const = 0;

public:
  virtual ~QHtmlStreamManip() {};
  void operator () (QHtmlStream& stream) const
  { apply(stream); }
};

class QHtmlStream
{
  QTextOStream m_stream;

  enum States
  {
    NORMAL_FLOW,
    TAG,
    BLOCK,
    PARAM
  };
  int m_state, m_enclosing_state;

  bool m_newline;
  QString m_indent;
  QStringList m_blockstack;

  void finalize_open()
  {
    if (m_state == PARAM)
      m_state = m_enclosing_state;

    if (m_state == BLOCK)
      m_stream << ">";
    else if (m_state == TAG)
      m_stream << " />";

    m_state = NORMAL_FLOW;
  }

  void indent()
  {
    if (m_newline)
    {
      m_stream << m_indent;
      m_newline = false;
    }
  }

  template<class T>
  QHtmlStream& output(const T& o)
  {
    indent();

    if (m_state == PARAM)
    {
      m_stream << "=\"" << o << "\"";
      m_state = m_enclosing_state;
      return *this;
    }

    if (m_state == BLOCK)
    {
      m_stream << ">";
      m_state = NORMAL_FLOW;
    }
    else if (m_state == TAG)
    {
      m_stream << "/>";
      m_state = NORMAL_FLOW;
    }
    m_stream << o;

    return *this;
  }

public:
  QHtmlStream(QString* buffer)
    : m_stream(buffer), m_state(NORMAL_FLOW), m_newline(true) {}
  ~QHtmlStream() {}

  void tag(const QString& name, const QString& cl, const QString& id)
  {
    finalize_open();
    indent();

    m_stream << '<' << name;
    m_state = TAG;

    if (!cl.isEmpty())
      m_stream << " class=\"" << cl << "\"";
    if (!id.isEmpty())
      m_stream << " id=\"" << id << "\"";
  }

  void block(const QString& name, const QString& cl, const QString& id)
  {
    finalize_open();
    indent();

    m_stream << '<' << name;
    m_indent += '\t';
    m_blockstack.push_front(name);
    m_state = BLOCK;

    if (!cl.isEmpty())
      m_stream << " class=\"" << cl << "\"";
    if (!id.isEmpty())
      m_stream << " id=\"" << id << "\"";
  }

  void parameter(const QString& param_name)
  {
    if (m_state == NORMAL_FLOW) return;

    m_stream << " " << param_name;
    m_enclosing_state = m_state;
    m_state = PARAM;
  }

  void close()
  {
    finalize_open();

    m_indent.truncate(m_indent.length() - 1);
    indent();
    m_stream << "</" << m_blockstack.first() << ">";
    m_blockstack.pop_front();
  }
  void close_all(bool indent)
  {
  	while( ! m_blockstack.empty() )
    {
      if (indent)
        (*this) << endl;
      close();
    }
  }

  void data()
  {
    finalize_open();
  }

  QHtmlStream & operator<< ( QChar c )  { return output(c); }
  QHtmlStream & operator<< ( char c )  { return output(c); }
  QHtmlStream & operator<< ( signed short i )  { return output(i); }
  QHtmlStream & operator<< ( unsigned short i )  { return output(i); }
  QHtmlStream & operator<< ( signed int i )  { return output(i); }
  QHtmlStream & operator<< ( unsigned int i )  { return output(i); }
  QHtmlStream & operator<< ( signed long i )  { return output(i); }
  QHtmlStream & operator<< ( unsigned long i )  { return output(i); }
  QHtmlStream & operator<< ( float f )  { return output(f); }
  QHtmlStream & operator<< ( double f )  { return output(f); }
  QHtmlStream & operator<< ( const char * s )  { return output(s); }
  QHtmlStream & operator<< ( const QString & s )  { return output(s); }
  QHtmlStream & operator<< ( const QCString & s )  { return output(s); }

  QHtmlStream & operator<< ( const QHtmlStreamManip& op )
  {
    op(*this);
    return *this;
  }

  QHtmlStream & operator<< (QTSManip m)
  {
  	finalize_open();
    m_stream << m;
    return (*this);
  }

  QHtmlStream & operator<< (QTSFUNC f)
  {
  	finalize_open();
    int old_flags = m_stream.flags();
    m_stream << f;
    if (old_flags == m_stream.flags())
      m_newline = true;
    return (*this);
  }
};

/***************************************************************************************
* Stream manipulators
*/

class QHtmlStreamManip0 : public QHtmlStreamManip
{
public:
  typedef void (QHtmlStream::*Method)();

private:
  Method m_method;

  void apply (QHtmlStream& stream) const
  { (stream.*m_method)(); }

public:
  QHtmlStreamManip0(Method m)
    : m_method(m) {}
};

class QHtmlStreamManip1 : public QHtmlStreamManip
{
public:
  typedef void (QHtmlStream::*Method)(const QString& param);

private:
  Method m_method;
  QString m_param;

  void apply(QHtmlStream& stream) const
  { (stream.*m_method)(m_param); }

public:
  QHtmlStreamManip1(Method m, const QString& param)
    : m_method(m), m_param(param) {}
};

class QHtmlStreamManip3 : public QHtmlStreamManip
{
public:
  typedef void (QHtmlStream::*Method)(const QString& param0, const QString& param1, const QString& param2);

private:
  Method m_method;
  QString m_param0, m_param1, m_param2;

  void apply(QHtmlStream& stream) const
  { (stream.*m_method)(m_param0, m_param1, m_param2); }

public:
  QHtmlStreamManip3(Method m, const QString& param0, const QString& param1, const QString& param2)
    : m_method(m),
    m_param0(param0), m_param1(param1), m_param2(param2) {}
};

class CloseAll : public QHtmlStreamManip
{
private:
  bool m_indent;
  void apply(QHtmlStream& stream) const
  { stream.close_all(m_indent); }
public:
  CloseAll(bool indent) : m_indent(indent) {}
};

inline QHtmlStreamManip3 tag(const QString& name, const QString& cl = QString::null, const QString& id = QString::null)
{ return QHtmlStreamManip3(&QHtmlStream::tag, name, cl, id); }
inline QHtmlStreamManip3 block(const QString& name, const QString& cl = QString::null, const QString& id = QString::null)
{ return QHtmlStreamManip3(&QHtmlStream::block, name, cl, id); }

inline QHtmlStreamManip1 param(const QString& name)
{ return QHtmlStreamManip1(&QHtmlStream::parameter, name); }

inline QHtmlStreamManip0 close()
{ return QHtmlStreamManip0(&QHtmlStream::close); }
inline QHtmlStreamManip0 data()
{ return QHtmlStreamManip0(&QHtmlStream::data); }
inline CloseAll close_all(bool indent = true)
{ return CloseAll(indent); }

#endif
