//
// Object_Palette
//

#include "ascript/Object_Color.h"
#include "ascript/Object_Palette.h"
#include "ascript/Object_Image.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Palette
//-----------------------------------------------------------------------------
Object_Palette::Object_Palette(Environment &env, size_t nEntries) :
			Object(env.LookupClass(VTYPE_Palette)), _buff(NULL), _nEntries(0)
{
	AllocBuff(nEntries);
}

Object_Palette::Object_Palette(const Object_Palette &obj) : Object(obj),
			_nEntries(obj._nEntries), _buff(NULL), _memory(obj._memory)
{
	_buff = reinterpret_cast<unsigned char *>(_memory.GetPointer());
}

Object_Palette::~Object_Palette()
{
}

Object *Object_Palette::Clone() const
{
	return new Object_Palette(*this);
}

Iterator *Object_Palette::CreateIterator(Signal sig)
{
	return new IteratorEach(Object_Palette::Reference(this));
}

Value Object_Palette::IndexGet(Signal sig, const Value &valueIdx)
{
	if (!valueIdx.IsNumber()) {
		sig.SetError(ERR_IndexError, "index must be a number");
		return Value::Null;
	}
	size_t idx = valueIdx.GetSizeT();
	if (idx >= CountEntries()) {
		sig.SetError(ERR_IndexError, "index is out of range");
		return Value::Null;
	}
	return GetColorValue(idx);
}

void Object_Palette::IndexSet(Signal sig, const Value &valueIdx, const Value &value)
{
	Environment &env = *this;
	if (!valueIdx.IsNumber()) {
		sig.SetError(ERR_IndexError, "index must be a number");
		return;
	}
	size_t idx = valueIdx.GetSizeT();
	if (idx >= CountEntries()) {
		sig.SetError(ERR_IndexError, "index is out of range");
		return;
	}
	if (value.IsColor()) {
		SetColorObj(idx, value.GetColorObj());
	} else {
		Value valueCasted;
		if (!env.LookupClass(VTYPE_Color)->CastFrom(env, sig, valueCasted)) {
			sig.SetError(ERR_ValueError, "color must be specified");
			return;
		}
		SetColorObj(idx, valueCasted.GetColorObj());
	}
}

void Object_Palette::AllocBuff(size_t nEntries)
{
	_buff = NULL;
	_memory.Free();
	_nEntries = nEntries;
	if (_nEntries > 0) {
		_buff = reinterpret_cast<unsigned char *>(_memory.Allocate(GetBuffSize()));
		::memset(_buff, 0x00, GetBuffSize());
		for (size_t idx = 0; idx < nEntries; idx++) {
			GetEntry(idx)[OffsetAlpha] = 0xff;
		}
	}
}

void Object_Palette::ResizeBuff(size_t nEntries, size_t nEntriesToCopy)
{
	_nEntries = nEntries;
	size_t bytesToCopy = CalcBuffSize(nEntriesToCopy);
	_buff = reinterpret_cast<unsigned char *>(
								_memory.Resize(GetBuffSize(), bytesToCopy));
	::memset(_buff + bytesToCopy, 0x00, GetBuffSize() - bytesToCopy);
	for (size_t idx = nEntriesToCopy; idx < nEntries; idx++) {
		GetEntry(idx)[OffsetAlpha] = 0xff;
	}
}

bool Object_Palette::Prepare(Signal sig, const Symbol *pSymbol)
{
	size_t nEntries = 0;
	const unsigned char *rgb = NULL;
	if (pSymbol->IsIdentical(AScript_Symbol(mono))) {
		nEntries = 2, rgb = _rgb_Mono;
	} else if (pSymbol->IsIdentical(AScript_Symbol(basic))) {
		nEntries = 16, rgb = _rgb_Basic;
	} else if (pSymbol->IsIdentical(AScript_Symbol(win256))) {
		nEntries = 256, rgb = _rgb_Win256;
	} else if (pSymbol->IsIdentical(AScript_Symbol(websafe))) {
		nEntries = 256, rgb = _rgb_WebSafe;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank2))) {
		nEntries = 2, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank4))) {
		nEntries = 4, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank8))) {
		nEntries = 8, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank16))) {
		nEntries = 16, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank32))) {
		nEntries = 32, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank64))) {
		nEntries = 64, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank128))) {
		nEntries = 128, rgb = NULL;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blank256))) {
		nEntries = 256, rgb = NULL;
	} else {
		sig.SetError(ERR_ValueError, "invalid palette symbol %s", pSymbol->GetName());
		return false;
	}
	AllocBuff(nEntries);
	if (rgb != NULL) {
		for (size_t idx = 0; idx < nEntries; idx++, rgb += 3) {
			SetEntry(idx, rgb[0], rgb[1], rgb[2]);
		}
	}
	return true;
}

Value Object_Palette::GetColorValue(size_t idx)
{
	Environment &env = *this;
	const unsigned char *entry = GetEntry(idx);
	return Value(new Object_Color(env,
		entry[OffsetRed], entry[OffsetGreen], entry[OffsetBlue], entry[OffsetAlpha]));
}

void Object_Palette::SetColorObj(size_t idx, const Object_Color *pObjColor)
{
	SetEntry(idx, pObjColor->GetRed(), pObjColor->GetGreen(),
								pObjColor->GetBlue(), pObjColor->GetAlpha());
}

