/* PaletteEntryPanel::loadEntry
 * Reads all palettes in the PLAYPAL entry and shows the first one
 *******************************************************************/
bool PaletteEntryPanel::loadEntry(ArchiveEntry* entry)
{
	// Clear any existing palettes
	for (size_t a = 0; a < palettes.size(); a++)
		delete palettes[a];
	palettes.clear();

	// Determine how many palettes are in the entry
	int n_palettes = entry->getSize() / 768;

	// Load each palette
	entry->seek(0, SEEK_SET);
	uint8_t pal_data[768];
	for (int a = 0; a < n_palettes; a++)
	{
		// Read palette data
		entry->read(&pal_data, 768);

		// Create palette
		Palette8bit* pal = new Palette8bit();
		pal->loadMem(pal_data, 768);

		// Add palette
		palettes.push_back(pal);
	}

	// Show first palette
	cur_palette = 0;
	showPalette(0);

	setModified(false);

	return true;
}
/* PaletteEntryPanel::move
 * Shifts the current palette's position in the list
 *******************************************************************/
bool PaletteEntryPanel::move(bool infront)
{
	// Avoid invalid moves
	if (palettes.size() == 1)
		return false;
	uint32_t newpos = cur_palette;
	if (infront)
	{
		if (newpos == 0)
			newpos = palettes.size() - 1;
		else --newpos;
	}
	else /* behind*/
	{
		if (newpos + 1 == palettes.size())
			newpos = 0;
		else ++newpos;
	}
	Palette8bit* tmp = palettes[cur_palette];
	palettes[cur_palette] = palettes[newpos];
	palettes[newpos] = tmp;

	// Refresh the display to show the updated amount of palettes
	cur_palette = newpos;
	showPalette(newpos);
	setModified();
	return true;
}
/* PaletteEntryPanel::clearOne
 * Deletes all palettes except the current one from the list
 *******************************************************************/
bool PaletteEntryPanel::clearOthers()
{
	// Nothing to do if there's already only one
	if (palettes.size() == 1)
		return true;

	// Keeping a pointer to the palette we keep
	Palette8bit* pal = palettes[cur_palette];

	// Swap current palette with the first one if needed
	if (cur_palette != 0)
	{
		palettes[cur_palette] = palettes[0];
		palettes[0] = pal;
	}

	// Delete all palettes after the first
	for (size_t i = 1; i < palettes.size(); ++i)
	{
		delete palettes[i];
	}
	// Empty the palette list and add back the single palette
	palettes.clear();
	palettes.push_back(pal);

	// Display the only remaining palette
	cur_palette = 0;
	showPalette(0);
	setModified();
	return true;
}
/* PaletteEntryPanel::generatePalettes
 * Generates the full complement of palettes needed by the game
 *******************************************************************/
bool PaletteEntryPanel::generatePalettes()
{
	GeneratePalettesDialog gpd(theMainWindow);
	if (gpd.ShowModal() == wxID_OK)
	{
		// Get choice
		int choice = gpd.getChoice();
		if (choice == 0) return false;

		// Make sure the current palette is the only one
		clearOthers();

		// The first thirteen palettes are common

		// Generate the eight REDPALS
		for (int a = 1; a < 9; ++a)
			generatePalette(255, 0, 0, a, 9);

		// Then the four BONUSPALS
		for (int a = 1; a < 5; ++a)
			generatePalette(215, 186, 69, a, 8);

		// And here we are at the crossroad
		if (choice == 1)
		{
			// Write the Doom/Heretic/Strife palettes, that is to say:

			// Write RADIATIONPAL with its oversaturated green
			generatePalette(0, 256, 0, 1, 8);

		}
		else
		{
			// Write all the Hexen palettes

			// Starting with the eight POISONPALS
			for (int a = 1; a < 9; ++a)
				generatePalette(44, 92, 36, a, 10);

			// Then the ICEPAL
			generatePalette(0, 0, 224, 1, 2);

			// The three HOLYPALS
			generatePalette(130, 130, 130, 1, 2);
			generatePalette(100, 100, 100, 1, 2);
			generatePalette(70, 70, 70, 1, 2);

			// And lastly the three SCOURGEPAL
			generatePalette(150, 110, 0, 1, 2);
			generatePalette(125, 92, 0, 1, 2);
			generatePalette(100, 73, 0, 1, 2);
		}

		// Refresh view to show changed amount of palettes
		cur_palette = 0;
		showPalette(0);
		setModified();
	}
	return true;
}
/* PaletteEntryPanel::toolbarButtonClick
 * Called when a (EntryPanel) toolbar button is clicked
 *******************************************************************/
