
#include "stdafx.h"

#include "PajaTypes.h"
#include "ColorSwatchSetC.h"
#include "lcms.h"
#include <assert.h>
#include <stdio.h>

using namespace PajaTypes;


ColorSwatchSetC::ColorSwatchSetC() :
	m_hInCMYK( 0 ),
	m_hTransCMYK( 0 ),
	m_hInLab( 0 ),
	m_hTransLab( 0 ),
	m_hOutRGB( 0 ),
	m_bLCMSIntialised( false ),
	m_bModified( false )
{
	// empty
}

ColorSwatchSetC::~ColorSwatchSetC()
{
	if( m_hTransCMYK )
		cmsDeleteTransform( m_hTransCMYK );
	if( m_hTransLab )
		cmsDeleteTransform( m_hTransLab );
	if( m_hInCMYK )
		cmsCloseProfile( m_hInCMYK );
	if( m_hInLab )
		cmsCloseProfile( m_hInLab );
	if( m_hOutRGB )
		cmsCloseProfile( m_hOutRGB );
}

bool
ColorSwatchSetC::init_color_management( const char* szCMYKProfile )
{
//	cmsErrorAction( LCMS_ERROR_SHOW );
	
	// Create build in profiles amd transforms
	
	m_hInLab = cmsCreateLabProfile( NULL );
	m_hOutRGB = cmsCreate_sRGBProfile();

	m_hTransLab = cmsCreateTransform( m_hInLab, TYPE_Lab_16, m_hOutRGB, TYPE_RGB_8, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC );

	// Build CMYK profile and transform
	m_hInCMYK = cmsOpenProfileFromFile( szCMYKProfile, "r");

	if( m_hInCMYK == NULL )
		return false;

	m_hTransCMYK = cmsCreateTransform( m_hInCMYK, TYPE_CMYK_16, m_hOutRGB, TYPE_RGB_8, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC );
	
	if( m_hTransCMYK == NULL )
		return false;
	
	return true;
}

bool
ColorSwatchSetC::load_swatches( const char* szFile, bool bAppend )
{
	FILE*	pStream;

	pStream = fopen( szFile, "rb" );
	if( !pStream )
		return false;

	// Read version
	uint16	ui16Version;

	fread( &ui16Version, sizeof( ui16Version ), 1, pStream );
	swap_endian( ui16Version );
	if( ui16Version != 1 ) {
		TRACE( "wrong color swatch version: 0x%04x\n", ui16Version );
		fclose( pStream );
		return false;
	}

	// read swatches
	uint16	ui16SwatchCount;
	fread( &ui16SwatchCount, sizeof( ui16SwatchCount ), 1, pStream );
	swap_endian( ui16SwatchCount );

	uint16	ui16Data[5];	// 10 bytes


	if( !bAppend )
		m_rColors.clear();

	for( uint32 i = 0; i < ui16SwatchCount; i++ ) {

		ColorSwatchS	rColSw;

		fread( &ui16Data, sizeof( ui16Data ), 1, pStream );

		memcpy( rColSw.m_ui16Data, ui16Data, sizeof( ui16Data ) );

		swap_endian( ui16Data[0] );
		swap_endian( ui16Data[1] );
		swap_endian( ui16Data[2] );
		swap_endian( ui16Data[3] );
		swap_endian( ui16Data[4] );

		ColorC	rCol( 0, 0, 0, 0 );
		int32	i32R, i32G, i32B;

		switch( ui16Data[0] ) {
		case 0:
			// RGB
			{
				i32R = (int32)ui16Data[1];
				i32G = (int32)ui16Data[2];
				i32B = (int32)ui16Data[3];
				rCol.convert_from_uint8( i32R >> 8, i32G >> 8, i32B >> 8 );
			}
			break;
		case 1:
			// HSB
			{
				int32	i32InH = ((int32)ui16Data[1]) * 36000 / 65536;
				int32	i32InS = (int32)ui16Data[2];
				int32	i32InB = (int32)ui16Data[3];

				hsv_to_rgb( i32InH, i32InS, i32InB, i32R, i32G, i32B );

				rCol.convert_from_uint8( i32R, i32G, i32B );
			}
			break;
		case 2:
			// CMYK
			{
				int32	i32InC = 65535 - (int32)ui16Data[1];
				int32	i32InM = 65535 - (int32)ui16Data[2];
				int32	i32InY = 65535 - (int32)ui16Data[3];
				int32	i32InK = 65535 - (int32)ui16Data[4];

				cmyk_to_rgb( i32InC, i32InM, i32InY, i32InK, i32R, i32G, i32B );

				rCol.convert_from_uint8( i32R, i32G, i32B );
			}
			break;
		case 7:
			// Lab
			{
				int16	i16TmpA, i16TmpB;

				i16TmpA = (int16)ui16Data[2];	// convert to signed
				i16TmpB = (int16)ui16Data[3];

				// now 'L' is in range 0..10000 -> convert to 0..65535
				int32	i32InL = ui16Data[1] * 65535 / 10000;
				// 'a' and 'b' are in range -12800..12700 -> convert to 0..65535
				int32	i32Ina = ((int32)i16TmpA + 12800) * 256 / 100;
				int32	i32Inb = ((int32)i16TmpB + 12800) * 256 / 100;

				lab_to_rgb( i32InL, i32Ina, i32Inb, i32R, i32G, i32B );

				rCol.convert_from_uint8( i32R, i32G, i32B );
			}
			break;
		case 8:
			// greyscale
			{
				int32	i32Val = 255 - ((ui16Data[1]) * 255) / 10000;
				rCol.convert_from_uint8( i32Val, i32Val, i32Val );
			}
			break;
		default:
			{
				TRACE( "Invalid swatch type: 0x%04x\n", ui16Data[0] );
			}
		}

		rColSw.m_rCol = rCol;
		m_rColors.push_back( rColSw );
	}

	fclose( pStream );

	if( !bAppend )
		m_bModified = false;

	return true;
}

