// --------------------------------------------------------------------
// Basic classes
// --------------------------------------------------------------------
/*

    This file is part of the extensible drawing editor Ipe.
    Copyright (C) 1993-2004  Otfried Cheong

    Ipe 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.

    As a special exception, you have permission to link Ipe with the
    CGAL library and distribute executables, as long as you follow the
    requirements of the Gnu General Public License in regard to all of
    the software in the executable aside from CGAL.

    Ipe 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 Ipe; if not, you can find it at
    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "ipebase.h"
#include "ipelet.h"
#include <cstring>
#include <stdarg.h>

// --------------------------------------------------------------------

/*! \defgroup base Ipe Base
  \brief Basic classes for Ipe.

  Some very basic type definitions, streams, lexical analysis, and XML
  parsing.

  All parts of Ipe make use of the STL.  The C++ I/O streams library
  is not used, as Ipelib doesn't do I/O.  Ipe objects support
  internalization and externalization through an abstract interface
  based on IpeStream's.

  Clients of Ipelib can use any I/O library that implements this
  interface.  Command line programs can use \c cstdio, Ipe itself uses
  the Qt file portability layer.
*/

//! Return the Ipelib version.
/*! This is available as a function so that one can verify what
  version of Ipelib one has actually linked with (as opposed to the
  header files used during compilation).
*/

extern int IpelibVersion()
{
  return IPELIB_VERSION;
}

// --------------------------------------------------------------------

/*! \class IpeString
  \ingroup base
  \brief Strings and buffers.

  IpeString is is an implicitly shared byte string.  It is designed to
  be efficient for strings of arbitrary length, and supposed to be
  passed by value (the size of IpeString is a single pointer).
  Sharing is implicit---the string creates its own representation as
  soon as it is modified.

  IpeString can be used for binary data.  For text, it is usually
  assumed that the string is UTF-8 encoded, but only the unicode
  member function actually requires this. In particular, all indices
  into the string are byte indices, not Unicode character indices.

  Ipe is not using the STL string type because the C++ standard does
  neither guarantee that passing by value is efficient, nor that
  std::string is efficient for very long strings (although in practice
  that is probably true).  However, IpeString is designed to be
  plug-in compatible with std::string, so Ipe can switch any time it
  is deemed appropriate (in particular, unlike the rest of Ipe,
  IpeString uses lower-case member names!).

*/

IpeString::IpeString()
{
  iImp = new Imp;
  iImp->iRefCount = 1;
  iImp->iSize = 0;
  iImp->iCapacity = 32;
  iImp->iData = new char[iImp->iCapacity];
}

IpeString::IpeString(const char *str)
{
  int len = strlen(str);
  iImp = new Imp;
  iImp->iRefCount = 1;
  iImp->iSize = len;
  iImp->iCapacity = len + 32;
  iImp->iData = new char[iImp->iCapacity];
  memcpy(iImp->iData, str, iImp->iSize);
}

IpeString::IpeString(const IpeString &rhs)
{
  iImp = rhs.iImp;
  iImp->iRefCount++;
}

IpeString::IpeString(const IpeString &rhs, int index, int len)
{
  // actually available data
  int len1 = index < rhs.size() ? rhs.size() - index : 0;
  if (len < 0 || len1 < len)
    len = len1;
  iImp = new Imp;
  iImp->iRefCount = 1;
  iImp->iSize = len;
  iImp->iCapacity = len + 32;
  iImp->iData = new char[iImp->iCapacity];
  if (len > 0)
    memcpy(iImp->iData, rhs.iImp->iData + index, len);
}

IpeString &IpeString::operator=(const IpeString &rhs)
{
  if (iImp != rhs.iImp) {
    if (iImp->iRefCount == 1) {
      delete [] iImp->iData;
      delete iImp;
    } else
      iImp->iRefCount--;
    iImp = rhs.iImp;
    iImp->iRefCount++;
  }
  return *this;
}

IpeString::~IpeString()
{
  if (iImp->iRefCount == 1) {
    delete [] iImp->iData;
    delete iImp;
  } else
    iImp->iRefCount--;
}