void PaletteEntryPanel::toolbarButtonClick(string action_id)
{
	// Prev. palette
	if (action_id == "pal_prev")
	{
		if (cur_palette == 0)
			cur_palette = palettes.size();
		if (showPalette(cur_palette - 1))
			cur_palette--;
	}

	// Next palette
	else if (action_id == "pal_next")
	{
		if (cur_palette + 1 == palettes.size())
			cur_palette = -1;
		if (showPalette(cur_palette + 1))
			cur_palette++;
	}
}
/* PaletteEntryPanel::refreshPanel
 * Redraws the panel
 *******************************************************************/
void PaletteEntryPanel::refreshPanel()
{
	if (entry)
	{
		uint32_t our_palette = cur_palette;
		//loadEntry(entry);
		if (our_palette > 0 && our_palette < palettes.size())
			showPalette(our_palette);
	}
	Update();
	Refresh();
}
/* PaletteEntryPanel::duplicate
 * Make a copy of the current palette and add it to the list
 *******************************************************************/
bool PaletteEntryPanel::duplicate()
{
	Palette8bit* newpalette = new Palette8bit;
	if (!newpalette)
		return false;

	newpalette->copyPalette(palettes[cur_palette]);
	palettes.push_back(newpalette);

	// Refresh the display to show the updated amount of palettes
	showPalette(cur_palette);
	setModified();
	return true;
}
/* PaletteEntryPanel::tweak
 * Tweaks the colours of the current palette
 *******************************************************************/
bool PaletteEntryPanel::tweak()
{
	Palette8bit* pal = new Palette8bit;
	if (pal == NULL) return false;
	pal->copyPalette(palettes[cur_palette]);
	PaletteColourTweakDialog pctd(theMainWindow, pal);
	if (pctd.ShowModal() == wxID_OK)
	{
		palettes[cur_palette]->copyPalette(pctd.getFinalPalette());
		showPalette(cur_palette);
		setModified();
	}
	delete pal;
	return true;
}
PaletteBoxButton::PaletteBoxButton(Palette* p, QWidget* parent)
   : QToolButton(parent)
      {
      palette = p;
      editAction = 0;

      setCheckable(true);
      setFocusPolicy(Qt::NoFocus);
      connect(this, SIGNAL(clicked(bool)), this, SLOT(showPalette(bool)));
      setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
      setText(qApp->translate("Palette", palette->name().toUtf8()));
      setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
      setArrowType(Qt::RightArrow);
      showPalette(false);
      }
/* PaletteEntryPanel::importFrom
 * Imports the selected file in the current palette
 *******************************************************************/
bool PaletteEntryPanel::importFrom()
{
	bool ret = false;

	// Run open file dialog
	SFileDialog::fd_info_t info;
	string extensions = "Raw Palette (*.pal)|*.pal|PNG File (*.png)|*.png|CSV Palette (*.csv)|*.csv|JASC Palette (*.pal)|*.pal|GIMP Palette (*.gpl)|*.gpl";
	if (SFileDialog::openFile(info, "Import Palette As", extensions, this))
	{
		// Load palette
		ret = palettes[cur_palette]->loadFile(info.filenames[0], info.ext_index);
		if (ret)
		{
			setModified();
			showPalette(cur_palette);
		}
	}
	return ret;
}
/* PaletteEntryPanel::clearOne
 * Deletes the current palette from the list
 *******************************************************************/
