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

#ifdef __GNUG__
#pragma implementation
#endif
#include "splib.h"
#include "Location.h"
#include "Entity.h"
#include "Mutex.h"

#ifdef SP_NAMESPACE
namespace SP_NAMESPACE {
#endif

class InputSourceOriginImpl : public EntityOrigin {
public:
  InputSourceOriginImpl();
  InputSourceOriginImpl(const Location &refLocation);
  const Location &parent() const;
  const ExternalInfo *externalInfo() const;
  Offset startOffset(Index ind) const;
  void noteCharRef(Index replacementIndex, const NamedCharRef &);
  Boolean isNamedCharRef(Index ind, NamedCharRef &ref) const;
  void setExternalInfo(ExternalInfo *);
  virtual InputSourceOrigin *copy() const;
  const InputSourceOrigin *asInputSourceOrigin() const;
private:
  InputSourceOriginImpl(const InputSourceOriginImpl &); // undefined
  void operator=(const InputSourceOriginImpl &);	// undefined
  size_t nPrecedingCharRefs(Index ind) const;
  Vector<InputSourceOriginNamedCharRef> charRefs_;
  StringC charRefOrigNames_;
  Owner<ExternalInfo> externalInfo_; // 0 for internal entities
  Location refLocation_;	// where referenced from
  Mutex mutex_;
};

class EntityOriginImpl : public InputSourceOriginImpl {
public:
  void *operator new(size_t sz, Allocator &alloc) {
    return alloc.alloc(sz);
  }
  void *operator new(size_t sz) {
    return Allocator::allocSimple(sz);
  }
  void operator delete(void *p) {
    Allocator::free(p);
  }
#ifdef SP_HAVE_PLACEMENT_OPERATOR_DELETE
  void operator delete(void *p, Allocator &) { Allocator::free(p); }
#endif
  EntityOriginImpl(const ConstPtr<Entity> &);
  EntityOriginImpl(const ConstPtr<Entity> &,
		   const Location &refLocation);
  EntityOriginImpl(const ConstPtr<Entity> &,
		   const Location &refLocation, Index refLength,
		   Owner<Markup> &markup);
  ~EntityOriginImpl();
  InputSourceOrigin *copy() const;
  const Entity *entity() const { return entity_.pointer(); }
  const EntityDecl *entityDecl() const;
  const EntityOrigin *asEntityOrigin() const;
  Boolean defLocation(Offset off, const Origin *&, Index &) const;
  Index refLength() const;
  const Markup *markup() const;
private:
  EntityOriginImpl(const EntityOriginImpl &); // undefined
  void operator=(const EntityOriginImpl &);	// undefined
  ConstPtr<Entity> entity_;	// 0 for document entity
  // total length of reference
  // (characters that were replaced by the entity)
  Index refLength_;
  Owner<Markup> markup_;
};

const size_t EntityOrigin::allocSize = sizeof(EntityOriginImpl);

Location::Location()
{
}

Location::Location(Origin *origin, Index i)
: origin_(origin), index_(i)
{
}

Location::Location(ConstPtr<Origin> origin, Index i)
: origin_(origin), index_(i)
{
}

Origin::~Origin()
{
}

const EntityOrigin *Origin::asEntityOrigin() const
{
  return 0;
}

const InputSourceOrigin *Origin::asInputSourceOrigin() const
{
  return 0;
}

Index Origin::refLength() const
{
  return 0;
}

Boolean Origin::origChars(const Char *&) const
{
  return 0;
}

Boolean Origin::inBracketedTextOpenDelim() const
{
  return 0;
}

Boolean Origin::inBracketedTextCloseDelim() const
{
  return 0;
}

Boolean Origin::isNumericCharRef(const Markup *&) const
{
  return 0;
}

Boolean Origin::isNamedCharRef(Index, NamedCharRef &) const
{
  return 0;
}

const EntityDecl *Origin::entityDecl() const
{
  return 0;
}

const Markup *Origin::markup() const
{
  return 0;
}
  
const Entity *Origin::entity() const
{
  return 0;
}

Boolean Origin::defLocation(Offset, const Origin *&, Index &) const
{
  return 0;
}

const ExternalInfo *Origin::externalInfo() const
{
  return 0;
}

Offset Origin::startOffset(Index ind) const
{
  return ind;
}

const StringC *Origin::entityName() const
{
  const EntityDecl *ent = entityDecl();
  if (ent)
    return &ent->name();
  else
    return 0;
}

BracketOrigin::BracketOrigin(const Location &loc, Position pos)
: loc_(loc), pos_(pos)
{
}

const Location &BracketOrigin::parent() const
{
  return loc_;
}

Boolean BracketOrigin::inBracketedTextOpenDelim() const
{
  return pos_ == open;
}

Boolean BracketOrigin::inBracketedTextCloseDelim() const
{
  return pos_ == close;
}

InputSourceOrigin *InputSourceOrigin::make()
{
  return new InputSourceOriginImpl;
}

InputSourceOrigin *InputSourceOrigin::make(const Location &refLocation)
{
  return new InputSourceOriginImpl(refLocation);
}

InputSourceOriginImpl::InputSourceOriginImpl()
{
}

InputSourceOriginImpl::InputSourceOriginImpl(const Location &refLocation)
: refLocation_(refLocation)
{
}

const InputSourceOrigin *InputSourceOriginImpl::asInputSourceOrigin() const
{
  return this;
}

const ExternalInfo *InputSourceOriginImpl::externalInfo() const
{
  return externalInfo_.pointer();
}

InputSourceOrigin *InputSourceOriginImpl::copy() const
{
  return new InputSourceOriginImpl(refLocation_);
}

const Location &InputSourceOriginImpl::parent() const
{
  return refLocation_;
}

void InputSourceOriginImpl::setExternalInfo(ExternalInfo *info)
{
  externalInfo_ = info;
}

void InputSourceOriginImpl::noteCharRef(Index replacementIndex,
					const NamedCharRef &ref)
{
  Mutex::Lock lock(&mutex_);
  charRefs_.resize(charRefs_.size() + 1);
  charRefs_.back().replacementIndex = replacementIndex;
  charRefs_.back().refStartIndex = ref.refStartIndex();
  charRefs_.back().refEndType = ref.refEndType();
  charRefs_.back().origNameOffset = charRefOrigNames_.size();
  charRefOrigNames_ += ref.origName();
}

// Number of character references whose replacement index < ind.

size_t InputSourceOriginImpl::nPrecedingCharRefs(Index ind) const
{
  size_t i;
  // Find i such that
  // charRefs_[I].replacementIndex >= ind
  // charRefs_[i - 1].replacementIndex < ind
  if (charRefs_.size() == 0
      || ind > charRefs_.back().replacementIndex)
    // This will be a common case, so optimize it.
    i = charRefs_.size();
  else {
    // Binary search
    // Invariant:
    // charRefs_ < i have replacementIndex < ind
    // charRefs_ >= lim have replacementIndex >= ind
    i = 0;
    size_t lim = charRefs_.size();
    while (i < lim) {
      size_t mid = i + (lim - i)/2;
      if (charRefs_[mid].replacementIndex >= ind)
	lim = mid;
      else
	i = mid + 1;
    }
  }
  return i;
}

Offset InputSourceOriginImpl::startOffset(Index ind) const
{
  Mutex::Lock lock(&((InputSourceOriginImpl *)this)->mutex_);
  size_t n = nPrecedingCharRefs(ind);
  if (n < charRefs_.size()
      && ind == charRefs_[n].replacementIndex) {
    for (;;) {
      ind = charRefs_[n].refStartIndex;
      if (n == 0 || charRefs_[n - 1].replacementIndex != ind)
	break;
      --n;
    }
  }
  // charRefs[n - 1].replacementIndex < ind
  return Offset(ind - n);
}

Boolean InputSourceOriginImpl::isNamedCharRef(Index ind, NamedCharRef &ref) const
{
  Mutex::Lock lock(&((InputSourceOriginImpl *)this)->mutex_);
  size_t n = nPrecedingCharRefs(ind);
  if (n < charRefs_.size() && ind == charRefs_[n].replacementIndex) {
    ref.set(charRefs_[n].refStartIndex,
	    charRefs_[n].refEndType,
	    charRefOrigNames_.data() + charRefs_[n].origNameOffset,
	    (n + 1 < charRefs_.size()
	     ? charRefs_[n + 1].origNameOffset
	     : charRefOrigNames_.size())
	    - charRefs_[n].origNameOffset);
    return 1;
  }
  return 0;
}

EntityOrigin *EntityOrigin::make(Allocator &alloc,
				 const ConstPtr<Entity> &entity)
{
  return new (alloc) EntityOriginImpl(entity);
}

EntityOrigin *EntityOrigin::make(Allocator &alloc,
				 const ConstPtr<Entity> &entity,
				 const Location &refLocation)
{
  return new (alloc) EntityOriginImpl(entity, refLocation);
}

EntityOrigin *EntityOrigin::make(Allocator &alloc,
				 const ConstPtr<Entity> &entity,
				 const Location &refLocation,
				 Index refLength,
				 Owner<Markup> &markup)
{
  return new (alloc) EntityOriginImpl(entity, refLocation, refLength, markup);
}

EntityOrigin *EntityOrigin::make(const ConstPtr<Entity> &entity,
				 const Location &refLocation,
				 Index refLength,
				 Owner<Markup> &markup)
{
  return new EntityOriginImpl(entity, refLocation, refLength, markup);
}

EntityOrigin *EntityOrigin::make(const ConstPtr<Entity> &entity,
				 const Location &refLocation)
{
  return new EntityOriginImpl(entity, refLocation);
}

EntityOriginImpl::EntityOriginImpl(const ConstPtr<Entity> &entity)
: refLength_(0), entity_(entity)
{
}

EntityOriginImpl::EntityOriginImpl(const ConstPtr<Entity> &entity,
				   const Location &refLocation)
: InputSourceOriginImpl(refLocation), refLength_(0), entity_(entity)
{
}

EntityOriginImpl::EntityOriginImpl(const ConstPtr<Entity> &entity,
				   const Location &refLocation,
				   Index refLength,
				   Owner<Markup> &markup)
: InputSourceOriginImpl(refLocation), refLength_(refLength), entity_(entity)
{
  markup.swap(markup_);
}

EntityOriginImpl::~EntityOriginImpl()
{
}

InputSourceOrigin *EntityOriginImpl::copy() const
{
  Owner<Markup> m;
  if (markup_)
    m = new Markup(*markup_);
  return new EntityOriginImpl(entity_, parent(), refLength_, m);
}

Index EntityOriginImpl::refLength() const
{
  return refLength_;
}

const EntityOrigin *EntityOriginImpl::asEntityOrigin() const
{
  return this;
}

Boolean EntityOriginImpl::defLocation(Offset off, const Origin *&origin, Index &index) const
{
  if (entity_.isNull())
    return 0;
  const InternalEntity *internal = entity_->asInternalEntity();
  if (!internal)
    return 0;
  return internal->text().charLocation(off, origin, index);
}

const EntityDecl *EntityOriginImpl::entityDecl() const
{
  return entity_.pointer();
}

const Markup *EntityOriginImpl::markup() const
{
  return markup_.pointer();
}


ReplacementOrigin::ReplacementOrigin(const Location &loc, Char origChar)
: loc_(loc), origChar_(origChar)
{
}

const Location &ReplacementOrigin::parent() const
{
  return loc_;
}

Boolean ReplacementOrigin::origChars(const Char *&s) const
{
  if (loc_.origin().isNull() || !loc_.origin()->origChars(s))
    s = &origChar_;
  return 1;
}

MultiReplacementOrigin::MultiReplacementOrigin(const Location &loc,
					       StringC &origChars)
: loc_(loc)
{
  origChars.swap(origChars_);
}

const Location &MultiReplacementOrigin::parent() const
{
  return loc_;
}

Boolean MultiReplacementOrigin::origChars(const Char *&s) const
{
  if (loc_.origin().isNull() || !loc_.origin()->origChars(s))
    s = origChars_.data();
  return 1;
}

ProxyOrigin::ProxyOrigin(const Origin *origin)
: origin_(origin)
{
}
 
const EntityOrigin *ProxyOrigin::asEntityOrigin() const
{
  return origin_->asEntityOrigin();
}

const InputSourceOrigin *ProxyOrigin::asInputSourceOrigin() const
{
  return origin_->asInputSourceOrigin();
}

const Location &ProxyOrigin::parent() const
{
  return origin_->parent();
}

Index ProxyOrigin::refLength() const
{
  return origin_->refLength();
}

Boolean ProxyOrigin::origChars(const Char *&p) const
{
  return origin_->origChars(p);
}

Boolean ProxyOrigin::inBracketedTextOpenDelim() const
{
  return origin_->inBracketedTextOpenDelim();
}

Boolean ProxyOrigin::inBracketedTextCloseDelim() const
{
  return origin_->inBracketedTextCloseDelim();
}

Boolean ProxyOrigin::isNumericCharRef(const Markup *&markup) const
{
  return origin_->isNumericCharRef(markup);
}

Boolean ProxyOrigin::isNamedCharRef(Index ind, NamedCharRef &ref) const
{
  return origin_->isNamedCharRef(ind, ref);
}

const EntityDecl *ProxyOrigin::entityDecl() const
{
  return origin_->entityDecl();
}

Boolean ProxyOrigin::defLocation(Offset off, const Origin *&origin, Index &index) const
{
  return origin_->defLocation(off, origin, index);
}

const Markup *ProxyOrigin::markup() const
{
  return origin_->markup();
}

const Entity *ProxyOrigin::entity() const
{
  return origin_->entity();
}

const ExternalInfo *ProxyOrigin::externalInfo() const
{
  return origin_->externalInfo();
}

Offset ProxyOrigin::startOffset(Index ind) const
{
  return origin_->startOffset(ind);
}

ExternalInfo::~ExternalInfo()
{
}

RTTI_DEF0(ExternalInfo)

NamedCharRef::NamedCharRef()
{
}

NamedCharRef::NamedCharRef(Index refStartIndex, RefEndType refEndType,
			   const StringC &origName)
: refStartIndex_(refStartIndex),
  refEndType_(refEndType),
  origName_(origName)
{
}

void NamedCharRef::set(Index refStartIndex, RefEndType refEndType,
		       const Char *s, size_t n)
{
  refStartIndex_ = refStartIndex;
  refEndType_ = refEndType;
  origName_.assign(s, n);
}

#ifdef SP_NAMESPACE
}
#endif
