/////////////////////////////////////////////////////////////////////////////
// Name:        MenuPalettes.h
// Purpose:     The class to create and store menu palettes 
// Author:      Alex Thuering
// Created:	04.11.2006
// RCS-ID:      $Id: MenuPalettes.cpp,v 1.2 2006/12/06 14:53:37 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "MenuPalettes.h"
#include <wx/log.h>

bool Palette::Create(IntHashSet& colours, const wxColour& transpColour)
{
  if (colours.find(-1) != colours.end()) // => image can contain transparent pixels
  {
    m_transpColour = transpColour;
    colours.erase(-1);
  }
  coloursCount = colours.size();
  unsigned char r[colours.size()], g[colours.size()], b[colours.size()];
  int i = 0;
  for (IntHashSet::iterator it = colours.begin(); it != colours.end(); ++it)
  {
    unsigned int color = (unsigned int) *it;
    r[i] = (color>>16)&0xFF;
    g[i] = (color>>8)&0xFF;
    b[i] = color&0xFF;
    i++;
  }
  return wxPalette::Create(colours.size(), r, g, b);
}

bool Palette::Apply(const unsigned char* pixel, unsigned char *dest) const
{
  if (coloursCount == 0)
  {
  	// there are no colours => colour of this pixel can be only transparent
  	dest[0] = m_transpColour.Red();
  	dest[1] = m_transpColour.Green();
    dest[2] = m_transpColour.Blue();
  }
  else if (!m_transpColour.Ok()
  		|| pixel[0] != m_transpColour.Red()
  		|| pixel[1] != m_transpColour.Green()
    	|| pixel[2] != m_transpColour.Blue()) // skip transparent pixels
  {
  	// find matched colour
	int n = GetPixel(pixel[0], pixel[1], pixel[2]);
	return wxPalette::GetRGB(n, dest, dest +1, dest + 2);
  }
  return true;
}

WX_DECLARE_HASH_MAP(int, IntHashSet, wxIntegerHash, wxIntegerEqual, IntHashSetMap);
WX_DECLARE_HASH_MAP(int, IntHashSetMap, wxIntegerHash, wxIntegerEqual, IntHashSet2DMap);

///////////////////////////// Menu Palettes //////////////////////////////////

MenuPalettes::MenuPalettes(MenuObject& obj, const wxColour& transpColour)
{
  m_transpColour = transpColour;
  // palette 1 for buttons_normal => all colours + transport colour
  IntHashSet colours;
  for (int i=0; i<obj.GetObjectParamsCount(); i++)
  {
    MenuObjectParam* param = obj.GetObjectParam(i);
    if (param->changeable) 
    {
      wxColour& colour = param->normalColour;
      int value = colour.Ok() ? (colour.Red()<<16) + (colour.Green()<<8) + colour.Blue() : -1;
      colours.insert(value);
    }
  }
  colours.insert(-1); // first image can always contain transparent pixels
  m_palette1.Create(colours, transpColour);
  
  // palette(s) 2 for buttons_highlighted => a palette per colour from palette 1
  IntHashSetMap coloursMap;
  for (int i=0; i<obj.GetObjectParamsCount(); i++)
  {
    MenuObjectParam* param = obj.GetObjectParam(i);
    if (param->changeable) 
    {
      wxColour& colour1 = param->normalColour;
      int key1 = colour1.Ok() ? (colour1.Red()<<16) + (colour1.Green()<<8) + colour1.Blue() : -1;
      wxColour& colour = param->highlightedColour;
      int value = colour.Ok() ? (colour.Red()<<16) + (colour.Green()<<8) + colour.Blue() : -1;
      coloursMap[key1].insert(value);
    }
  }
  coloursMap[-1].insert(-1); // second image can contain transparent pixels if pixel in first image is transparent 
  for (IntHashSetMap::iterator it = coloursMap.begin(); it != coloursMap.end(); ++it)
    m_palette2[it->first].Create(it->second, transpColour);
  
  // palette(s) 2 for buttons_selected => a palette per colour from palette 1 and 2
  IntHashSet2DMap colours2DMap;
  for (int i=0; i<obj.GetObjectParamsCount(); i++)
  {
    MenuObjectParam* param = obj.GetObjectParam(i);
    if (param->changeable) 
    {
      wxColour& colour1 = param->normalColour;
      int key1 = colour1.Ok() ? (colour1.Red()<<16) + (colour1.Green()<<8) + colour1.Blue() : -1;
      wxColour& colour2 = param->highlightedColour;
      int key2 = colour2.Ok() ? (colour2.Red()<<16) + (colour2.Green()<<8) + colour2.Blue() : -1;
      wxColour& colour = param->selectedColour;
      int value = colour.Ok() ? (colour.Red()<<16) + (colour.Green()<<8) + colour.Blue() : -1;
      colours2DMap[key1][key2].insert(value);
    }
  }
  colours2DMap[-1][-1].insert(-1); // third image can contain transparent pixels if pixel in first and second image is transparent
  for (IntHashSet2DMap::iterator it = colours2DMap.begin(); it != colours2DMap.end(); ++it)
  	for (IntHashSetMap::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2)
      m_palette3[it->first][it2->first].Create(it2->second, transpColour);
}

int MenuPalettes::GetKey(const unsigned char* pixel)
{
  if (pixel[0] == m_transpColour.Red()
  		&& pixel[1] == m_transpColour.Green()
    	&& pixel[2] == m_transpColour.Blue())
    return -1; // it's transparent colour
  return (pixel[0]<<16) + (pixel[1]<<8) + pixel[2];
}

bool MenuPalettes::Apply(const unsigned char* pixel1, unsigned char *dest)
{
  return m_palette1.Apply(pixel1, dest);
}

bool MenuPalettes::Apply(const unsigned char* pixel1, const unsigned char* pixel2,
	unsigned char *dest)
{
  int key1 = GetKey(pixel1);
  return m_palette2[key1].Apply(pixel2, dest);
}

bool MenuPalettes::Apply(const unsigned char* pixel1, const unsigned char*  pixel2,
	const unsigned char* pixel3, unsigned char *dest)
{
  int key1 = GetKey(pixel1);
  int key2 = GetKey(pixel2);
  return m_palette3[key1][key2].Apply(pixel3, dest);
}
