// -*- C++ -*-
// --------------------------------------------------------------------
// Ipe object attributes (not just color!)
// --------------------------------------------------------------------
/*

    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 "ipecolor.h"

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

/*! \defgroup attr Ipe Attributes
  \brief Attributes for Ipe objects.

  To make the representation of IpeObjects reasonably compact, Ipe
  objects store attribute values as an index into an IpeRepository.
  IpeAttribute encapsulates this index.
*/

/*! \class IpeDashStyle
  \ingroup attr
  \brief An absolute dash style.
*/

/*! \class IpeStrokeStyle
  \ingroup attr
  \brief Encapsulates line join, line cap, and eofill vs windfill rule.

  This class encapsulates three IpeAttributes in the space of one \c int.
*/

/* Bit layout:
   0x1000 join set in 0x0003
   0x2000 cap set in 0x000c
   0x4000 wind set in 0x0010
 */

IpeAttribute IpeStrokeStyle::Join() const
{
  if (iBits & 0x1000)
    return IpeAttribute(IpeAttribute::ELineJoin, false, iBits & 3);
  return IpeAttribute();
}

IpeAttribute IpeStrokeStyle::Cap() const
{
  if (iBits & 0x2000)
    return IpeAttribute(IpeAttribute::ELineCap, false, (iBits & 0x0c) >> 2);
  return IpeAttribute();
}

IpeAttribute IpeStrokeStyle::WindRule() const
{
  if (iBits & 0x4000)
    return IpeAttribute(IpeAttribute::EWindRule, false, (iBits & 0x10) >> 4);
  return IpeAttribute();
}

void IpeStrokeStyle::SetJoin(int val)
{
  iBits &= ~0x03;
  iBits |= val + 0x1000;
}

void IpeStrokeStyle::SetCap(int val)
{
  iBits &= ~0x0c;
  iBits |= (val << 2) + 0x2000;
}

void IpeStrokeStyle::SetWindRule(bool wind)
{
  iBits &= ~0x10;
  iBits |= 0x4000;
  if (wind)
    iBits |= 0x10;
}

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

/*! \class IpeAttribute
  \ingroup attr
  \brief An attribute of an Ipe object.

  An attribute is either null (i.e. not defined---it hasn't been set
  yet), or symbolic (a name that has to be looked up using an
  IpeStyleSheet), or an absolute value.

  A null value is used in higher levels of the hierarchy to defer the
  setting of the attribute to a lower level.  For example, if the pen
  width is set in an IpeGroup object, all its members have this
  uniform pen width.  To allow the members to have individual pen
  width, the IpeGroup must have a null pen width.

  To make the representation more compact, Ipe objects store attribute
  values as an index into an IpeRepository.  IpeAttribute encapsulates
  this index.  Its meaning is as follows:

   - 0 : a <b>null</b> argument, see below,
   - 1 : the void color
   - 2 : the solid line style
   - 3 : the black color
   - 4 : the white color
   - if IsSymbolic: a symbolic name.
     Use IpeStyleSheet to map to an absolute value.
   - if IsNumeric: an absolute, numeric value.
     Use Number() to return the number, in the range 0.0 to 16777.215
     (with 3 digits fixed point precision).
   - if IsValue: an absolute value.
     Use IpeRepository to map to the absolute value.

  An attribute is absolute (IsAbsolute() returns true) if it is
  neither null nor symbolic.

  Furthermore, the attribute stores the type of attribute, such as
  scalar (a double value), color, dash style, etc.

  Note the difference between the null color, which means that the
  color has not yet been defined (it is left to lower-level objects),
  and the "void" color, which means that the object should not be
  stroked or filled.

  When rendering an object, a null color is interpreted as the void
  color. This allows leaf objects not to specify the void color.

  Note that it is impossible for style sheets to redefine the meaning
  of "black" and "white".  If a user needs a symbolic name that is
  black or white in some styles, she can always define another
  symbolic name, e.g. "Black" and "White".

  Note also the difference between null and "solid" (the special dash
  style of a solid line).  It is impossible for style sheets to
  redefine the meaning of "solid".

  When rendering an object, a null dash style is interpreted as solid.

*/

