/////////////////////////////////////////////////////////////////////////////////////////
//
// Please refer to "COPYRIGHT.md" for the relevant copyright statement of this software.
//
/////////////////////////////////////////////////////////////////////////////////////////
//
#ifndef GD_DBCOLOR_H
#define GD_DBCOLOR_H 1

#include "gsoft.h"
#include "gcadstrc.h"
#include "rxobject.h"
#include "GcDbCore2dDefs.h"
#include <vector>

#pragma pack(push, 8)

class GcCmEntityColor;

#if defined(_GSOFT_IOS_) || defined(LOCAL_SHARDER)
#  define GcCmColor_DEPRECATED_METHODS
#endif

class GCBASE_PORT GcCmEntityColor
{
public:
  enum Color
  {
    kRed,
    kGreen,
    kBlue
  };

  enum ColorMethod
  {
    kByLayer = 0xC0,
    kByBlock,
    kByColor,
    kByACI,
    kByPen,
    kForeground,
    kLayerOff,
    kLayerFrozen,
    kNone
  };

  enum ACIcolorMethod
  {
    kACIbyBlock = 0,
    kACIforeground = 7,
    kACIbyLayer = 256,
    kACIclear = 0,
    kACIstandard = 7,
    kACImaximum = 255,
    kACInone = 257,
    kACIminimum = -255,
    kACIfrozenLayer = -32700
  };

  GcCmEntityColor();
  GcCmEntityColor(const GcCmEntityColor& color);
  GcCmEntityColor(Gsoft::UInt8 red, Gsoft::UInt8 green, Gsoft::UInt8 blue);
  ~GcCmEntityColor(){};
  GcCmEntityColor& operator=(const GcCmEntityColor& color);
  bool operator==(const GcCmEntityColor& color) const;
  bool operator!=(const GcCmEntityColor& color) const;

  void setNone()
  {
    mRGBM.whole = 0;
    mRGBM.mdata.colorMethod = kNone;
  }
  void setByBlock()
  {
    mRGBM.whole = 0;
    mRGBM.mdata.colorMethod = kByBlock;
  }
  void setForeground()
  {
    mRGBM.whole = 0;
    mRGBM.mdata.colorMethod = kForeground;
  }
  void setByLayer()
  {
    mRGBM.whole = 0;
    mRGBM.mdata.colorMethod = kByLayer;
  }
  void setLayerOff();

  static GcCmEntityColor None()
  {
    GcCmEntityColor ec;
    ec.setNone();
    return ec;
  }
  static GcCmEntityColor ByBlock()
  {
    GcCmEntityColor ec;
    ec.setByBlock();
    return ec;
  }
  static GcCmEntityColor ByLayer()
  {
    GcCmEntityColor ec;
    ec.setByLayer();
    return ec;
  }
  static GcCmEntityColor Foreground()
  {
    GcCmEntityColor ec;
    ec.setForeground();
    return ec;
  }
  static GcCmEntityColor white()
  {
    return GcCmEntityColor(255, 255, 255);
  }
  static GcCmEntityColor black()
  {
    return GcCmEntityColor(0, 0, 0);
  }
  ColorMethod colorMethod() const;

  Gcad::ErrorStatus setColorIndex(Gsoft::Int16 colorIndex);
  Gsoft::Int16 colorIndex() const;

  Gcad::ErrorStatus setLayerIndex(Gsoft::Int32 layerIndex);
  Gsoft::Int32 layerIndex() const;

  Gcad::ErrorStatus setPenIndex(Gsoft::UInt16 penIndex);
  Gsoft::UInt16 penIndex() const;

  Gcad::ErrorStatus setRGB(Gsoft::UInt8 red, Gsoft::UInt8 green, Gsoft::UInt8 blue);
  Gsoft::UInt8 red() const;
  Gsoft::UInt8 green() const;
  Gsoft::UInt8 blue() const;

  Gcad::ErrorStatus setCOLORREF(Gsoft::ColorRef cref);
  Gsoft::ColorRef getCOLORREF() const;

  Gcad::ErrorStatus setRGB(Gsoft::RGBQuad rgbquad);
  Gsoft::RGBQuad getRGB() const;

  Gcad::ErrorStatus setRGBM(Gsoft::UInt32 rgbmValue);
  Gsoft::UInt32 getRGBM() const;