size_t Object_Palette::LookupNearest(unsigned char red, unsigned char green, unsigned char blue) const
{
	size_t idxMin = 0;
	const unsigned char *entry = _buff;
	size_t distMin = Color::CalcDistRGB(red, green, blue,
						entry[OffsetRed], entry[OffsetGreen], entry[OffsetBlue]);
	if (distMin == 0) return idxMin;
	entry += 4;
	for (size_t idx = 1; idx < _nEntries; idx++, entry += 4) {
		size_t dist = Color::CalcDistRGB(red, green, blue,
						entry[OffsetRed], entry[OffsetGreen], entry[OffsetBlue]);
		if (distMin > dist) {
			if (dist == 0) return idx;
			idxMin = idx, distMin = dist;
		}
	}
	return idxMin;
}

bool Object_Palette::UpdateByImage(const Object_Image *pObjImage, bool shrinkFlag)
{
	ColorSet colorSet;
	size_t idxBlank = NextBlankIndex(colorSet);
	size_t bytesPerPixel = pObjImage->GetBytesPerPixel();
	size_t bytesPerLine = pObjImage->GetBytesPerLine();
	const unsigned char *pLine = pObjImage->GetPointer(0);
	for (size_t y = 0; y < pObjImage->GetHeight(); y++) {
		const unsigned char *pPixel = pLine;
		for (size_t x = 0; x < pObjImage->GetWidth(); x++) {
			Color color(pPixel[OffsetRed], pPixel[OffsetGreen], pPixel[OffsetBlue]);
			std::pair<ColorSet::iterator, bool> rtn = colorSet.insert(color);
			if (!rtn.second) {
				// nothing to do
			} else if (idxBlank < _nEntries) {
				SetEntry(idxBlank, color);
				idxBlank++;
			} else {
				return false;
			}
			pPixel += bytesPerPixel;
		}
		pLine += bytesPerLine;
	}
	if (shrinkFlag) {
		size_t nEntries = 1;
		for ( ; idxBlank > nEntries; nEntries <<= 1) ;
		if (nEntries < _nEntries) ResizeBuff(nEntries, idxBlank);
	}
	return true;
}

bool Object_Palette::UpdateByImage(Signal sig,
								const Object_Image *pObjImage, bool shrinkFlag)
{
	if (UpdateByImage(pObjImage, shrinkFlag)) return true;
	sig.SetError(ERR_ValueError,
				"number of palette elements exceeds maximum %d", _nEntries);
	return false;
}

bool Object_Palette::UpdateByPalette(const Object_Palette *pObjPalette, bool shrinkFlag)
{
	ColorSet colorSet;
	size_t idxBlank = NextBlankIndex(colorSet);
	for (size_t idx = 0; idx < pObjPalette->CountEntries(); idx++) {
		const unsigned char *pEntry = pObjPalette->GetEntry(idx);
		Color color(pEntry[OffsetRed], pEntry[OffsetGreen], pEntry[OffsetBlue]);
		std::pair<ColorSet::iterator, bool> rtn = colorSet.insert(color);
		if (!rtn.second) {
		} else if (idxBlank < _nEntries) {
			SetEntry(idxBlank, color);
			idxBlank++;
		} else {
			return false;
		}
	}
	if (shrinkFlag) {
		size_t nEntries = 1;
		for ( ; idxBlank > nEntries; nEntries <<= 1) ;
		if (nEntries < _nEntries) ResizeBuff(nEntries, idxBlank);
	}
	return true;
}

bool Object_Palette::UpdateByPalette(Signal sig,
								const Object_Palette *pObjPalette, bool shrinkFlag)
{
	if (UpdateByPalette(pObjPalette, shrinkFlag)) return true;
	sig.SetError(ERR_ValueError,
				"number of palette elements exceeds maximum %d", _nEntries);
	return false;
}

void Object_Palette::Shrink(size_t nEntries)
{
	ColorSet colorSet;
	if (nEntries == 0) {
		nEntries = NextBlankIndex(colorSet);
	}
	size_t nEntriesAligned = 1;
	for ( ; nEntries > nEntriesAligned; nEntriesAligned <<= 1) ;
	if (nEntriesAligned < _nEntries) ResizeBuff(nEntriesAligned, nEntries);
}

size_t Object_Palette::NextBlankIndex() const
{
	ColorSet colorSet;
	return NextBlankIndex(colorSet);
}

size_t Object_Palette::NextBlankIndex(ColorSet &colorSet) const
{
	size_t idxMax = 0;
	for (size_t idx = 0; idx < _nEntries; idx++) {
		const unsigned char *pEntry = GetEntry(idx);
		Color color(pEntry[OffsetRed], pEntry[OffsetGreen], pEntry[OffsetBlue]);
		std::pair<ColorSet::iterator, bool> rtn = colorSet.insert(color);
		if (rtn.second) idxMax = idx;
	}
	return idxMax + 1;
}

String Object_Palette::ToString(Signal sig, bool exprFlag)
{
	String str;
	str += "<Palette:";
	do {
		char buff[64];
		::sprintf(buff, "%dentries", _nEntries);
		str += buff;
	} while (0);
	str += ">";
	return str;
}