bool PaletteEntryPanel::clearOne()
{
	// Always keep at least one palette
	if (cur_palette == 0 && palettes.size() == 1)
	{
		wxLogMessage("Palette cannot be removed, no other palette in this entry.");
		return false;
	}

	// Erase palette
	delete palettes[cur_palette];
	palettes.erase(palettes.begin() + cur_palette);

	// Display the next, or previous, palette instead
	if (cur_palette >= palettes.size())
		--cur_palette;
	showPalette(cur_palette);
	setModified();
	return true;
}
void PaletteEntryPanel::analysePalettes()
{
	if (palettes.size() < PALETTECHECK + 1)
		return;
#ifdef GPALCOMPANALYSIS
	int devR, devG, devB;
	int minR, minG, minB;
	int maxR, maxG, maxB;
	int wrongcount;
#else
	unsigned int reds[256];
	unsigned int greens[256];
	unsigned int blues[256];
#endif
	string report = "\n";
#ifdef GPALCOMPANALYSIS
	int i = PALETTECHECK;
	if (i)
	{
		report += S_FMT("Deviation between palettes 0 and %i:\n\n", i);
		devR = devG = devB = 0;
		maxR = maxG = maxB = -1;
		minR = minG = minB = 256;
		wrongcount = 0;
#else
	report += S_FMT("Changes between %u palettes compared to the first:\n\n", palettes.size());
	for (size_t i = 1; i < palettes.size(); ++i)
	{
		for (int j = 0; j < 256; ++j)
			reds[j] = blues[j] = greens[j] = 999;
#endif
		report += S_FMT("\n==============\n= Palette %02u =\n==============\n", i);
		for (size_t c = 0; c < 256; ++c)
		{
			rgba_t ref1 = palettes[0]->colour(c);
			rgba_t cmp1 = palettes[i]->colour(c);
			hsl_t  ref2 = Misc::rgbToHsl(ref1);
			hsl_t  cmp2 = Misc::rgbToHsl(cmp1);
#ifdef GPALCOMPANALYSIS
			int r, g, b;
			double h, s, l;
			r = cmp1.r - ref1.r;
			g = cmp1.g - ref1.g;
			b = cmp1.b - ref1.b;
			h = cmp2.h - ref2.h;
			s = cmp2.s - ref2.s;
			l = cmp2.l - ref2.l;
			devR += r;
			devG += g;
			devB += b;
			if (r > maxR) maxR = r; if (r < minR) minR = r;
			if (g > maxG) maxG = g; if (g < minG) minG = g;
			if (b > maxB) maxB = b; if (b < minB) minB = b;
			if (r | g | b)
			{
				++wrongcount;
				report += S_FMT("Index %003u: [%003i %003i %003i | %1.3f %1.3f %1.3f]->[%003i %003i %003i | %1.3f %1.3f %1.3f]\t\tR %+003i\tG %+003i\tB %+003i\t\t\tH %+1.3f\tS %+1.3f\tL %+1.3f\n",
				                c,
				                ref1.r, ref1.g, ref1.b, ref2.h, ref2.s, ref2.l,
				                cmp1.r, cmp1.g, cmp1.b, cmp2.h, cmp2.s, cmp2.l,
				                r, g, b, h, s, l);
			}
#else
			if (reds[ref1.r] != cmp1.r && reds[ref1.r] != 999)
				DPrintf("Discrepency for red channel at index %i, value %i: %d vs. %d set before",
				        c, ref1.r, cmp1.r, reds[ref1.r]);
			if (greens[ref1.g] != cmp1.g && greens[ref1.g] != 999)
				DPrintf("Discrepency for green channel at index %i, value %i: %d vs. %d set before",
				        c, ref1.g, cmp1.g, greens[ref1.g]);
			if (blues[ref1.b] != cmp1.b && blues[ref1.b] != 999)
				DPrintf("Discrepency for blue channel at index %i, value %i: %d vs. %d set before",
				        c, ref1.b, cmp1.b, blues[ref1.b]);
			reds[ref1.r] = cmp1.r;
			greens[ref1.g] = cmp1.g;
			blues[ref1.b] = cmp1.b;
#endif
		}
#ifdef GPALCOMPANALYSIS
		report += S_FMT("Deviation sigma: R %+003i G %+003i B %+003i\t%s\n", devR, devG, devB, entry->getName(true));
		report += S_FMT("Min R %+003i Min G %+003i Min B %+003i Max R %+003i Max G %+003i Max B %+003i \nError count: %i\n",
		                minR, minG, minB, maxR, maxG, maxB, wrongcount);
#else
		report += "Shift table for existing channel values:\n|  I  |  R  |  G  |  B  |\n";
		for (size_t i = 0; i < 256; ++i)
		{
			if (reds[i] < 999 || greens[i] < 999 || blues[i] < 999)
				report += S_FMT("| %003d | %003d | %003d | %003d |\n", i, reds[i], greens[i], blues[i]);
		}
		report.Replace("999", "   ");
#endif
	}

	wxLogMessage(report);

}

/*******************************************************************
 * PALETTEENTRYPANEL CLASS EVENTS
 *******************************************************************/

/* PaletteEntryPanel::onPalCanvasMouseEvent
 * Called when a mouse event happens within the palette canvas (eg.
 * button clicked, pointer moved, etc)
 *******************************************************************/
void PaletteEntryPanel::onPalCanvasMouseEvent(wxMouseEvent& e)
{
	// Update colour info label with selected colour (if any)
	if (e.LeftDown())
	{
		// Send to palette canvas
		pal_canvas->onMouseLeftDown(e);

		// Update status bar
		updateStatus();
	}
	else if (e.RightDown())
	{
		// Would this be better if the colour picking was handled by
		// the canvas' onMouseRightDown() function? The problem here
		// being that the canvas processes its events after the panel.
		// So for the left click we can afford to call it from there
		// first and let it harmlessly process it again, but for the
		// right click it would result in the colour box being shown
		// twice to the user, the second time being ignored. So it is
		// preferrable to handle all this on this side rather than try
		// to make the canvas do the work.
		// Pretend there was a left click to get the selected colour.
		pal_canvas->onMouseLeftDown(e);
		int sel = pal_canvas->getSelectionStart();

		// There actually was a colour selected
		if (sel > -1)
		{
			rgba_t col = pal_canvas->getSelectedColour();
			// Open a colour dialog
			wxColour cd = wxGetColourFromUser(GetParent(), WXCOL(col));

			if (cd.Ok())
			{
				col.r = cd.Red();
				col.g = cd.Green();
				col.b = cd.Blue();

				palettes[cur_palette]->setColour(sel, col);
				setModified();
				showPalette(cur_palette);
			}
		}
	}
}