  bool isByColor() const;
  bool isByLayer() const;
  bool isByBlock() const;
  bool isByACI() const;
  bool isByPen() const;
  bool isForeground() const;
  bool isLayerOff() const;
  bool isLayerFrozen() const;
  bool isNone() const;
  bool isLayerFrozenOrOff() const;

  bool canResolveRGB() const;
  GcCmEntityColor makeRGB(Gsoft::ColorRef background_rgb = 0) const;
  GcCmEntityColor forceRGB(Gsoft::ColorRef background_rgb = 0) const;
  GcCmEntityColor makeTrueColor() const;

private:
  union
  {
    Gsoft::UInt32 whole;
    Gsoft::Int16 indirect;
    struct
    {
      Gsoft::UInt8 blue, green, red, colorMethod;
    } mdata;
    Gsoft::Int32 mnIndirect32;
  } mRGBM;

  Gsoft::Int32 indirect24() const;
  Gsoft::UInt8 lookUpACI() const;
  const std::vector<GcCmEntityColor>& lookup_table() const;

public:
  GcCmEntityColor(ColorMethod eColorMethod);
  Gcad::ErrorStatus setColorMethod(ColorMethod eColorMethod);
  Gcad::ErrorStatus setColor(Gsoft::UInt32 color);
  Gsoft::UInt32 color() const;
  Gcad::ErrorStatus setRed(Gsoft::UInt8 red);
  Gcad::ErrorStatus setGreen(Gsoft::UInt8 green);
  Gcad::ErrorStatus setBlue(Gsoft::UInt8 blue);
  Gsoft::UInt32 trueColor() const;
  Gsoft::UInt8 trueColorMethod() const;
  Gcad::ErrorStatus setTrueColor();
  Gcad::ErrorStatus setTrueColorMethod();
  static Gsoft::UInt32 lookUpRGB(Gsoft::UInt8 colorIndex);
  static Gsoft::UInt8 lookUpACI(Gsoft::UInt8 red, Gsoft::UInt8 green, Gsoft::UInt8 blue);
};

inline GcCmEntityColor::GcCmEntityColor(ColorMethod eColorMethod)
{
  mRGBM.whole = 0;
  mRGBM.mdata.colorMethod = static_cast<Gsoft::UInt8>(eColorMethod);
}

inline Gcad::ErrorStatus GcCmEntityColor::setColor(Gsoft::UInt32 color)
{
  return setRGBM(color);
}

inline Gsoft::UInt32 GcCmEntityColor::color() const
{
  return getRGBM();
}

inline Gcad::ErrorStatus GcCmEntityColor::setRed(Gsoft::UInt8 red)
{
  mRGBM.mdata.red = red;
  return Gcad::eOk;
}

inline Gcad::ErrorStatus GcCmEntityColor::setGreen(Gsoft::UInt8 green)
{
  mRGBM.mdata.green = green;
  return Gcad::eOk;
}

inline Gcad::ErrorStatus GcCmEntityColor::setBlue(Gsoft::UInt8 blue)
{
  mRGBM.mdata.blue = blue;
  return Gcad::eOk;
}

inline GcCmEntityColor::GcCmEntityColor()
{
  mRGBM.whole = 0;
  mRGBM.mdata.colorMethod = kByColor;
}

inline GcCmEntityColor::GcCmEntityColor(const GcCmEntityColor& color)
{
  mRGBM.whole = color.mRGBM.whole;
}

inline GcCmEntityColor::GcCmEntityColor(Gsoft::UInt8 red, Gsoft::UInt8 green, Gsoft::UInt8 blue)
{
  mRGBM.mdata.red = red;
  mRGBM.mdata.green = green;
  mRGBM.mdata.blue = blue;
  mRGBM.mdata.colorMethod = kByColor;
}

inline GcCmEntityColor& GcCmEntityColor::operator=(const GcCmEntityColor& color)
{
  mRGBM.whole = color.mRGBM.whole;
  return *this;
}

inline bool GcCmEntityColor::operator==(const GcCmEntityColor& color) const
{
  return mRGBM.whole == color.mRGBM.whole;
}

inline bool GcCmEntityColor::operator!=(const GcCmEntityColor& color) const
{
  return mRGBM.whole != color.mRGBM.whole;
}

inline GcCmEntityColor::ColorMethod GcCmEntityColor::colorMethod() const
{
  return (ColorMethod)mRGBM.mdata.colorMethod;
}