bool
ColorSwatchSetC::save_swatches( const char* szFile )
{
	FILE*	pStream;

	pStream = fopen( szFile, "wb" );
	if( !pStream )
		return false;

	// write version
	uint16	ui16Version = 1;
	swap_endian( ui16Version );
	fwrite( &ui16Version, sizeof( ui16Version ), 1, pStream );

	// write swatch count
	uint16	ui16SwatchCount = m_rColors.size();
	swap_endian( ui16SwatchCount );
	fwrite( &ui16SwatchCount, sizeof( ui16SwatchCount ), 1, pStream );

	uint16	ui16Data[5] = { 0, 0, 0, 0, 0 };	// 10 bytes

	// write data
	for( uint32 i = 0; i < m_rColors.size(); i++ ) {

/*		ui16Data[0] = 0;	// RGB ID
		ui16Data[1] = (uint16)(m_rColors[i][0] * 65535.0f);
		ui16Data[2] = (uint16)(m_rColors[i][1] * 65535.0f);
		ui16Data[3] = (uint16)(m_rColors[i][2] * 65535.0f);

		swap_endian( ui16Data[0] );
		swap_endian( ui16Data[1] );
		swap_endian( ui16Data[2] );
		swap_endian( ui16Data[3] );
		swap_endian( ui16Data[4] );*/

		fwrite( m_rColors[i].m_ui16Data, sizeof( m_rColors[i].m_ui16Data ), 1, pStream );
	}

	fclose( pStream );

	m_bModified = false;

	return true;
}

uint32
ColorSwatchSetC::get_color_count() const
{
	return m_rColors.size();
}

void
ColorSwatchSetC::add_color( const ColorC& rCol, int32 i32InsBefore )
{
	ColorSwatchS	rColSw;

	rColSw.m_rCol = rCol;

	rColSw.m_ui16Data[0] = 0;	// RGB ID
	rColSw.m_ui16Data[1] = (uint16)(rCol[0] * 65535.0f);
	rColSw.m_ui16Data[2] = (uint16)(rCol[1] * 65535.0f);
	rColSw.m_ui16Data[3] = (uint16)(rCol[2] * 65535.0f);
	rColSw.m_ui16Data[4] = 0;

	swap_endian( rColSw.m_ui16Data[0] );
	swap_endian( rColSw.m_ui16Data[1] );
	swap_endian( rColSw.m_ui16Data[2] );
	swap_endian( rColSw.m_ui16Data[3] );
	swap_endian( rColSw.m_ui16Data[4] );

	if( i32InsBefore == -1 || i32InsBefore >= m_rColors.size() )
		m_rColors.push_back( rColSw );
	else {
		m_rColors.insert( m_rColors.begin() + i32InsBefore, rColSw );
	}

	m_bModified = true;
}

void
ColorSwatchSetC::del_color( uint32 ui32Idx )
{
	m_rColors.erase( m_rColors.begin() + ui32Idx );

	m_bModified = true;
}

const ColorC&
ColorSwatchSetC::get_color( uint32 ui32Idx ) const
{
	assert( ui32Idx < m_rColors.size() );
	return m_rColors[ui32Idx].m_rCol;
}