//! Return kind of attribute.
IpeKind IpeAttribute::Kind() const
{
  if (iName == 1 || iName == 3 || iName == 4)
    return EColor;
  if (iName == 2)
    return EDashStyle;
  return IpeKind((iName & EKindMask) >> EKindShift);
}

//! Create an attribute of given \a kind.
IpeAttribute::IpeAttribute(IpeKind kind, bool symbolic, int index)
{
  iName = index | (kind << EKindShift) | (symbolic ? ESymbolic : EValue);
}

//! Create an absolute numeric attribute.
IpeAttribute::IpeAttribute(IpeKind kind, IpeFixed value)
{
  iName = (kind << EKindShift) | EValue | ENumeric | value.Internal();
}

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

/*! \class IpeColor
  \ingroup attr
  \brief An absolute RGB color.
*/

//! Construct a color.
IpeColor::IpeColor(IpeScalar red, IpeScalar green, IpeScalar blue)
{
  iRed = red;
  iGreen = green;
  iBlue = blue;
}

//! Save to stream.
void IpeColor::Save(IpeStream &stream) const
{
  if (IsGray())
    stream << iRed;
  else
    stream << iRed << " " << iGreen << " " << iBlue;
}

//! Is it an absolute gray value?
bool IpeColor::IsGray() const
{
  return (iRed == iGreen && iRed == iBlue);
}

bool IpeColor::operator==(const IpeColor &rhs) const
{
  return (iRed == rhs.iRed) && (iGreen == rhs.iGreen) && (iBlue == rhs.iBlue);
}

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

/*! \class IpeRepository
  \ingroup attr
  \brief Repository of attribute values.

  Ipe documents can use symbolic attributes, such as 'normal', 'fat',
  or 'thin' for line thickness, or 'red', 'navy', 'turquoise' for
  color, as well as absolute attributes such as (0.5,0.5,0.5) for
  medium gray.  To avoid storing these very common values hundreds of
  times, IpeRepository keeps a repository of all the non-scalar
  attribute values in the document. Inside IpeObject's attributes are
  represented as integer indices into the repository.

  There are currently the following symbolic attributes:
  - Color
  - Line style (dash pattern)
  - Font size
  - Media size

  These attributes can be symbolic, but the value is stored inside the
  IpeAttribute:
  - Mark size
  - Arrow size
  - Grid size
  - Angle size (for angular snap)
  - Line width

*/

//! Constructor.
IpeRepository::IpeRepository()
{
  // ensure that string zero is 'normal'
  iStrings.push_back("normal");
  // ensure that symbolic dash styles 1 .. 4 are dashed, dotted, etc.
  iStrings.push_back("dashed");
  iStrings.push_back("dotted");
  iStrings.push_back("dash dotted");
  iStrings.push_back("dash dot dotted");
}

//! Return string with given index.
/*! Both positive (dash style) and negativ (symbolic name) index is
  possible. */
IpeString IpeRepository::ToString(IpeAttribute attr) const
{
  assert(attr.IsSymbolic() ||
	 attr.IsValue() &&
	 (attr.Kind() == IpeAttribute::EDashStyle ||
	  attr.Kind() == IpeAttribute::ETextSize));
  return iStrings[attr.Index()];
}

//! Return color with given index
IpeColor IpeRepository::ToColor(IpeAttribute attr) const
{
  assert(attr.Kind() == IpeAttribute::EColor);
  if (attr.IsValue())
    return iColors[attr.Index()];
  if (attr.Index() == 3)
    return IpeColor(0.0, 0.0, 0.0);
  assert(attr.Index() == 4);
  return IpeColor(1.0, 1.0, 1.0);
}