const unsigned char Object_Palette::_rgb_Mono[] = {
	0x00, 0x00, 0x00,	// 0: black
	0xff, 0xff, 0xff,	// 15: white
};

const unsigned char Object_Palette::_rgb_Basic[] = {
	0x00, 0x00, 0x00,	// 0: black
	0x80, 0x00, 0x00,	// 1: maroon
	0x00, 0x80, 0x00,	// 2: green
	0x80, 0x80, 0x00,	// 3: olive
	0x00, 0x00, 0x80,	// 4: navy
	0x80, 0x00, 0x80,	// 5: purple
	0x00, 0x80, 0x80,	// 6: teal
	0x80, 0x80, 0x80,	// 7: gray
	0xc0, 0xc0, 0xc0,	// 8: silver
	0xff, 0x00, 0x00,	// 9: red
	0x00, 0xff, 0x00,	// 10: lime
	0xff, 0xff, 0x00,	// 11: yellow
	0x00, 0x00, 0xff,	// 12: blue
	0xff, 0x00, 0xff,	// 13: fuchsia
	0x00, 0xff, 0xff,	// 14: aqua
	0xff, 0xff, 0xff,	// 15: white
};

const unsigned char Object_Palette::_rgb_Win256[] = {
	0x00, 0x00, 0x00,	// 0
	0x80, 0x00, 0x00,	// 1
	0x00, 0x80, 0x00,	// 2
	0x80, 0x80, 0x00,	// 3
	0x00, 0x00, 0x80,	// 4
	0x80, 0x00, 0x80,	// 5
	0x00, 0x80, 0x80,	// 6
	0xc0, 0xc0, 0xc0,	// 7
	0xc0, 0xdc, 0xc0,	// 8
	0xa6, 0xca, 0xf0,	// 9
	0x40, 0x20, 0x00,	// 10
	0x60, 0x20, 0x00,	// 11
	0x80, 0x20, 0x00,	// 12
	0xa0, 0x20, 0x00,	// 13
	0xc0, 0x20, 0x00,	// 14
	0xe0, 0x20, 0x00,	// 15
	0x00, 0x40, 0x00,	// 16
	0x20, 0x40, 0x00,	// 17
	0x40, 0x40, 0x00,	// 18
	0x60, 0x40, 0x00,	// 19
	0x80, 0x40, 0x00,	// 20
	0xa0, 0x40, 0x00,	// 21
	0xc0, 0x40, 0x00,	// 22
	0xe0, 0x40, 0x00,	// 23
	0x00, 0x60, 0x00,	// 24
	0x20, 0x60, 0x00,	// 25
	0x40, 0x60, 0x00,	// 26
	0x60, 0x60, 0x00,	// 27
	0x80, 0x60, 0x00,	// 28
	0xa0, 0x60, 0x00,	// 29
	0xc0, 0x60, 0x00,	// 30
	0xe0, 0x60, 0x00,	// 31
	0x00, 0x80, 0x00,	// 32
	0x20, 0x80, 0x00,	// 33
	0x40, 0x80, 0x00,	// 34
	0x60, 0x80, 0x00,	// 35
	0x80, 0x80, 0x00,	// 36
	0xa0, 0x80, 0x00,	// 37
	0xc0, 0x80, 0x00,	// 38
	0xe0, 0x80, 0x00,	// 39
	0x00, 0xa0, 0x00,	// 40
	0x20, 0xa0, 0x00,	// 41
	0x40, 0xa0, 0x00,	// 42
	0x60, 0xa0, 0x00,	// 43
	0x80, 0xa0, 0x00,	// 44
	0xa0, 0xa0, 0x00,	// 45
	0xc0, 0xa0, 0x00,	// 46
	0xe0, 0xa0, 0x00,	// 47
	0x00, 0xc0, 0x00,	// 48
	0x20, 0xc0, 0x00,	// 49
	0x40, 0xc0, 0x00,	// 50
	0x60, 0xc0, 0x00,	// 51
	0x80, 0xc0, 0x00,	// 52
	0xa0, 0xc0, 0x00,	// 53
	0xc0, 0xc0, 0x00,	// 54
	0xe0, 0xc0, 0x00,	// 55
	0x00, 0xe0, 0x00,	// 56
	0x20, 0xe0, 0x00,	// 57
	0x40, 0xe0, 0x00,	// 58
	0x60, 0xe0, 0x00,	// 59
	0x80, 0xe0, 0x00,	// 60
	0xa0, 0xe0, 0x00,	// 61
	0xc0, 0xe0, 0x00,	// 62
	0xe0, 0xe0, 0x00,	// 63
	0x00, 0x00, 0x40,	// 64
	0x20, 0x00, 0x40,	// 65
	0x40, 0x00, 0x40,	// 66
	0x60, 0x00, 0x40,	// 67
	0x80, 0x00, 0x40,	// 68
	0xa0, 0x00, 0x40,	// 69
	0xc0, 0x00, 0x40,	// 70
	0xe0, 0x00, 0x40,	// 71
	0x00, 0x20, 0x40,	// 72
	0x20, 0x20, 0x40,	// 73
	0x40, 0x20, 0x40,	// 74
	0x60, 0x20, 0x40,	// 75
	0x80, 0x20, 0x40,	// 76
	0xa0, 0x20, 0x40,	// 77
	0xc0, 0x20, 0x40,	// 78
	0xe0, 0x20, 0x40,	// 79
	0x00, 0x40, 0x40,	// 80
	0x20, 0x40, 0x40,	// 81
	0x40, 0x40, 0x40,	// 82
	0x60, 0x40, 0x40,	// 83
	0x80, 0x40, 0x40,	// 84
	0xa0, 0x40, 0x40,	// 85
	0xc0, 0x40, 0x40,	// 86
	0xe0, 0x40, 0x40,	// 87
	0x00, 0x60, 0x40,	// 88
	0x20, 0x60, 0x40,	// 89
	0x40, 0x60, 0x40,	// 90
	0x60, 0x60, 0x40,	// 91
	0x80, 0x60, 0x40,	// 92
	0xa0, 0x60, 0x40,	// 93
	0xc0, 0x60, 0x40,	// 94
	0xe0, 0x60, 0x40,	// 95
	0x00, 0x80, 0x40,	// 96
	0x20, 0x80, 0x40,	// 97
	0x40, 0x80, 0x40,	// 98
	0x60, 0x80, 0x40,	// 99
	0x80, 0x80, 0x40,	// 100
	0xa0, 0x80, 0x40,	// 101
	0xc0, 0x80, 0x40,	// 102
	0xe0, 0x80, 0x40,	// 103
	0x00, 0xa0, 0x40,	// 104
	0x20, 0xa0, 0x40,	// 105
	0x40, 0xa0, 0x40,	// 106
	0x60, 0xa0, 0x40,	// 107
	0x80, 0xa0, 0x40,	// 108
	0xa0, 0xa0, 0x40,	// 109
	0xc0, 0xa0, 0x40,	// 110
	0xe0, 0xa0, 0x40,	// 111
	0x00, 0xc0, 0x40,	// 112
	0x20, 0xc0, 0x40,	// 113
	0x40, 0xc0, 0x40,	// 114
	0x60, 0xc0, 0x40,	// 115
	0x80, 0xc0, 0x40,	// 116
	0xa0, 0xc0, 0x40,	// 117
	0xc0, 0xc0, 0x40,	// 118
	0xe0, 0xc0, 0x40,	// 119
	0x00, 0xe0, 0x40,	// 120
	0x20, 0xe0, 0x40,	// 121
	0x40, 0xe0, 0x40,	// 122
	0x60, 0xe0, 0x40,	// 123
	0x80, 0xe0, 0x40,	// 124
	0xa0, 0xe0, 0x40,	// 125
	0xc0, 0xe0, 0x40,	// 126
	0xe0, 0xe0, 0x40,	// 127
	0x00, 0x00, 0x80,	// 128
	0x20, 0x00, 0x80,	// 129
	0x40, 0x00, 0x80,	// 130
	0x60, 0x00, 0x80,	// 131
	0x80, 0x00, 0x80,	// 132
	0xa0, 0x00, 0x80,	// 133
	0xc0, 0x00, 0x80,	// 134
	0xe0, 0x00, 0x80,	// 135
	0x00, 0x20, 0x80,	// 136
	0x20, 0x20, 0x80,	// 137
	0x40, 0x20, 0x80,	// 138
	0x60, 0x20, 0x80,	// 139
	0x80, 0x20, 0x80,	// 140
	0xa0, 0x20, 0x80,	// 141
	0xc0, 0x20, 0x80,	// 142
	0xe0, 0x20, 0x80,	// 143
	0x00, 0x40, 0x80,	// 144
	0x20, 0x40, 0x80,	// 145
	0x40, 0x40, 0x80,	// 146
	0x60, 0x40, 0x80,	// 147
	0x80, 0x40, 0x80,	// 148
	0xa0, 0x40, 0x80,	// 149
	0xc0, 0x40, 0x80,	// 150
	0xe0, 0x40, 0x80,	// 151
	0x00, 0x60, 0x80,	// 152
	0x20, 0x60, 0x80,	// 153
	0x40, 0x60, 0x80,	// 154
	0x60, 0x60, 0x80,	// 155
	0x80, 0x60, 0x80,	// 156
	0xa0, 0x60, 0x80,	// 157
	0xc0, 0x60, 0x80,	// 158
	0xe0, 0x60, 0x80,	// 159
	0x00, 0x80, 0x80,	// 160
	0x20, 0x80, 0x80,	// 161
	0x40, 0x80, 0x80,	// 162
	0x60, 0x80, 0x80,	// 163
	0x80, 0x80, 0x80,	// 164
	0xa0, 0x80, 0x80,	// 165
	0xc0, 0x80, 0x80,	// 166
	0xe0, 0x80, 0x80,	// 167
	0x00, 0xa0, 0x80,	// 168
	0x20, 0xa0, 0x80,	// 169
	0x40, 0xa0, 0x80,	// 170
	0x60, 0xa0, 0x80,	// 171
	0x80, 0xa0, 0x80,	// 172
	0xa0, 0xa0, 0x80,	// 173
	0xc0, 0xa0, 0x80,	// 174
	0xe0, 0xa0, 0x80,	// 175
	0x00, 0xc0, 0x80,	// 176
	0x20, 0xc0, 0x80,	// 177
	0x40, 0xc0, 0x80,	// 178
	0x60, 0xc0, 0x80,	// 179
	0x80, 0xc0, 0x80,	// 180
	0xa0, 0xc0, 0x80,	// 181
	0xc0, 0xc0, 0x80,	// 182
	0xe0, 0xc0, 0x80,	// 183
	0x00, 0xe0, 0x80,	// 184
	0x20, 0xe0, 0x80,	// 185
	0x40, 0xe0, 0x80,	// 186
	0x60, 0xe0, 0x80,	// 187
	0x80, 0xe0, 0x80,	// 188
	0xa0, 0xe0, 0x80,	// 189
	0xc0, 0xe0, 0x80,	// 190
	0xe0, 0xe0, 0x80,	// 191
	0x00, 0x00, 0xc0,	// 192
	0x20, 0x00, 0xc0,	// 193
	0x40, 0x00, 0xc0,	// 194
	0x60, 0x00, 0xc0,	// 195
	0x80, 0x00, 0xc0,	// 196
	0xa0, 0x00, 0xc0,	// 197
	0xc0, 0x00, 0xc0,	// 198
	0xe0, 0x00, 0xc0,	// 199
	0x00, 0x20, 0xc0,	// 200
	0x20, 0x20, 0xc0,	// 201
	0x40, 0x20, 0xc0,	// 202
	0x60, 0x20, 0xc0,	// 203
	0x80, 0x20, 0xc0,	// 204
	0xa0, 0x20, 0xc0,	// 205
	0xc0, 0x20, 0xc0,	// 206
	0xe0, 0x20, 0xc0,	// 207
	0x00, 0x40, 0xc0,	// 208
	0x20, 0x40, 0xc0,	// 209
	0x40, 0x40, 0xc0,	// 210
	0x60, 0x40, 0xc0,	// 211
	0x80, 0x40, 0xc0,	// 212
	0xa0, 0x40, 0xc0,	// 213
	0xc0, 0x40, 0xc0,	// 214
	0xe0, 0x40, 0xc0,	// 215
	0x00, 0x60, 0xc0,	// 216
	0x20, 0x60, 0xc0,	// 217
	0x40, 0x60, 0xc0,	// 218
	0x60, 0x60, 0xc0,	// 219
	0x80, 0x60, 0xc0,	// 220
	0xa0, 0x60, 0xc0,	// 221
	0xc0, 0x60, 0xc0,	// 222
	0xe0, 0x60, 0xc0,	// 223
	0x00, 0x80, 0xc0,	// 224
	0x20, 0x80, 0xc0,	// 225
	0x40, 0x80, 0xc0,	// 226
	0x60, 0x80, 0xc0,	// 227
	0x80, 0x80, 0xc0,	// 228
	0xa0, 0x80, 0xc0,	// 229
	0xc0, 0x80, 0xc0,	// 230
	0xe0, 0x80, 0xc0,	// 231
	0x00, 0xa0, 0xc0,	// 232
	0x20, 0xa0, 0xc0,	// 233
	0x40, 0xa0, 0xc0,	// 234
	0x60, 0xa0, 0xc0,	// 235
	0x80, 0xa0, 0xc0,	// 236
	0xa0, 0xa0, 0xc0,	// 237
	0xc0, 0xa0, 0xc0,	// 238
	0xe0, 0xa0, 0xc0,	// 239
	0x00, 0xc0, 0xc0,	// 240
	0x20, 0xc0, 0xc0,	// 241
	0x40, 0xc0, 0xc0,	// 242
	0x60, 0xc0, 0xc0,	// 243
	0x80, 0xc0, 0xc0,	// 244
	0xa0, 0xc0, 0xc0,	// 245
	0xff, 0xfb, 0xf0,	// 246
	0xa0, 0xa0, 0xa4,	// 247
	0x80, 0x80, 0x80,	// 248
	0xff, 0x00, 0x00,	// 249
	0x00, 0xff, 0x00,	// 250
	0xff, 0xff, 0x00,	// 251
	0x00, 0x00, 0xff,	// 252
	0xff, 0x00, 0xff,	// 253
	0x00, 0xff, 0xff,	// 254
	0xff, 0xff, 0xff,	// 255
};

