#ifndef __OBJECT_COLOR_H__
#define __OBJECT_COLOR_H__

#include "Object.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Color
//-----------------------------------------------------------------------------
class DLLDECLARE Color {
private:
	unsigned long _num;
public:
	static const Color Null;
public:
	inline Color() : _num(0x00000000) {}
	inline Color(unsigned long num) : _num(num) {}
	inline Color(const Color &color) : _num(color._num) {}
	inline Color(unsigned char red, unsigned char green, unsigned char blue) :
		_num((static_cast<unsigned long>(red) << 16) +
			 (static_cast<unsigned long>(green) << 8) + blue) {}
	inline bool IsValid() const { return _num < 0x1000000; }
	inline bool IsInvalid() const { return !IsValid(); }
	inline unsigned long GetULong() const { return _num; }
	inline unsigned char GetRed() const {
		return static_cast<unsigned char>(_num >> 16);
	}
	inline unsigned char GetGreen() const {
		return static_cast<unsigned char>(_num >> 8);
	}
	inline unsigned char GetBlue() const {
		return static_cast<unsigned char>(_num >> 0);
	}
	inline unsigned char GetGray() const {
		return CalcGray(GetRed(), GetGreen(), GetBlue());
	}
	inline void SetRed(unsigned char red) {
		_num = _num & ~(0xff << 16) | (static_cast<unsigned long>(red) << 16);
	}
	inline void SetGreen(unsigned char green) {
		_num = _num & ~(0xff << 8) | (static_cast<unsigned long>(green) << 8);
	}
	inline void SetBlue(unsigned char blue) {
		_num = _num & ~(0xff << 0) | (static_cast<unsigned long>(blue) << 0);
	}
	inline bool operator<(const Color &c) const { return GetULong() < c.GetULong(); }
	inline size_t CalcDist(unsigned char red, unsigned char green, unsigned char blue) const {
		return CalcDistRGB(GetRed(), GetGreen(), GetBlue(), red, green, blue);
	}
	inline size_t CalcDist(const Color &c) const {
		return CalcDistRGB(GetRed(), GetGreen(), GetBlue(), c.GetRed(), c.GetGreen(), c.GetBlue());
	}
	inline String GetHTML() const {
		char buff[32];
		::sprintf(buff, "#%06x", static_cast<unsigned int>(_num));
		return String(buff);
	}
	static inline size_t CalcDistRGB(
				unsigned char red1, unsigned char green1, unsigned char blue1,
				unsigned char red2, unsigned char green2, unsigned char blue2) {
		size_t dist = (red1 > red2)? red1 - red2 : red2 - red1;
		dist += (green1 > green2)? green1 - green2 : green2 - green1;
		dist += (blue1 > blue2)? blue1 - blue2 : blue2 - blue1;
		return dist;
	}
	// revise this equation to convert a color into gray scale.
	static inline unsigned char CalcGray(unsigned char red, unsigned char green, unsigned char blue) {
		return static_cast<unsigned char>(
			(static_cast<unsigned short>(red) +
			 static_cast<unsigned short>(green) +
			 static_cast<unsigned short>(blue)) / 3);
	}
};

typedef std::set<Color> ColorSet;
typedef std::map<const Symbol *, Color> ColorMap;

//-----------------------------------------------------------------------------
// Class_Color
//-----------------------------------------------------------------------------
class DLLDECLARE Class_Color : public Class {
public:
	Class_Color(Environment *pEnvOuter);
	virtual bool CastFrom(Environment &env, Signal sig, Value &value);
	virtual Object *CreateDescendant(Environment &env, Signal sig, Class *pClass);
};

//-----------------------------------------------------------------------------
// Object_Color
//-----------------------------------------------------------------------------
class DLLDECLARE Object_Color : public Object {
public:
	struct ElementEntry {
		const char *name;
		unsigned char red, green, blue;
	};
public:
	AScript_DeclareObjectAccessor(Color)
private:
	Color _color;
	unsigned char _alpha;
	static const ElementEntry _elemEntries[];
	static ColorMap *_pColorMap;
public:
	inline Object_Color(const Object_Color &obj) : Object(obj),
					_color(obj._color), _alpha(obj._alpha) {}
	inline Object_Color(Environment &env, unsigned char red, unsigned char green,
									unsigned char blue, unsigned char alpha) :
					Object(env.LookupClass(VTYPE_Color)),
					_color(red, green, blue), _alpha(alpha) {}
	inline Object_Color(Class *pClass) : Object(pClass), _alpha(0) {}
	inline void Set(unsigned char red, unsigned char green,
									unsigned char blue, unsigned char alpha) {
		_color = Color(red, green, blue), _alpha = alpha;
	}
	inline Color &GetColor() { return _color; }
	inline const Color &GetColor() const { return _color; }
	inline unsigned char GetRed() const { return _color.GetRed(); }
	inline unsigned char GetGreen() const { return _color.GetGreen(); }
	inline unsigned char GetBlue() const { return _color.GetBlue(); }
	inline unsigned char GetAlpha() const { return _alpha; }
	inline void SetRed(unsigned char red) { _color.SetRed(red); }
	inline void SetGreen(unsigned char green) { _color.SetGreen(green); }
	inline void SetBlue(unsigned char blue) { _color.SetBlue(blue); }
	inline void SetAlpha(unsigned char alpha) { _alpha = alpha; }
	virtual ~Object_Color();
	virtual Object *Clone() const;
	virtual Value DoPropGet(Signal sig,
				const Symbol *pSymbol, bool &evaluatedFlag);
	virtual Value DoPropSet(Signal sig,
				const Symbol *pSymbol, const Value &value, bool &evaluatedFlag);
	virtual String ToString(Signal sig, bool exprFlag);
	String GetHTML() const;
	inline size_t CalcDist(unsigned char red, unsigned char green, unsigned char blue) const {
		return _color.CalcDist(red, green, blue);
	}
	inline size_t CalcDist(const Object_Color *pObjColor) const {
		return _color.CalcDist(pObjColor->GetRed(), pObjColor->GetGreen(), pObjColor->GetBlue());
	}
	static Object_Color *CreateNamedColor(Environment &env,
						Signal sig, const char *name, unsigned char alpha);
};

}

#endif