inline Gsoft::Int32 GcCmEntityColor::indirect24() const
{
  Gsoft::Int32 nRet = mRGBM.mnIndirect32;
  if ((nRet & 0x800000) != 0)
    nRet |= 0xff000000;
  else
    nRet &= ~0xff000000;
  return nRet;
}

inline Gsoft::Int32 GcCmEntityColor::layerIndex() const
{
  if (mRGBM.mdata.colorMethod != kByLayer && mRGBM.mdata.colorMethod != kLayerOff)
    return -1;

  return indirect24();
}

inline Gsoft::UInt16 GcCmEntityColor::penIndex() const
{
  if (mRGBM.mdata.colorMethod != kByPen)
    return 0xffff;
  return (Gsoft::UInt16)mRGBM.indirect;
}

inline bool GcCmEntityColor::isByColor() const
{
  return mRGBM.mdata.colorMethod == kByColor;
}

inline bool GcCmEntityColor::isByLayer() const
{
  return (mRGBM.mdata.colorMethod == kByLayer || (mRGBM.mdata.colorMethod == kByACI && mRGBM.indirect == kACIbyLayer)) ?
           true :
           false;
}

inline bool GcCmEntityColor::isByBlock() const
{
  return (mRGBM.mdata.colorMethod == kByBlock || (mRGBM.mdata.colorMethod == kByACI && mRGBM.indirect == kACIbyBlock)) ?
           true :
           false;
}

inline bool GcCmEntityColor::isByACI() const
{
  return mRGBM.mdata.colorMethod == kByACI;
}

inline bool GcCmEntityColor::isByPen() const
{
  return mRGBM.mdata.colorMethod == kByPen;
}

inline bool GcCmEntityColor::isForeground() const
{
  return (mRGBM.mdata.colorMethod == kForeground ||
          (mRGBM.mdata.colorMethod == kByACI && mRGBM.indirect == kACIforeground)) ?
           true :
           false;
}

inline bool GcCmEntityColor::isLayerOff() const
{
  return (mRGBM.mdata.colorMethod == kLayerOff ||
          (mRGBM.mdata.colorMethod == kByACI && mRGBM.indirect < 0 && mRGBM.indirect != kACIfrozenLayer)) ?
           true :
           false;
}

inline bool GcCmEntityColor::isLayerFrozen() const
{
  return (mRGBM.mdata.colorMethod == kLayerFrozen ||
          (mRGBM.mdata.colorMethod == kByACI && mRGBM.indirect == kACIfrozenLayer)) ?
           true :
           false;
}

inline bool GcCmEntityColor::isLayerFrozenOrOff() const
{
  return isLayerFrozen() || isLayerOff();
}

inline bool GcCmEntityColor::isNone() const
{
  return (mRGBM.mdata.colorMethod == kNone || (mRGBM.mdata.colorMethod == kByACI && mRGBM.indirect == kACInone)) ?
           true :
           false;
}

class GSOFT_NO_VTABLE GCBASE_PORT GcCmColorBase
{
public:
  virtual ~GcCmColorBase()
  {
  }

  virtual GcCmEntityColor::ColorMethod colorMethod() const = 0;

  virtual void setNone() = 0;
  virtual void setByBlock() = 0;
  virtual void setForeground() = 0;
  virtual void setLayerOff() = 0;
  virtual void setByLayer() = 0;

  virtual bool isByColor() const = 0;
  virtual bool isByLayer() const = 0;
  virtual bool isByBlock() const = 0;
  virtual bool isByACI() const = 0;
  virtual bool isByPen() const = 0;
  virtual bool isForeground() const = 0;

  virtual Gcad::ErrorStatus setRGB(Gsoft::UInt8 red, Gsoft::UInt8 green, Gsoft::UInt8 blue) = 0;
  virtual Gsoft::UInt8 red() const = 0;
  virtual Gsoft::UInt8 green() const = 0;
  virtual Gsoft::UInt8 blue() const = 0;

  virtual Gcad::ErrorStatus setCOLORREF(Gsoft::ColorRef cref) = 0;
  virtual Gsoft::ColorRef getCOLORREF() const = 0;

  virtual Gcad::ErrorStatus setRGB(Gsoft::RGBQuad rgbquad) = 0;
  virtual Gsoft::RGBQuad getRGB() const = 0;

  virtual Gcad::ErrorStatus setRGBM(Gsoft::UInt32 rgbmValue) = 0;
  virtual Gsoft::UInt32 getRGBM() const = 0;