//! Make a private copy of the string with \a n bytes to spare.
/*! When a private copy has to be made an extra 32 bytes is ensured. */
void IpeString::Detach(int n)
{
  if (iImp->iRefCount > 1 || (iImp->iSize + n > iImp->iCapacity)) {
    Imp *imp = new Imp;
    imp->iRefCount = 1;
    imp->iSize = iImp->iSize;
    imp->iCapacity = iImp->iCapacity;
    while (imp->iSize + 32 + n > imp->iCapacity)
      imp->iCapacity *= 2;
    imp->iData = new char[imp->iCapacity];
    memcpy(imp->iData, iImp->iData, imp->iSize);
    iImp->iRefCount--;
    if (iImp->iRefCount == 0) {
      delete [] iImp->iData;
      delete iImp;
    }
    iImp = imp;
  }
}

const char *IpeString::CString() const
{
  IpeString *This = const_cast<IpeString *>(this);
  if (iImp->iSize == iImp->iCapacity)
    This->Detach(1);
  This->iImp->iData[iImp->iSize] = '\0';
  return data();
}

void IpeString::erase()
{
  Detach(0);
  iImp->iSize = 0;
}

void IpeString::append(const IpeString &rhs)
{
  int n = rhs.size();
  Detach(n);
  memcpy(iImp->iData + iImp->iSize, rhs.iImp->iData, n);
  iImp->iSize += n;
}

void IpeString::append(char ch)
{
  Detach(1);
  iImp->iData[iImp->iSize++] = ch;
}

bool IpeString::operator==(const IpeString &rhs) const
{
  return (size() == rhs.size() &&
	  strncmp(iImp->iData, rhs.iImp->iData, size()) == 0);
}

bool IpeString::operator<(const IpeString &rhs) const
{
  int n = size() < rhs.size() ? size() : rhs.size();
  int cmp = strncmp(iImp->iData, rhs.iImp->iData, n);
  return (cmp < 0 || cmp == 0 && size() < rhs.size());
}

IpeString IpeString::operator+(const IpeString &rhs) const
{
  IpeString s(*this);
  s.append(rhs);
  return s;
}

static const uchar bytesFromUTF8[256] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};

static const uchar firstByteMark[7] = {
  0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0
};

//! Return Unicode value from UTF-8 string.
/*! The \a index is incremented to the next UTF-8 character. */
int IpeString::unicode(int &index) const
{
  int wch = uchar(iImp->iData[index++]);
  if ((wch & 0xc0) == 0x80) {
    // not on first byte of a UTF-8 sequence
    while (index < iImp->iSize && (iImp->iData[index] & 0xc0) == 0x80)
      index++;
    return 0xfffd;
  }
  int extraBytes = bytesFromUTF8[wch & 0xff];
  wch -= firstByteMark[extraBytes];
  while (extraBytes--) {
    if (index >= iImp->iSize)
      return 0xfffd; // UTF-8 sequence is incomplete
    if ((iImp->iData[index] & 0xc0) != 0x80)
      return 0xfffd; // UTF-8 sequence is incorrect
    wch <<= 6;
    wch |= (iImp->iData[index++] & 0x3f);
  }
  return wch;
}

// --------------------------------------------------------------------

/*! \class IpeFixed
  \ingroup base
  \brief Fixed point number with three (decimal) fractional digits
*/

//! Return value times (a/b)
IpeFixed IpeFixed::Mult(int a, int b) const
{
  return IpeFixed::FromInternal(iValue * a / b);
}

// --------------------------------------------------------------------

/*! \class IpeLex
  \ingroup base
  \brief Lexical analyser. Seeded with a string.
*/

//! Construct lexical analyzer from a string.
IpeLex::IpeLex(IpeString str)
  : iString(str), iPos(0)
{
  // nothing
}

//! Return NextToken, but without extracting it.
IpeString IpeLex::Token()
{
  int pos = iPos;
  IpeString str = NextToken();
  iPos = pos;
  return str;
}

/*! Skips any whitespace before the token.
  Returns empty string if end of string is reached.
*/

//! Extract next token.
IpeString IpeLex::NextToken()
{
  SkipWhitespace();
  uint mark = iPos;
  while (!Eos() && iString[iPos] > ' ')
    ++iPos;
  return iString.substr(mark, iPos - mark);
}

