#pragma once

#include "gsoft.h"
#include "dbcolor.h"
#include "gcutGcString.h"
#include "gcdb.h"

extern const wchar_t kszGstarCAD_Color_Index[];
extern const wchar_t kszUNNAMED[];

class GcDbDxfFiler;
class GcDbAuditInfo;
class GcDbDwgFiler;
class GCBASE_PORT GcCmColor : public GcCmColorBase
{
public:
  GcCmColor();
  GcCmColor(const GcCmColor&);
  GcCmColor(const GcCmColorBase&);
  GcCmColor& operator=(const GcCmColor& inputColor);
  GcCmColor& operator=(const GcCmColorBase& inputColor);
  GcCmColor(const GcCmEntityColor&);
  GcCmColor& operator=(const GcCmEntityColor& inputColor);
  ~GcCmColor();

  bool operator==(const GcCmColor& color) const;
  bool operator!=(const GcCmColor& color) const;
  bool operator==(const GcCmColorBase& color) const;
  bool operator!=(const GcCmColorBase& color) const;

  Gcad::ErrorStatus getDescription(GcString& desc) const;
  Gcad::ErrorStatus getExplanation(GcString& expl) const;
#define GcCmColor_Deprecated_GCHAR_Methods
#ifdef GcCmColor_Deprecated_GCHAR_Methods
  Gcad::ErrorStatus getDescription(GCHAR*& desc) const;
  Gcad::ErrorStatus getExplanation(GCHAR*& expl) const;
#endif
  GcCmEntityColor::ColorMethod colorMethod() const override;

  void setNone() override
  {
    mColor.setNone();
  }
  void setByBlock() override
  {
    mColor.setByBlock();
  }
  void setForeground() override
  {
    mColor.setForeground();
  }
  void setLayerOff() override
  {
    mColor.setLayerOff();
  }
  void setByLayer() override
  {
    mColor.setByLayer();
  }
  bool isByColor() const override;
  bool isByLayer() const override;
  bool isByBlock() const override;
  bool isByACI() const override;
  bool isByPen() const override;
  bool isForeground() const override;

  bool isNone() const;

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

  Gsoft::UInt8 red() const override;
  Gsoft::UInt8 green() const override;
  Gsoft::UInt8 blue() const override;

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

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

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

  Gsoft::UInt16 colorIndex() const override;
  Gcad::ErrorStatus setColorIndex(Gsoft::UInt16 colorIndex) override;
  Gsoft::UInt16 penIndex() const override;
  Gcad::ErrorStatus setPenIndex(Gsoft::UInt16 penIndex) override;

  Gcad::ErrorStatus setNames(const GCHAR* colorName, const GCHAR* bookName = nullptr) override;
  const GCHAR* colorName(void) const override;
  const GCHAR* bookName(void) const override;
  const GCHAR* colorNameForDisplay(void) override;
  bool hasColorName(void) const override;
  bool hasBookName(void) const override;

  GcCmEntityColor entityColor(void) const;
  int dictionaryKeyLength(void) const;

  Gcad::ErrorStatus getDictionaryKey(GcString& sKey) const;
#ifdef GcCmColor_Deprecated_GCHAR_Methods
  Gcad::ErrorStatus getDictionaryKey(GCHAR* pKey, size_t nLen) const;
#endif
  Gcad::ErrorStatus setNamesFromDictionaryKey(const GCHAR* pKey);

  Gcad::ErrorStatus dwgIn(GcDbDwgFiler* pInputFiler);
  Gcad::ErrorStatus dwgOut(GcDbDwgFiler* pOutputFiler) const;

  Gcad::ErrorStatus dxfIn(GcDbDxfFiler* pFiler, int groupCodeOffset = 0);
  Gcad::ErrorStatus dxfOut(GcDbDxfFiler* pFiler, int groupCodeOffset = 0) const;

  Gcad::ErrorStatus audit(GcDbAuditInfo* pAuditInfo);

