// Copyright (c) 1994 James Clark
// See the file COPYING for copying permission.

#ifndef Text_INCLUDED
#define Text_INCLUDED 1
#ifdef __GNUG__
#pragma interface
#endif

#include "types.h"
#include "StringC.h"
#include "Vector.h"
#include "Location.h"
#include "SubstTable.h"
#include <stddef.h>

#ifdef SP_NAMESPACE
namespace SP_NAMESPACE {
#endif

class InternalEntity;

struct SP_API TextItem {
  TextItem();
  enum Type {
    data,
    cdata,
    sdata,
    nonSgml,
    entityStart,
    entityEnd,
    startDelim,
    endDelim,
    endDelimA,
    ignore
    };
  Type type;
  // char that was ignored
  Char c;
  // location of this item
  // data - location of first char
  // (c/sdata)entityStart - location of first char of entity
  // (c/sdata)entityEnd - location of entity end in entity
  // ignore - location of ignored character
  // startDelim - location of first char of delimiter
  // endDelim(A) - location of first char of delimiter
  Location loc;
  // index of character in chars_ to which this applies
  size_t index;
};

// This is used to represent literals and attribute values.

class SP_API Text {
public:
  Text();
  void clear();
  void swap(Text &to);
  void addChar(Char c, const Location &);
  void addChars(const StringC &, const Location &);
  void addChars(const Char *, size_t, const Location &);
  void insertChars(const StringC &, const Location &);
  void ignoreChar(Char, const Location &);
  void ignoreLastChar();
  void addNonSgmlChar(Char c, const Location &);
  void addEntityStart(const Location &);
  void addEntityEnd(const Location &);
  void addCdata(const InternalEntity *, const ConstPtr<Origin> &);
  void addSdata(const InternalEntity *, const ConstPtr<Origin> &);
  void addStartDelim(const Location &loc);
  void addEndDelim(const Location &loc, Boolean lita);
  void subst(const SubstTable<Char> &, Char space);
  void addCharsTokenize(const Char *, size_t, const Location &loc, Char space);
  void addCharsTokenize(const StringC &, const Location &loc, Char space);
  void tokenize(Char space, Text &text) const;
  Location charLocation(size_t i) const;
  Boolean charLocation(size_t, const Origin *&, Index &) const;
  Boolean charLocation(size_t i, const ConstPtr<Origin> *&, Index &) const;
  size_t size() const;
  Char lastChar() const;
  const StringC &string() const;
  size_t normalizedLength(size_t normsep) const;
  Boolean fixedEqual(const Text &) const;
  // Location of first char of start delimiter.
  Boolean startDelimLocation(Location &) const;
  // Location of first char of end delimiter
  Boolean endDelimLocation(Location &) const;
  // Is delimiter a lit or lita?
  Boolean delimType(Boolean &lita) const;
private:
  void addSimple(TextItem::Type, const Location &);
  StringC chars_;
  Vector<TextItem> items_;
  friend class TextIter;
};

class SP_API TextIter {
public:
  TextIter(const Text &);
  void rewind();
  Boolean next(TextItem::Type &, const Char *&, size_t &,
	       const Location *&);
  // Alternative interface to next()
  Boolean valid() const;
  void advance();
  TextItem::Type type() const;
  const Location &location() const;
  const Char *chars(size_t &length) const;
private:
  const TextItem *ptr_;
  const Text *text_;
};

inline
size_t Text::size() const
{
  return chars_.size();
}

inline
Char Text::lastChar() const
{
  return chars_[chars_.size() - 1];
}

inline
const StringC &Text::string() const
{
  return chars_;
}

inline
void Text::addEntityStart(const Location &loc)
{
  addSimple(TextItem::entityStart, loc);
}

inline
void Text::addEntityEnd(const Location &loc)
{
  addSimple(TextItem::entityEnd, loc);
}

inline
void Text::addChars(const StringC &s, const Location &loc)
{
  addChars(s.data(), s.size(), loc);
}

inline
void Text::addStartDelim(const Location &loc)
{
  addSimple(TextItem::startDelim, loc);
}

inline
void Text::addEndDelim(const Location &loc, Boolean lita)
{
  addSimple(lita ? TextItem::endDelimA : TextItem::endDelim,
	    loc);
}

inline
void Text::addCharsTokenize(const StringC &str, const Location &loc,
			    Char space)
{
  addCharsTokenize(str.data(), str.size(), loc, space);
}

inline
Location Text::charLocation(size_t i) const
{
  const ConstPtr<Origin> *originP;
  Index index;
  if (charLocation(i, originP, index))
    return Location(*originP, index);
  else
    return Location();
}

inline
Boolean Text::charLocation(size_t i, const Origin *&origin, Index &index) const
{
  const ConstPtr<Origin> *originP;
  if (charLocation(i, originP, index)) {
    origin = originP->pointer();
    return 1;
  }
  else
    return 0;
}

inline
void TextIter::rewind()
{
  ptr_ = text_->items_.begin();
}

inline
void TextIter::advance()
{
  ptr_++;
}

inline
Boolean TextIter::valid() const
{
  return ptr_ != (text_->items_.begin() + text_->items_.size());
}

inline
const Location &TextIter::location() const
{
  return ptr_->loc;
}

inline
TextItem::Type TextIter::type() const
{
  return ptr_->type;
}

#ifdef SP_NAMESPACE
}
#endif

#endif /* not Text_INCLUDED */