const unsigned char Object_Palette::_rgb_WebSafe[] = {
	0x00, 0x00, 0x00,	// 0
	0x33, 0x00, 0x00,	// 1
	0x66, 0x00, 0x00,	// 2
	0x99, 0x00, 0x00,	// 3
	0xcc, 0x00, 0x00,	// 4
	0xff, 0x00, 0x00,	// 5
	0x00, 0x33, 0x00,	// 6
	0x33, 0x33, 0x00,	// 7
	0x66, 0x33, 0x00,	// 8
	0x99, 0x33, 0x00,	// 9
	0xcc, 0x33, 0x00,	// 10
	0xff, 0x33, 0x00,	// 11
	0x00, 0x66, 0x00,	// 12
	0x33, 0x66, 0x00,	// 13
	0x66, 0x66, 0x00,	// 14
	0x99, 0x66, 0x00,	// 15
	0xcc, 0x66, 0x00,	// 16
	0xff, 0x66, 0x00,	// 17
	0x00, 0x99, 0x00,	// 18
	0x33, 0x99, 0x00,	// 19
	0x66, 0x99, 0x00,	// 20
	0x99, 0x99, 0x00,	// 21
	0xcc, 0x99, 0x00,	// 22
	0xff, 0x99, 0x00,	// 23
	0x00, 0xcc, 0x00,	// 24
	0x33, 0xcc, 0x00,	// 25
	0x66, 0xcc, 0x00,	// 26
	0x99, 0xcc, 0x00,	// 27
	0xcc, 0xcc, 0x00,	// 28
	0xff, 0xcc, 0x00,	// 29
	0x00, 0xff, 0x00,	// 30
	0x33, 0xff, 0x00,	// 31
	0x66, 0xff, 0x00,	// 32
	0x99, 0xff, 0x00,	// 33
	0xcc, 0xff, 0x00,	// 34
	0xff, 0xff, 0x00,	// 35
	0x00, 0x00, 0x33,	// 36
	0x33, 0x00, 0x33,	// 37
	0x66, 0x00, 0x33,	// 38
	0x99, 0x00, 0x33,	// 39
	0xcc, 0x00, 0x33,	// 40
	0xff, 0x00, 0x33,	// 41
	0x00, 0x33, 0x33,	// 42
	0x33, 0x33, 0x33,	// 43
	0x66, 0x33, 0x33,	// 44
	0x99, 0x33, 0x33,	// 45
	0xcc, 0x33, 0x33,	// 46
	0xff, 0x33, 0x33,	// 47
	0x00, 0x66, 0x33,	// 48
	0x33, 0x66, 0x33,	// 49
	0x66, 0x66, 0x33,	// 50
	0x99, 0x66, 0x33,	// 51
	0xcc, 0x66, 0x33,	// 52
	0xff, 0x66, 0x33,	// 53
	0x00, 0x99, 0x33,	// 54
	0x33, 0x99, 0x33,	// 55
	0x66, 0x99, 0x33,	// 56
	0x99, 0x99, 0x33,	// 57
	0xcc, 0x99, 0x33,	// 58
	0xff, 0x99, 0x33,	// 59
	0x00, 0xcc, 0x33,	// 60
	0x33, 0xcc, 0x33,	// 61
	0x66, 0xcc, 0x33,	// 62
	0x99, 0xcc, 0x33,	// 63
	0xcc, 0xcc, 0x33,	// 64
	0xff, 0xcc, 0x33,	// 65
	0x00, 0xff, 0x33,	// 66
	0x33, 0xff, 0x33,	// 67
	0x66, 0xff, 0x33,	// 68
	0x99, 0xff, 0x33,	// 69
	0xcc, 0xff, 0x33,	// 70
	0xff, 0xff, 0x33,	// 71
	0x00, 0x00, 0x66,	// 72
	0x33, 0x00, 0x66,	// 73
	0x66, 0x00, 0x66,	// 74
	0x99, 0x00, 0x66,	// 75
	0xcc, 0x00, 0x66,	// 76
	0xff, 0x00, 0x66,	// 77
	0x00, 0x33, 0x66,	// 78
	0x33, 0x33, 0x66,	// 79
	0x66, 0x33, 0x66,	// 80
	0x99, 0x33, 0x66,	// 81
	0xcc, 0x33, 0x66,	// 82
	0xff, 0x33, 0x66,	// 83
	0x00, 0x66, 0x66,	// 84
	0x33, 0x66, 0x66,	// 85
	0x66, 0x66, 0x66,	// 86
	0x99, 0x66, 0x66,	// 87
	0xcc, 0x66, 0x66,	// 88
	0xff, 0x66, 0x66,	// 89
	0x00, 0x99, 0x66,	// 90
	0x33, 0x99, 0x66,	// 91
	0x66, 0x99, 0x66,	// 92
	0x99, 0x99, 0x66,	// 93
	0xcc, 0x99, 0x66,	// 94
	0xff, 0x99, 0x66,	// 95
	0x00, 0xcc, 0x66,	// 96
	0x33, 0xcc, 0x66,	// 97
	0x66, 0xcc, 0x66,	// 98
	0x99, 0xcc, 0x66,	// 99
	0xcc, 0xcc, 0x66,	// 100
	0xff, 0xcc, 0x66,	// 101
	0x00, 0xff, 0x66,	// 102
	0x33, 0xff, 0x66,	// 103
	0x66, 0xff, 0x66,	// 104
	0x99, 0xff, 0x66,	// 105
	0xcc, 0xff, 0x66,	// 106
	0xff, 0xff, 0x66,	// 107
	0x00, 0x00, 0x99,	// 108
	0x33, 0x00, 0x99,	// 109
	0x66, 0x00, 0x99,	// 110
	0x99, 0x00, 0x99,	// 111
	0xcc, 0x00, 0x99,	// 112
	0xff, 0x00, 0x99,	// 113
	0x00, 0x33, 0x99,	// 114
	0x33, 0x33, 0x99,	// 115
	0x66, 0x33, 0x99,	// 116
	0x99, 0x33, 0x99,	// 117
	0xcc, 0x33, 0x99,	// 118
	0xff, 0x33, 0x99,	// 119
	0x00, 0x66, 0x99,	// 120
	0x33, 0x66, 0x99,	// 121
	0x66, 0x66, 0x99,	// 122
	0x99, 0x66, 0x99,	// 123
	0xcc, 0x66, 0x99,	// 124
	0xff, 0x66, 0x99,	// 125
	0x00, 0x99, 0x99,	// 126
	0x33, 0x99, 0x99,	// 127
	0x66, 0x99, 0x99,	// 128
	0x99, 0x99, 0x99,	// 129
	0xcc, 0x99, 0x99,	// 130
	0xff, 0x99, 0x99,	// 131
	0x00, 0xcc, 0x99,	// 132
	0x33, 0xcc, 0x99,	// 133
	0x66, 0xcc, 0x99,	// 134
	0x99, 0xcc, 0x99,	// 135
	0xcc, 0xcc, 0x99,	// 136
	0xff, 0xcc, 0x99,	// 137
	0x00, 0xff, 0x99,	// 138
	0x33, 0xff, 0x99,	// 139
	0x66, 0xff, 0x99,	// 140
	0x99, 0xff, 0x99,	// 141
	0xcc, 0xff, 0x99,	// 142
	0xff, 0xff, 0x99,	// 143
	0x00, 0x00, 0xcc,	// 144
	0x33, 0x00, 0xcc,	// 145
	0x66, 0x00, 0xcc,	// 146
	0x99, 0x00, 0xcc,	// 147
	0xcc, 0x00, 0xcc,	// 148
	0xff, 0x00, 0xcc,	// 149
	0x00, 0x33, 0xcc,	// 150
	0x33, 0x33, 0xcc,	// 151
	0x66, 0x33, 0xcc,	// 152
	0x99, 0x33, 0xcc,	// 153
	0xcc, 0x33, 0xcc,	// 154
	0xff, 0x33, 0xcc,	// 155
	0x00, 0x66, 0xcc,	// 156
	0x33, 0x66, 0xcc,	// 157
	0x66, 0x66, 0xcc,	// 158
	0x99, 0x66, 0xcc,	// 159
	0xcc, 0x66, 0xcc,	// 160
	0xff, 0x66, 0xcc,	// 161
	0x00, 0x99, 0xcc,	// 162
	0x33, 0x99, 0xcc,	// 163
	0x66, 0x99, 0xcc,	// 164
	0x99, 0x99, 0xcc,	// 165
	0xcc, 0x99, 0xcc,	// 166
	0xff, 0x99, 0xcc,	// 167
	0x00, 0xcc, 0xcc,	// 168
	0x33, 0xcc, 0xcc,	// 169
	0x66, 0xcc, 0xcc,	// 170
	0x99, 0xcc, 0xcc,	// 171
	0xcc, 0xcc, 0xcc,	// 172
	0xff, 0xcc, 0xcc,	// 173
	0x00, 0xff, 0xcc,	// 174
	0x33, 0xff, 0xcc,	// 175
	0x66, 0xff, 0xcc,	// 176
	0x99, 0xff, 0xcc,	// 177
	0xcc, 0xff, 0xcc,	// 178
	0xff, 0xff, 0xcc,	// 179
	0x00, 0x00, 0xff,	// 180
	0x33, 0x00, 0xff,	// 181
	0x66, 0x00, 0xff,	// 182
	0x99, 0x00, 0xff,	// 183
	0xcc, 0x00, 0xff,	// 184
	0xff, 0x00, 0xff,	// 185
	0x00, 0x33, 0xff,	// 186
	0x33, 0x33, 0xff,	// 187
	0x66, 0x33, 0xff,	// 188
	0x99, 0x33, 0xff,	// 189
	0xcc, 0x33, 0xff,	// 190
	0xff, 0x33, 0xff,	// 191
	0x00, 0x66, 0xff,	// 192
	0x33, 0x66, 0xff,	// 193
	0x66, 0x66, 0xff,	// 194
	0x99, 0x66, 0xff,	// 195
	0xcc, 0x66, 0xff,	// 196
	0xff, 0x66, 0xff,	// 197
	0x00, 0x99, 0xff,	// 198
	0x33, 0x99, 0xff,	// 199
	0x66, 0x99, 0xff,	// 200
	0x99, 0x99, 0xff,	// 201
	0xcc, 0x99, 0xff,	// 202
	0xff, 0x99, 0xff,	// 203
	0x00, 0xcc, 0xff,	// 204
	0x33, 0xcc, 0xff,	// 205
	0x66, 0xcc, 0xff,	// 206
	0x99, 0xcc, 0xff,	// 207
	0xcc, 0xcc, 0xff,	// 208
	0xff, 0xcc, 0xff,	// 209
	0x00, 0xff, 0xff,	// 210
	0x33, 0xff, 0xff,	// 211
	0x66, 0xff, 0xff,	// 212
	0x99, 0xff, 0xff,	// 213
	0xcc, 0xff, 0xff,	// 214
	0xff, 0xff, 0xff,	// 215
	0x00, 0x00, 0x00,	// 216
	0x00, 0x00, 0x00,	// 217
	0x00, 0x00, 0x00,	// 218
	0x00, 0x00, 0x00,	// 219
	0x00, 0x00, 0x00,	// 220
	0x00, 0x00, 0x00,	// 221
	0x00, 0x00, 0x00,	// 222
	0x00, 0x00, 0x00,	// 223
	0x00, 0x00, 0x00,	// 224
	0x00, 0x00, 0x00,	// 225
	0x00, 0x00, 0x00,	// 226
	0x00, 0x00, 0x00,	// 227
	0x00, 0x00, 0x00,	// 228
	0x00, 0x00, 0x00,	// 229
	0x00, 0x00, 0x00,	// 230
	0x00, 0x00, 0x00,	// 231
	0x00, 0x00, 0x00,	// 232
	0x00, 0x00, 0x00,	// 233
	0x00, 0x00, 0x00,	// 234
	0x00, 0x00, 0x00,	// 235
	0x00, 0x00, 0x00,	// 236
	0x00, 0x00, 0x00,	// 237
	0x00, 0x00, 0x00,	// 238
	0x00, 0x00, 0x00,	// 239
	0x00, 0x00, 0x00,	// 240
	0x00, 0x00, 0x00,	// 241
	0x00, 0x00, 0x00,	// 242
	0x00, 0x00, 0x00,	// 243
	0x00, 0x00, 0x00,	// 244
	0x00, 0x00, 0x00,	// 245
	0x00, 0x00, 0x00,	// 246
	0x00, 0x00, 0x00,	// 247
	0x00, 0x00, 0x00,	// 248
	0x00, 0x00, 0x00,	// 249
	0x00, 0x00, 0x00,	// 250
	0x00, 0x00, 0x00,	// 251
	0x00, 0x00, 0x00,	// 252
	0x00, 0x00, 0x00,	// 253
	0x00, 0x00, 0x00,	// 254
	0x00, 0x00, 0x00,	// 255
};

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Palette
//-----------------------------------------------------------------------------
// palette#each() {block?}
AScript_DeclareMethod(Palette, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that iterates each element in the palette.\n"
	ITERATOR_HELP
	"Block parameter format: |value, idx:number|");
}

