///////////////////////////////////////////////////////////////////////////////
// Name:        Palettes3D.h
// Purpose:     The class to create and store palette with 3 colours per entry 
// Author:      Alex Thuering
// Created:	05.11.2006
// RCS-ID:      $Id: Palette3D.cpp,v 1.2 2006/12/06 14:53:37 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
///////////////////////////////////////////////////////////////////////////////

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

inline unsigned char getR(unsigned int c) { return (unsigned char) c; }
inline unsigned char getG(unsigned int c) { return (unsigned char) (c >> 8); }
inline unsigned char getB(unsigned int c) { return (unsigned char) (c >> 16); }

inline int Colour2Int(const wxColour& colour)
{
  return colour.Ok() ? (colour.Red()<<16) + (colour.Green()<<8) + colour.Blue() : -1;
}

inline wxColour Int2Colour(int colour)
{
  return colour != -1 ? wxColour(getR(colour), getG(colour), getB(colour)) : wxColour();
}

inline double getDinstance(int c1, int c2)
{
  if (c1 == -1 && c2 == -1)
  	return 0;
  else if (c1 == -1 || c2 == -1)
    return 768;
  return  0.299 * abs(getR(c1) - getR(c2)) +
          0.587 * abs(getG(c1) - getG(c2)) +
          0.114 * abs(getB(c1) - getB(c2));
}

void Palette3D::Add(const wxColour& colour1, const wxColour& colour2,
		const wxColour& colour3)
{
  int c1 = Colour2Int(colour1); 
  int c2 = Colour2Int(colour2);
  int c3 = Colour2Int(colour3);
  for (int i = 0; i<(int)m_colours1.GetCount(); i++)
  {
  	if (m_colours1[i] == c1 && m_colours2[i] == c2 && m_colours3[i] == c3)
  	  return; // duplicated entry
  }
  m_colours1.Add(c1);
  m_colours2.Add(c2);
  m_colours3.Add(c3);
}

bool Palette3D::Apply(wxColour& colour1, wxColour& colour2, wxColour& colour3)
{
  if (m_colours1.GetCount() == 0)
    return false;
  
  int c1 = Colour2Int(colour1); 
  int c2 = Colour2Int(colour2);
  int c3 = Colour2Int(colour3);
  
  int closest = 0;
  double d, dinstance = 3000; // max 3*768
  for (int i = 0; i<(int)m_colours1.GetCount(); i++)
  {
  	d = getDinstance(c1, m_colours1[i]) + getDinstance(c2, m_colours2[i])
  	    + getDinstance(c3, m_colours3[i]);
  	if (d < dinstance)
  	{
  		dinstance = d;
  		closest = i;
  	}
  }
  if (c1 != m_colours1[closest] || c2 != m_colours2[closest] || c3 != m_colours3[closest])
  {
    colour1 = Int2Colour(m_colours1[closest]);
    colour2 = Int2Colour(m_colours2[closest]);
    colour3 = Int2Colour(m_colours3[closest]);
    return true;
  }
  return false;
}

void Palette3D::ReduceColours(int maxColours)
{
  while ((int)m_colours1.GetCount() > maxColours)
  {
  	int closest = 0;
    double d, dinstance = 3000; // max 3*768
  	for (int i = 0; i<(int)m_colours1.GetCount(); i++)
      for (int j = 0; j<(int)m_colours1.GetCount(); j++)
        if (i != j)
        {
          d = getDinstance(m_colours1[i], m_colours1[j])
        	+ getDinstance(m_colours2[i], m_colours2[j])
  		    + getDinstance(m_colours3[i], m_colours3[j]);
  	      if (d < dinstance)
  	      {
  	        dinstance = d;
  	        closest = j;
  	      }
        }
    m_colours1.RemoveAt(closest);
    m_colours2.RemoveAt(closest);
    m_colours3.RemoveAt(closest);
  }
}
