/***************************************************************************
 *   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 TQHtmlStream_H
#define TQHtmlStream_H

#include <tqtextstream.h>
#include <tqstringlist.h>

class TQHtmlStream;
class TQHtmlStreamManip;

/**
@author Sylvain Joyeux
*/

class TQHtmlStreamManip
{
protected:
  virtual void apply(TQHtmlStream& stream) const = 0;

public:
  virtual ~TQHtmlStreamManip() {};
  void operator () (TQHtmlStream& stream) const
  { apply(stream); }
};

class TQHtmlStream
{
  TQTextOStream m_stream;

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

  bool m_newline;
  TQString m_indent;
  TQStringList 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>
  TQHtmlStream& 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:
  TQHtmlStream(TQString* buffer)
    : m_stream(buffer), m_state(NORMAL_FLOW), m_newline(true) {}
  ~TQHtmlStream() {}

  void tag(const TQString& name, const TQString& cl, const TQString& 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 TQString& name, const TQString& cl, const TQString& 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 TQString& 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();
  }

  TQHtmlStream & operator<< ( TQChar c )  { return output(c); }
  TQHtmlStream & operator<< ( char c )  { return output(c); }
  TQHtmlStream & operator<< ( signed short i )  { return output(i); }
  TQHtmlStream & operator<< ( unsigned short i )  { return output(i); }
  TQHtmlStream & operator<< ( signed int i )  { return output(i); }
  TQHtmlStream & operator<< ( unsigned int i )  { return output(i); }
  TQHtmlStream & operator<< ( signed long i )  { return output(i); }
  TQHtmlStream & operator<< ( unsigned long i )  { return output(i); }
  TQHtmlStream & operator<< ( float f )  { return output(f); }
  TQHtmlStream & operator<< ( double f )  { return output(f); }
  TQHtmlStream & operator<< ( const char * s )  { return output(s); }
  TQHtmlStream & operator<< ( const TQString & s )  { return output(s); }
  TQHtmlStream & operator<< ( const TQCString & s )  { return output(s); }

  TQHtmlStream & operator<< ( const TQHtmlStreamManip& op )
  {
    op(*this);
    return *this;
  }

  TQHtmlStream & operator<< (TQTSManip m)
  {
  	finalize_open();
    m_stream << m;
    return (*this);
  }

  TQHtmlStream & operator<< (TQTSFUNC 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 TQHtmlStreamManip0 : public TQHtmlStreamManip
{
public:
  typedef void (TQHtmlStream::*Method)();

private:
  Method m_method;

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

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

class TQHtmlStreamManip1 : public TQHtmlStreamManip
{
public:
  typedef void (TQHtmlStream::*Method)(const TQString& param);

private:
  Method m_method;
  TQString m_param;

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

public:
  TQHtmlStreamManip1(Method m, const TQString& param)
    : m_method(m), m_param(param) {}
};

class TQHtmlStreamManip3 : public TQHtmlStreamManip
{
public:
  typedef void (TQHtmlStream::*Method)(const TQString& param0, const TQString& param1, const TQString& param2);

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

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

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

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

inline TQHtmlStreamManip3 tag(const TQString& name, const TQString& cl = TQString(), const TQString& id = TQString())
{ return TQHtmlStreamManip3(&TQHtmlStream::tag, name, cl, id); }
inline TQHtmlStreamManip3 block(const TQString& name, const TQString& cl = TQString(), const TQString& id = TQString())
{ return TQHtmlStreamManip3(&TQHtmlStream::block, name, cl, id); }

inline TQHtmlStreamManip1 param(const TQString& name)
{ return TQHtmlStreamManip1(&TQHtmlStream::parameter, name); }

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

#endif