//! Extract integer token (skipping whitespace).
int IpeLex::GetInt()
{
  IpeString str = NextToken();
  return std::atoi(str.CString());
}

inline int HexDigit(int ch)
{
  if ('0' <= ch && ch <= '9')
    return ch - '0';
  if ('a' <= ch && ch <= 'f')
    return ch - 'a' + 10;
  if ('A' <= ch && ch <= 'F')
    return ch - 'A' + 10;
  return 0;
}

//! Extract byte in hex (skipping whitespace).
int IpeLex::GetHexByte()
{
  int ch1 = '0', ch2 = '0';
  SkipWhitespace();
  if (!Eos())
    ch1 = iString[iPos++];
  SkipWhitespace();
  if (!Eos())
    ch2 = iString[iPos++];
  return (HexDigit(ch1) << 4) | HexDigit(ch2);
}

//! Extract hexadecimal token (skipping whitespace).
unsigned long int IpeLex::GetHexNumber()
{
  IpeString str = NextToken();
  return std::strtoul(str.CString(), 0, 16);
}

//! Extract IpeFixed token (skipping whitespace).
IpeFixed IpeLex::GetFixed()
{
  IpeString str = NextToken();
  int i = 0;
  while (i < str.size() && str[i] != '.')
    ++i;
  int integral = std::strtol(str.substr(0, i).CString(), 0, 10);
  int fractional = 0;
  if (i < str.size()) {
    IpeString s = (str.substr(i+1) + "000").substr(0, 3);
    fractional = std::strtol(s.CString(), 0, 10);
  }
  return IpeFixed::FromInternal(integral * 1000 + fractional);
}

//! Extract double token (skipping whitespace).
double IpeLex::GetDouble()
{
  IpeString str = NextToken();
  return std::atof(str.CString());
}

//! Skip over whitespace.
void IpeLex::SkipWhitespace()
{
  while (!Eos() && iString[iPos] <= ' ')
    ++iPos;
}

// --------------------------------------------------------------------

/*! \class IpeBuffer
  \ingroup base
  \brief A memory buffer.

  Implements sharing semantics using reference counting.
*/

//! Create empty buffer.
IpeBuffer::IpeBuffer()
{
  iImp = new Imp;
  iImp->iRefCount = 1;
  iImp->iData = 0;
  iImp->iSize = 0;
}

//! Create buffer of specified size.
IpeBuffer::IpeBuffer(int size)
{
  iImp = new Imp;
  iImp->iRefCount = 1;
  iImp->iSize = size;
  iImp->iData = new char[size];
}

//! Copy constructor. Constant time, buffer is shared.
IpeBuffer::IpeBuffer(const IpeBuffer &rhs)
{
  iImp = rhs.iImp;
  iImp->iRefCount++;
}

//! Create buffer by copying the data.
IpeBuffer::IpeBuffer(const char *data, int size)
{
  iImp = new Imp;
  iImp->iRefCount = 1;
  iImp->iSize = size;
  iImp->iData = new char[size];
  memcpy(iImp->iData, data, size);
}

//! Destructor.
IpeBuffer::~IpeBuffer()
{
  iImp->iRefCount--;
  if (iImp->iRefCount == 0) {
    delete [] iImp->iData;
    delete iImp;
  }
}

//! Assignment operator (constant-time).
IpeBuffer &IpeBuffer::operator=(const IpeBuffer &rhs)
{
  if (this != &rhs) {
    iImp->iRefCount--;
    if (iImp->iRefCount == 0) {
      delete [] iImp->iData;
      delete iImp;
    }
    iImp = rhs.iImp;
    iImp->iRefCount++;
  }
  return *this;
}

// --------------------------------------------------------------------

/*! \class IpeStream
  \ingroup base
  \brief Abstract base class for output streams.
*/

IpeStream::~IpeStream()
{
  // empty implementation of pure virtual destructor
}

//! Close the stream.  No more writing allowed!
void IpeStream::Close()
{
  // nothing
}

//! Default implementation uses PutChar.
void IpeStream::PutString(IpeString s)
{
  for (int i = 0; i < s.size(); ++i)
    PutChar(s[i]);
}