//! Return value with given index.
IpeFixed IpeRepository::ToScalar(IpeAttribute attr) const
{
  assert(attr.Kind() < IpeAttribute::EDashStyle);
  assert(attr.IsNumeric());
  return attr.Number();
}

//! Return vector with given index
IpeVector IpeRepository::ToVector(IpeAttribute attr) const
{
  assert(attr.IsValue() && (attr.Kind() == IpeAttribute::EMedia ||
			    attr.Kind() == IpeAttribute::ETextStretch));
  return iVectors[attr.Index()];
}

/*! Lookup a string (add it if it doesn't exist yet), and return
  symbolic attribute for it. */
IpeAttribute IpeRepository::ToSymbolic(IpeKind kind, IpeString name)
{
  assert(!name.empty());
  std::vector<IpeString>::const_iterator it =
    std::find(iStrings.begin(), iStrings.end(), name);
  if (it != iStrings.end())
    return IpeAttribute(kind, true, (it - iStrings.begin()));
  iStrings.push_back(name);
  return IpeAttribute(kind, true, iStrings.size() - 1);
}

//! Lookup a color (add it if it doesn't exist yet), and return index.
IpeAttribute IpeRepository::ToAttribute(const IpeColor &color)
{
  if (color == IpeColor(0.0, 0.0, 0.0))
    return IpeAttribute::Black();
  if (color == IpeColor(1.0, 1.0, 1.0))
    return IpeAttribute::White();
  std::vector<IpeColor>::const_iterator it =
    std::find(iColors.begin(), iColors.end(), color);
  if (it != iColors.end())
    return IpeAttribute(IpeAttribute::EColor, false, (it - iColors.begin()));
  iColors.push_back(color);
  // IpeAttribute a =  IpeAttribute(IpeAttribute::EColor, false, iColors.size() - 1);
  return IpeAttribute(IpeAttribute::EColor, false, iColors.size() - 1);
}

//! Lookup \a value (add it if it doesn't exist yet), and return index.
IpeAttribute IpeRepository::ToAttribute(IpeKind kind, IpeFixed value)
{
  return IpeAttribute(kind, value);
}

//! Lookup \a vector (add it if it doesn't exist yet), and return index.
IpeAttribute IpeRepository::ToAttribute(IpeKind kind, const IpeVector &vector)
{
  std::vector<IpeVector>::const_iterator it =
    std::find(iVectors.begin(), iVectors.end(), vector);
  if (it != iVectors.end())
    return IpeAttribute(kind, false, (it - iVectors.begin()));
  iVectors.push_back(vector);
  return IpeAttribute(kind, false, iVectors.size() - 1);
}

//! Create an IpeAttribute representing the color described.
/*! Empty string creates null color, string starting with a letter
  creates a symbolic color (includes special case "void"), otherwise
  absolute color. */
IpeAttribute IpeRepository::MakeColor(IpeString str)
{
  if (str.empty()) {
    return IpeAttribute();
  } else if (str == "void") {
    return IpeAttribute::Void();
  } else if (str == "black") {
    return IpeAttribute::Black();
  } else if (str == "white") {
    return IpeAttribute::White();
  } else if ('a' <= str[0] && str[0] <= 'z' ||
	     'A' <= str[0] && str[0] <= 'Z') {
    return ToSymbolic(IpeAttribute::EColor, str);
  } else {
    IpeLex st(str);
    IpeColor col;
    st >> col.iRed >> col.iGreen;
    if (st.Eos())
      col.iGreen = col.iBlue = col.iRed;
    else
      st >> col.iBlue;
    return ToAttribute(col);
  }
}