AScript_ImplementMethod(Palette, each)
{
	Object_Palette *pSelf = Object_Palette::GetSelfObj(args);
	Object_Palette *pObj = Object_Palette::Reference(pSelf);
	Iterator *pIterator = new Object_Palette::IteratorEach(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// palette#nearest(color:color):map:[index]
AScript_DeclareMethod(Palette, nearest)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "color", VTYPE_Color);
	DeclareAttr(AScript_Symbol(index));
	SetHelp("Returns an index of the nearest entry with the specified color.");
}

AScript_ImplementMethod(Palette, nearest)
{
	Object_Palette *pSelf = Object_Palette::GetSelfObj(args);
	size_t idx = pSelf->LookupNearest(args.GetColorObj(0));
	if (args.IsSet(AScript_Symbol(index))) return Value(static_cast<unsigned int>(idx));
	return pSelf->GetColorValue(idx);
}

// palette#updateby(image_or_palette):reduce:[shrink]
AScript_DeclareMethod(Palette, updateby)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "image_or_palette", VTYPE_Any);
	DeclareAttr(AScript_Symbol(shrink));
	SetHelp(
	"Updates palette entries according to color data in an image or a palette.\n"
	"The order of existing entries will be kept intact. If attribute shrink is\n"
	"specified, the whole size will be shrinked to a number powered by two that\n"
	"is enough to contain unique entries.");
}