void
ColorSwatchSetC::set_color( uint32 ui32Idx, const ColorC& rCol )
{
	assert( ui32Idx < m_rColors.size() );
	m_rColors[ui32Idx].m_rCol = rCol;

	m_rColors[ui32Idx].m_ui16Data[0] = 0;	// RGB ID
	m_rColors[ui32Idx].m_ui16Data[1] = (uint16)(rCol[0] * 65535.0f);
	m_rColors[ui32Idx].m_ui16Data[2] = (uint16)(rCol[1] * 65535.0f);
	m_rColors[ui32Idx].m_ui16Data[3] = (uint16)(rCol[2] * 65535.0f);
	m_rColors[ui32Idx].m_ui16Data[4] = 0;

	swap_endian( m_rColors[ui32Idx].m_ui16Data[0] );
	swap_endian( m_rColors[ui32Idx].m_ui16Data[1] );
	swap_endian( m_rColors[ui32Idx].m_ui16Data[2] );
	swap_endian( m_rColors[ui32Idx].m_ui16Data[3] );
	swap_endian( m_rColors[ui32Idx].m_ui16Data[4] );

	m_bModified = true;
}

void
ColorSwatchSetC::cmyk_to_rgb( int32 iC, int32 iM, int32 iY, int32 iK, int32& iR, int32& iG, int32& iB )
{
	if( !m_hTransCMYK ) {
		iR = 0;
		iG = 0;
		iB = 0;
		return;
	}
	
	unsigned short	iSrc[4];
	unsigned char	iDst[3];

	iSrc[0] = (unsigned short)iC;
	iSrc[1] = (unsigned short)iM;
	iSrc[2] = (unsigned short)iY;
	iSrc[3] = (unsigned short)iK;

	cmsDoTransform( m_hTransCMYK, iSrc, iDst, 1 );

	iR = iDst[0];
	iG = iDst[1];
	iB = iDst[2];
}

void
ColorSwatchSetC::hsv_to_rgb( int32 iH, int32 iS, int32 iV, int32& iR, int32& iG, int32& iB )
{
	if( iS == 0 ) {
		// Achromatic color
		iR = (iV >> 8);
		iG = (iV >> 8);
		iB = (iV >> 8);
		return;
	}

	unsigned int	iF, iP, iQ, iT, i;

	if( iH == 36000 )	// 360 degrees is equivalent to 0 degrees
		iH = 0;

	i = iH / 6000;

	iF = ((iH - (i * 6000)) * 65535) / 6000;	// fractional part

	iP = (iV * (65535 - iS)) >> 16;
	iQ = (iV * (65535 - ((iS * iF) >> 16))) >> 16;
	iT = (iV * (65535 - ((iS * (65535 - iF)) >> 16) )) >> 16;

	switch( i ) {
	case 0:	iR = (iV >> 8); iG = (iT >> 8); iB = (iP >> 8); break;
	case 1:	iR = (iQ >> 8); iG = (iV >> 8); iB = (iP >> 8); break;
	case 2:	iR = (iP >> 8); iG = (iV >> 8); iB = (iT >> 8); break;
	case 3:	iR = (iP >> 8); iG = (iQ >> 8); iB = (iV >> 8); break;
	case 4:	iR = (iT >> 8); iG = (iP >> 8); iB = (iV >> 8); break;
	case 5:	iR = (iV >> 8); iG = (iP >> 8); iB = (iQ >> 8); break;
	};
}

void
ColorSwatchSetC::lab_to_rgb( int32 iL, int32 ia, int32 ib, int32& iR, int32& iG, int32& iB )
{
	if( !m_hTransLab ) {
		iR = 0;
		iG = 0;
		iB = 0;
		return;
	}
	
	unsigned short	iSrc[3];
	unsigned char	iDst[3];

	iSrc[0] = (unsigned short)iL;
	iSrc[1] = (unsigned short)ia;
	iSrc[2] = (unsigned short)ib;

	cmsDoTransform( m_hTransLab, iSrc, iDst, 1 );


	iR = iDst[0];
	iG = iDst[1];
	iB = iDst[2];
}

void
ColorSwatchSetC::swap_endian( PajaTypes::uint16& ui16Data )
{
	uint16	ui16Tmp = ui16Data;
	ui16Data = ((ui16Tmp & 0xff) << 8) | (ui16Tmp >> 8);
}

void
ColorSwatchSetC::clear_all()
{
	m_rColors.clear();
	m_bModified = true;
}

bool
ColorSwatchSetC::is_modified() const
{
	return m_bModified;
}