//! Construct dash style attribute from string.
/*! Empty string creates null value, string starting with '[' creates
  an absolute dash style, otherwise symbolic dash style (including the
  special case "solid").
*/
IpeAttribute IpeRepository::MakeDashStyle(IpeString str)
{
  if (str.empty()) {
    return IpeAttribute();
  } else if (str == "solid") {
    return IpeAttribute::Solid();
  } else if (str[0] == '[') {
    return MakeString(IpeAttribute::EDashStyle, str);
  } else
    return ToSymbolic(IpeAttribute::EDashStyle, str);
}

//! Construct text size attribute from string.
/*! Empty string creates null value, string starting with digit
  creates an numeric absolute value, string starting with letter
  creates symbolic text size, anything else creates absolute (string)
  text size.
*/
IpeAttribute IpeRepository::MakeTextSize(IpeString str)
{
  if (str.empty())
    return IpeAttribute();
  else if ('0' <= str[0] && str[0] <= '9')
    return IpeAttribute(IpeAttribute::ETextSize, IpeLex(str).GetFixed());
  else if ('a' <= str[0] && str[0] <= 'z' ||
	   'A' <= str[0] && str[0] <= 'Z')
    return ToSymbolic(IpeAttribute::ETextSize, str);
  else
    return MakeString(IpeAttribute::ETextSize, str);
}

//! Construct absolute value (which is a string).
IpeAttribute IpeRepository::MakeString(IpeKind kind, IpeString str)
{
  if (str.empty())
    return IpeAttribute();
  int index = ToSymbolic(kind, str).Index();
  return IpeAttribute(kind, false, index);
}

//! Construct scalar attribute from string.
/*! Empty string creates null value, string starting with non-letter creates
  an absolute value, string starting with letter creates symbolic value. */
IpeAttribute IpeRepository::MakeScalar(IpeKind kind, IpeString str)
{
  if (str.empty()) {
    return IpeAttribute();
  } else if ('a' <= str[0] && str[0] <= 'z' ||
	     'A' <= str[0] && str[0] <= 'Z') {
    return ToSymbolic(kind, str);
  } else {
    return ToAttribute(kind, IpeLex(str).GetFixed());
  }
}

//! Construct vector attribute from string.
/*! Empty string creates null value, string starting with non-letter creates
  an absolute value, string starting with letter creates symbolic value. */
IpeAttribute IpeRepository::MakeVector(IpeKind kind, IpeString str)
{
  if (str.empty()) {
    return IpeAttribute();
  } else if ('a' <= str[0] && str[0] <= 'z' ||
	     'A' <= str[0] && str[0] <= 'Z') {
    return ToSymbolic(kind, str);
  } else {
    IpeLex lex(str);
    IpeVector v;
    lex >> v.iX >> v.iY;
    return ToAttribute(kind, v);
  }
}

//! Construct symbolic attribute from string.
/*! Empty string creates null value, anything else creates symbolic value. */
IpeAttribute IpeRepository::MakeSymbol(IpeKind kind, IpeString str)
{
  if (str.empty())
    return IpeAttribute();
  else
    return ToSymbolic(kind, str);
}

//! Return string representation of attribute (symbolic or absolute).
IpeString IpeRepository::String(IpeAttribute attr) const
{
  if (attr.IsNull())
    return IpeString();
  if (attr.IsSymbolic())
    return ToString(attr);
  if (attr.IsVoid())
    return "void";
  if (attr.IsSolid())
    return "solid";
  if (attr.IsBlack())
    return "black";
  if (attr.IsWhite())
    return "white";

  IpeString str;
  IpeStringStream stream(str);
  if (attr.IsNumeric()) {
    stream << attr.Number();
    return str;
  }

  switch (attr.Kind()) {
  case IpeAttribute::EColor:
    stream << ToColor(attr);
    return str;
  case IpeAttribute::EDashStyle:
  case IpeAttribute::ETextSize:
    return ToString(attr);
  case IpeAttribute::EMedia:
  case IpeAttribute::ETextStretch:
    stream << ToVector(attr);
    return str;
  default:
    stream << ToScalar(attr);
    return str;
  }
}

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