//! Default implementation uses PutChar.
void IpeStream::PutCString(const char *s)
{
  while (*s)
    PutChar(*s++);
}

//! Default implementation uses PutChar.
void IpeStream::PutRaw(const char *data, int size)
{
  for (int i = 0; i < size; i++)
    PutChar(data[i]);
}

//! Output integer.
IpeStream &IpeStream::operator<<(int i)
{
  char buf[30];
  std::sprintf(buf, "%d", i);
  *this << buf;
  return *this;
}

//! Output double.
IpeStream &IpeStream::operator<<(double d)
{
  char buf[30];
  std::sprintf(buf, "%g", d);
  *this << buf;
  return *this;
}

//! Output byte in hexadecimal.
void IpeStream::PutHexByte(char b)
{
  char buf[3];
  std::sprintf(buf, "%02x", (b & 0xff));
  *this << buf;
}

//! Save a string with XML escaping of &, >, <.
void IpeStream::PutXmlString(IpeString s)
{
  for (int i = 0; i < s.size(); ++i) {
    char ch = s[i];
    switch (ch) {
    case '&': *this << "&amp;"; break;
    case '<': *this << "&lt;"; break;
    case '>': *this << "&gt;"; break;
    default:
      *this << ch;
      break;
    }
  }
}

// --------------------------------------------------------------------

/*! \class IpeStringStream
  \ingroup base
  \brief Stream writing into an IpeString.
*/

//! Construct with string reference.
IpeStringStream::IpeStringStream(IpeString &string)
  : iString(string)
{
  // nothing
}


void IpeStringStream::PutChar(char ch)
{
  iString += ch;
}

void IpeStringStream::PutString(IpeString s)
{
  iString += s;
}

void IpeStringStream::PutCString(const char *s)
{
  iString += s;
}

void IpeStringStream::PutRaw(const char *data, int size)
{
  for (int i = 0; i < size; i++)
    iString += data[i];
}

int IpeStringStream::Tell() const
{
  return iString.size();
}

// --------------------------------------------------------------------

/*! \class IpeFileStream
  \ingroup base
  \brief Stream writing into an open file.
*/

//! Constructor.
IpeFileStream::IpeFileStream(std::FILE *file)
  : iFile(file)
{
  // nothing
}

void IpeFileStream::PutChar(char ch)
{
  std::fputc(ch, iFile);
}

void IpeFileStream::PutString(IpeString s)
{
  for (int i = 0; i < s.size(); ++i)
    std::fputc(s[i], iFile);
}

void IpeFileStream::PutCString(const char *s)
{
  fputs(s, iFile);
}

void IpeFileStream::PutRaw(const char *data, int size)
{
  for (int i = 0; i < size; i++)
    std::fputc(data[i], iFile);
}

int IpeFileStream::Tell() const
{
  return std::ftell(iFile);
}

// --------------------------------------------------------------------

/*! \class IpeDataSource
 * \ingroup base
 * \brief Interface for getting data for parsing.
 */

//! Pure virtual destructor.
IpeDataSource::~IpeDataSource()
{
  // nothing
}

// --------------------------------------------------------------------

/*! \class IpeFileSource
  \ingroup base
  \brief Data source for parsing from a file.
*/

IpeFileSource::IpeFileSource(std::FILE *file)
  : iFile(file)
{
  // nothing
}

int IpeFileSource::GetChar()
{
  return std::fgetc(iFile);
}

/*! \class IpeBufferSource
  \ingroup base
  \brief Data source for parsing from a buffer.
*/

IpeBufferSource::IpeBufferSource(const IpeBuffer &buffer)
  : iBuffer(buffer)
{
  iPos = 0;
}

int IpeBufferSource::GetChar()
{
  if (iPos >= iBuffer.size())
    return EOF;
  return uchar(iBuffer[iPos++]);
}

// --------------------------------------------------------------------

void (*ipeDebugHandler)(const char *) = 0;

void ipeDebug(const char *msg, ...)
{
  if (ipeDebugHandler) {
    char buf[8196];
    va_list ap;
    va_start(ap, msg);
    std::vsprintf(buf, msg, ap);
    va_end(ap);
    ipeDebugHandler(buf);
  }
}

// --------------------------------------------------------------------