  virtual Gsoft::UInt16 colorIndex() const = 0;
  virtual Gcad::ErrorStatus setColorIndex(Gsoft::UInt16 colorIndex) = 0;
  virtual Gsoft::UInt16 penIndex() const = 0;
  virtual Gcad::ErrorStatus setPenIndex(Gsoft::UInt16 penIndex) = 0;

  virtual Gcad::ErrorStatus setNames(const GCHAR* colorName, const GCHAR* bookName = NULL) = 0;
  virtual const GCHAR* colorName(void) const = 0;
  virtual const GCHAR* bookName(void) const = 0;
  virtual const GCHAR* colorNameForDisplay(void) = 0;
  virtual bool hasColorName(void) const = 0;
  virtual bool hasBookName(void) const = 0;
  virtual Gcad::ErrorStatus setColorMethod(GcCmEntityColor::ColorMethod eColorMethod) = 0;
  virtual Gsoft::UInt32 color() const = 0;
  virtual Gcad::ErrorStatus setColor(Gsoft::UInt32 color) = 0;
  virtual Gcad::ErrorStatus setRed(Gsoft::UInt8 red) = 0;
  virtual Gcad::ErrorStatus setGreen(Gsoft::UInt8 green) = 0;
  virtual Gcad::ErrorStatus setBlue(Gsoft::UInt8 blue) = 0;
};

class GCBASE_PORT GcCmTransparency
{
public:
  enum transparencyMethod
  {
    kByLayer = 0,
    kByBlock,
    kByAlpha,
    kErrorValue
  };

  enum
  {
    kTransparencyByLayer = (unsigned long)kByLayer,
    kTransparencyByBlock = (unsigned long)kByBlock,
    kTransparencySolid = ((unsigned long)(kByAlpha | (0xff << 24))),
    kTransparencyClear = (unsigned long)kByAlpha
  };

  GcCmTransparency()
  {
    mAM.whole = kTransparencyByLayer;
  }
  GcCmTransparency(Gsoft::UInt8 alpha);
  GcCmTransparency(double alphaPercent);
  GcCmTransparency(const GcCmTransparency& other)
  {
    mAM.whole = other.mAM.whole;
  }
  ~GcCmTransparency()
  {
  }

  GcCmTransparency& operator=(const GcCmTransparency& other);
  bool operator==(const GcCmTransparency& other) const;
  bool operator!=(const GcCmTransparency& other) const;

  void setAlpha(Gsoft::UInt8 alpha);
  void setAlphaPercent(double alphaPercent);

  void setMethod(transparencyMethod method);

  Gsoft::UInt8 alpha(void) const;
  double alphaPercent(void) const;

  bool isByAlpha(void) const
  {
    return (mAM.mdata.method == kByAlpha);
  }
  bool isByBlock(void) const
  {
    return (mAM.mdata.method == kByBlock);
  }
  bool isByLayer(void) const
  {
    return (mAM.mdata.method == kByLayer);
  }
  bool isInvalid(void) const
  {
    return (mAM.mdata.method == kErrorValue);
  }
  bool isClear(void) const;
  bool isSolid(void) const;

  Gsoft::UInt32 serializeOut(void) const
  {
    return mAM.whole;
  }
  void serializeIn(Gsoft::UInt32);

private:
  union AM
  {
    struct
    {
      Gsoft::UInt8 alpha;
      Gsoft::UInt8 reserved1;
      Gsoft::UInt8 reserved2;
      Gsoft::UInt8 method;
    } mdata;
    Gsoft::UInt32 whole;
  };

  AM mAM;
};

namespace GcCm
{
  enum DialogTabs
  {
    kACITab = 1,
    kTrueColorTab = 2,
    kColorBookTab = 4,
  };

  enum ACIColors
  {
    kACIByBlock = 0,
    kACIRed = 1,
    kACIYellow = 2,
    kACIGreen = 3,
    kACICyan = 4,
    kACIBlue = 5,
    kACIMagenta = 6,
    kACIForeground = 7,
    kACIByLayer = 256,
  };
}

#pragma pack(pop)

class GcDbObjectId;

GCBASE_PORT GcCmEntityColor gccmResolveEffectiveColorToRGB(const GcCmEntityColor& effectiveColor,
                                                           GcDbObjectId entLayerId);

GCBASE_PORT GcCmEntityColor gccmAttenuateRGB(const GcCmEntityColor& in);

#define GSFT_GCCMENTITYCOLOR_DEFINED
#include "gcarrayhelper.h"

#endif