  Gcad::ErrorStatus serializeOut(void* pBuff, int* pBuflen, GcDb::GcDbDwgVersion ver = GcDb::kDHL_CURRENT) const;

  Gcad::ErrorStatus serializeIn(const void* pBuff, size_t nDataSizeIn, int* pDataSizeOut = nullptr);
  Gcad::ErrorStatus serializeIn(const void* pBuff, int* pBuflen);

  static const Gsoft::UInt16 MaxColorIndex;

  Gcad::ErrorStatus dwgInAsTrueColor(GcDbDwgFiler* pInputFiler);
  Gcad::ErrorStatus dwgOutAsTrueColor(GcDbDwgFiler* pOutputFiler) const;

private:
  enum NameFlags
  {
    kNoNames = 0x00,
    kHasColorName = 0x01,
    kHasBookName = 0x02
  };

  static const Gsoft::UInt16 ComplexColorFlag;

  GcCmEntityColor mColor;
  GcString msColorName;
  GcString msBookName;
  Gsoft::UInt8 mNameFlags = 0;

public:
  Gcad::ErrorStatus setColorMethod(GcCmEntityColor::ColorMethod eColorMethod) override;
  Gsoft::UInt32 color() const override;
  Gcad::ErrorStatus setColor(Gsoft::UInt32 color) override;
  Gcad::ErrorStatus setRed(Gsoft::UInt8 red) override;
  Gcad::ErrorStatus setGreen(Gsoft::UInt8 green) override;
  Gcad::ErrorStatus setBlue(Gsoft::UInt8 blue) override;
};

inline Gcad::ErrorStatus GcCmColor::setColorMethod(GcCmEntityColor::ColorMethod eColorMethod)
{
  return mColor.setColorMethod(eColorMethod);
}

inline Gsoft::UInt32 GcCmColor::color() const
{
  return mColor.color();
}

inline Gcad::ErrorStatus GcCmColor::setColor(Gsoft::UInt32 color)
{
  return mColor.setColor(color);
}

inline Gcad::ErrorStatus GcCmColor::setRed(Gsoft::UInt8 red)
{
  if (colorMethod() == GcCmEntityColor::kByColor)
  {
    return mColor.setRed(red);
  }
  return Gcad::eNotApplicable;
}

inline Gcad::ErrorStatus GcCmColor::setGreen(Gsoft::UInt8 green)
{
  if (colorMethod() == GcCmEntityColor::kByColor)
  {
    return mColor.setGreen(green);
  }
  return Gcad::eNotApplicable;
}

inline Gcad::ErrorStatus GcCmColor::setBlue(Gsoft::UInt8 blue)
{
  if (colorMethod() == GcCmEntityColor::kByColor)
  {
    return mColor.setBlue(blue);
  }
  return Gcad::eNotApplicable;
}

#ifdef GcCmColor_Deprecated_GCHAR_Methods
inline Gcad::ErrorStatus GcCmColor::getDescription(GCHAR*& desc) const
{
  return ::gcutGetGcStringConvertToGChar<GcCmColor>(this, &GcCmColor::getDescription, desc);
}

inline Gcad::ErrorStatus GcCmColor::getExplanation(GCHAR*& expl) const
{
  return ::gcutGetGcStringConvertToGChar<GcCmColor>(this, &GcCmColor::getExplanation, expl);
}

inline Gcad::ErrorStatus GcCmColor::getDictionaryKey(GCHAR* pKey, size_t nLen) const
{
  if (nLen <= 0 || pKey == nullptr)
  {
    return Gcad::eInvalidInput;
  }
  GcString sRet;
  const Gcad::ErrorStatus es = this->getDictionaryKey(sRet);
  if (es != Gcad::eOk)
  {
    pKey[0] = 0;
    return es;
  }
  for (size_t i = 0;; i++)
  {
    if (i == nLen)
    {
      pKey[i - 1] = 0;
      return Gcad::eBufferTooSmall;
    }
    pKey[i] = sRet[i];
    if (sRet[i] == 0)
    {
      return Gcad::eOk;
    }
  }
}

#endif