AScript_ImplementMethod(Palette, updateby)
{
	Object_Palette *pSelf = Object_Palette::GetSelfObj(args);
	bool shrinkFlag = args.IsSet(AScript_Symbol(shrink));
	if (args.IsImage(0)) {
		if (!pSelf->UpdateByImage(sig, args.GetImageObj(0), shrinkFlag)) {
			return Value::Null;
		}
	} else if (args.IsPalette(0)) {
		if (!pSelf->UpdateByPalette(sig, args.GetPaletteObj(0), shrinkFlag)) {
			return Value::Null;
		}
	} else {
		sig.SetError(ERR_ValueError, "image or palette must be specified");
		return Value::Null;
	}
	return args.GetSelf();
}

// palette#shrink():reduce
AScript_DeclareMethod(Palette, shrink)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	SetHelp(
	"Shrinks the size of the palette to a number powered by two that is\n"
	"enough to contain unique entries. The ordef of existing entries will\n"
	"be kept intact.");
}

AScript_ImplementMethod(Palette, shrink)
{
	Object_Palette *pSelf = Object_Palette::GetSelfObj(args);
	pSelf->Shrink();
	return args.GetSelf();
}

// assignment
Class_Palette::Class_Palette(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Palette, each);
	AScript_AssignMethod(Palette, nearest);
	AScript_AssignMethod(Palette, updateby);
	AScript_AssignMethod(Palette, shrink);
}

bool Class_Palette::CastFrom(Environment &env, Signal sig, Value &value)
{
	if (value.IsSymbol()) {
		Object_Palette *pObjPalette = new Object_Palette(env);
		if (!pObjPalette->Prepare(sig, value.GetSymbol())) {
			Object::Delete(pObjPalette);
			return false;
		}
		value = Value(pObjPalette);
		return true;
	}
	return false;
}

Object *Class_Palette::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	ERROREND(env, "this function must not be called");
	return NULL;
}

//-----------------------------------------------------------------------------
// Object_Palette::IteratorEach
//-----------------------------------------------------------------------------
Object_Palette::IteratorEach::~IteratorEach()
{
	Object::Delete(_pObjPalette);
}

bool Object_Palette::IteratorEach::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_idx >= _pObjPalette->CountEntries()) return false;
	value = _pObjPalette->GetColorValue(_idx);
	_idx++;
	return true;
}

String Object_Palette::IteratorEach::ToString(Signal sig) const
{
	return String("<iterator:palette#each>");
}

}
