/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_tools.hxx"

#include <stdlib.h>
#include <vos/macros.hxx>
#include <tools/color.hxx>
#include <tools/debug.hxx>
#include <tools/stream.hxx>
#include <tools/rc.hxx>
#include <tools/rcid.h>
#include <tools/resid.hxx>
#ifndef _SV_RC_H
#include <tools/rc.h>
#endif

// -----------
// - Inlines -
// -----------

static inline long _FRound( double fVal )
{
	return( fVal > 0.0 ? (long) ( fVal + 0.5 ) : -(long) ( -fVal + 0.5 ) );
}

// ---------
// - Color -
// ---------

Color::Color( const ResId& rResId )
{
	rResId.SetRT( RSC_COLOR );
	ResMgr* pResMgr = rResId.GetResMgr();
	if ( pResMgr && pResMgr->GetResource( rResId ) )
	{
		// Header ueberspringen
		pResMgr->Increment( sizeof( RSHEADER_TYPE ) );

		// Daten laden
		sal_uInt16 nRed		= pResMgr->ReadShort();
		sal_uInt16 nGreen	= pResMgr->ReadShort();
		sal_uInt16 nBlue	= pResMgr->ReadShort();
		// one more historical sal_uIntPtr
		pResMgr->ReadLong();

		// RGB-Farbe
		mnColor = RGB_COLORDATA( nRed>>8, nGreen>>8, nBlue>>8 );
	}
	else
    {
        mnColor = RGB_COLORDATA( 0, 0, 0 );
    }
}
sal_uInt8 Color::GetColorError( const Color& rCompareColor ) const
{
	const long nErrAbs = labs( (long) rCompareColor.GetRed() - GetRed() ) + 
						 labs( (long) rCompareColor.GetGreen() - GetGreen() ) + 
						 labs( (long) rCompareColor.GetBlue() - GetBlue() );

	return (sal_uInt8) _FRound( nErrAbs * 0.3333333333 );
}

// -----------------------------------------------------------------------

void Color::IncreaseLuminance( sal_uInt8 cLumInc )
{
	SetRed( (sal_uInt8) VOS_BOUND( (long) COLORDATA_RED( mnColor ) + cLumInc, 0L, 255L ) );
	SetGreen( (sal_uInt8) VOS_BOUND( (long) COLORDATA_GREEN( mnColor ) + cLumInc, 0L, 255L ) );
	SetBlue( (sal_uInt8) VOS_BOUND( (long) COLORDATA_BLUE( mnColor ) + cLumInc, 0L, 255L ) );
}

// -----------------------------------------------------------------------

void Color::DecreaseLuminance( sal_uInt8 cLumDec )
{
	SetRed( (sal_uInt8) VOS_BOUND( (long) COLORDATA_RED( mnColor ) - cLumDec, 0L, 255L ) );
	SetGreen( (sal_uInt8) VOS_BOUND( (long) COLORDATA_GREEN( mnColor ) - cLumDec, 0L, 255L ) );
	SetBlue( (sal_uInt8) VOS_BOUND( (long) COLORDATA_BLUE( mnColor ) - cLumDec, 0L, 255L ) );
}

// -----------------------------------------------------------------------

void Color::IncreaseContrast( sal_uInt8 cContInc )
{
	if( cContInc)
	{
		const double fM = 128.0 / ( 128.0 - 0.4985 * cContInc );
		const double fOff = 128.0 - fM * 128.0;

		SetRed( (sal_uInt8) VOS_BOUND( _FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L ) );
		SetGreen( (sal_uInt8) VOS_BOUND( _FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L ) );
		SetBlue( (sal_uInt8) VOS_BOUND( _FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L ) );
	}
}

// -----------------------------------------------------------------------

void Color::DecreaseContrast( sal_uInt8 cContDec )
{
	if( cContDec )
	{
		const double fM = ( 128.0 - 0.4985 * cContDec ) / 128.0;
		const double fOff = 128.0 - fM * 128.0;

		SetRed( (sal_uInt8) VOS_BOUND( _FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L ) );
		SetGreen( (sal_uInt8) VOS_BOUND( _FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L ) );
		SetBlue( (sal_uInt8) VOS_BOUND( _FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L ) );
	}
}

// -----------------------------------------------------------------------

void Color::Invert()
{
	SetRed( ~COLORDATA_RED( mnColor ) );
	SetGreen( ~COLORDATA_GREEN( mnColor ) );
	SetBlue( ~COLORDATA_BLUE( mnColor ) );
}

// -----------------------------------------------------------------------

sal_Bool Color::IsDark() const
{
    return GetLuminance() <= 55;
}

// -----------------------------------------------------------------------

sal_Bool Color::IsBright() const
{
    return GetLuminance() >= 245;
}

// -----------------------------------------------------------------------
// color space conversion
// -----------------------------------------------------------------------

void Color::RGBtoHSB( sal_uInt16& nHue, sal_uInt16& nSat, sal_uInt16& nBri ) const
{
	sal_uInt8 c[3];
	sal_uInt8 cMax, cMin;

	c[0] = GetRed();
	c[1] = GetGreen();
	c[2] = GetBlue();

	cMax = c[0];
	if( c[1] > cMax )
		cMax = c[1];
	if( c[2] > cMax )
		cMax = c[2];

	// Brightness = max(R, G, B);
	nBri = cMax * 100 / 255;

	cMin = c[0];
	if( c[1] < cMin )
		cMin = c[1];
	if( c[2] < cMin )
		cMin = c[2];

	sal_uInt8 cDelta = cMax - cMin;

	// Saturation = max - min / max
	if( nBri > 0 )
		nSat = cDelta * 100 / cMax;
	else
		nSat = 0;

	if( nSat == 0 )
		nHue = 0; // Default = undefined
	else
	{
		double dHue = 0.0;

		if( c[0] == cMax )
		{
			dHue = (double)( c[1] - c[2] ) / (double)cDelta;
		}
		else if( c[1] == cMax )
		{
			dHue = 2.0 + (double)( c[2] - c[0] ) / (double)cDelta;
		}
		else if ( c[2] == cMax )
		{
			dHue = 4.0 + (double)( c[0] - c[1] ) / (double)cDelta;
		}
		dHue *= 60.0;

		if( dHue < 0.0 )
			dHue += 360.0;

		nHue = (sal_uInt16) dHue;
	}
}

ColorData Color::HSBtoRGB( sal_uInt16 nHue, sal_uInt16 nSat, sal_uInt16 nBri )
{
	sal_uInt8 cR=0,cG=0,cB=0;
	sal_uInt8 nB = (sal_uInt8) ( nBri * 255 / 100 );

	if( nSat == 0 )
	{
		cR = nB;
		cG = nB;
		cB = nB;
	}
	else
	{
		double dH = nHue;
		double f;
		sal_uInt16 n;
		if( dH == 360.0 )
			dH = 0.0;

		dH /= 60.0;
		n = (sal_uInt16) dH;
		f = dH - n;

		sal_uInt8 a = (sal_uInt8) ( nB * ( 100 - nSat ) / 100 );
		sal_uInt8 b = (sal_uInt8) ( nB * ( 100 - ( (double)nSat * f ) ) / 100 );
		sal_uInt8 c = (sal_uInt8) ( nB * ( 100 - ( (double)nSat * ( 1.0 - f ) ) ) / 100 );

		switch( n )
		{
			case 0: cR = nB;	cG = c;		cB = a; 	break;
			case 1: cR = b;		cG = nB;	cB = a; 	break;
			case 2: cR = a;		cG = nB;	cB = c;		break;
			case 3: cR = a;		cG = b; 	cB = nB;	break;
			case 4: cR = c;		cG = a; 	cB = nB;	break;
			case 5: cR = nB; 	cG = a;		cB = b;		break;
		}
	}

	return RGB_COLORDATA( cR, cG, cB );
}

// -----------------------------------------------------------------------

// CMYK values from 0 to 1
ColorData Color::CMYKtoRGB( double fCyan, double fMagenta, double fYellow, double fKey )
{
    fCyan = (fCyan * ( 1.0 - fKey )) + fKey;
    fMagenta = (fMagenta * ( 1.0 - fKey )) + fKey;
    fYellow = (fYellow * ( 1.0 - fKey )) + fKey;

    sal_uInt8 nRed = static_cast< sal_uInt8 >( std::max( std::min( ( 1.0 - fCyan ) * 255.0, 255.0), 0.0 ) );
    sal_uInt8 nGreen = static_cast< sal_uInt8 >( std::max( std::min( ( 1.0 - fMagenta ) * 255.0, 255.0), 0.0 ) );
    sal_uInt8 nBlue = static_cast< sal_uInt8 >( std::max( std::min( ( 1.0 - fYellow ) * 255.0, 255.0), 0.0 ) );

    return RGB_COLORDATA( nRed, nGreen, nBlue );
}

// -----------------------------------------------------------------------

// RGB values from 0 to 255
// CMY results from 0 to 1
void Color::RGBtoCMYK( double& fCyan, double& fMagenta, double& fYellow, double& fKey )
{
    fCyan = 1 - ( GetRed() / 255.0 );
    fMagenta = 1 - ( GetGreen() / 255.0 );
    fYellow = 1 - ( GetBlue() / 255.0 );

    //CMYK and CMY values from 0 to 1
    fKey = 1.0;
    if( fCyan < fKey ) fKey = fCyan;
    if( fMagenta < fKey ) fKey = fMagenta;
    if( fYellow < fKey ) fKey = fYellow;

    if ( fKey == 1.0 )
    {
       //Black
       fCyan = 0.0;
       fMagenta = 0.0;
       fYellow = 0.0;
    }
    else
    {
       fCyan = ( fCyan - fKey ) / ( 1.0 - fKey );
       fMagenta = ( fMagenta - fKey ) / ( 1.0 - fKey );
       fYellow = ( fYellow - fKey ) / ( 1.0 - fKey );
    }
}

// -----------------------------------------------------------------------

SvStream& Color::Read( SvStream& rIStm, sal_Bool bNewFormat )
{
	if ( bNewFormat )
		rIStm >> mnColor;
	else
		rIStm >> *this;

	return rIStm;
}

// -----------------------------------------------------------------------

SvStream& Color::Write( SvStream& rOStm, sal_Bool bNewFormat )
{
	if ( bNewFormat )
		rOStm << mnColor;
	else
		rOStm << *this;

	return rOStm;
}

// -----------------------------------------------------------------------

#define COL_NAME_USER		((sal_uInt16)0x8000)
#define COL_RED_1B			((sal_uInt16)0x0001)
#define COL_RED_2B			((sal_uInt16)0x0002)
#define COL_GREEN_1B		((sal_uInt16)0x0010)
#define COL_GREEN_2B		((sal_uInt16)0x0020)
#define COL_BLUE_1B 		((sal_uInt16)0x0100)
#define COL_BLUE_2B 		((sal_uInt16)0x0200)

// -----------------------------------------------------------------------

SvStream& operator>>( SvStream& rIStream, Color& rColor )
{
	DBG_ASSERTWARNING( rIStream.GetVersion(), "Color::>> - Solar-Version not set on rIStream" );

	sal_uInt16		nColorName;
	sal_uInt16		nRed;
	sal_uInt16		nGreen;
	sal_uInt16		nBlue;

	rIStream >> nColorName;

	if ( nColorName & COL_NAME_USER )
	{
		if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL )
		{
			unsigned char	cAry[6];
			sal_uInt16			i = 0;

			nRed	= 0;
			nGreen	= 0;
			nBlue	= 0;

			if ( nColorName & COL_RED_2B )
				i += 2;
			else if ( nColorName & COL_RED_1B )
				i++;
			if ( nColorName & COL_GREEN_2B )
				i += 2;
			else if ( nColorName & COL_GREEN_1B )
				i++;
			if ( nColorName & COL_BLUE_2B )
				i += 2;
			else if ( nColorName & COL_BLUE_1B )
				i++;

			rIStream.Read( cAry, i );
			i = 0;

			if ( nColorName & COL_RED_2B )
			{
				nRed = cAry[i];
				nRed <<= 8;
				i++;
				nRed |= cAry[i];
				i++;
			}
			else if ( nColorName & COL_RED_1B )
			{
				nRed = cAry[i];
				nRed <<= 8;
				i++;
			}
			if ( nColorName & COL_GREEN_2B )
			{
				nGreen = cAry[i];
				nGreen <<= 8;
				i++;
				nGreen |= cAry[i];
				i++;
			}
			else if ( nColorName & COL_GREEN_1B )
			{
				nGreen = cAry[i];
				nGreen <<= 8;
				i++;
			}
			if ( nColorName & COL_BLUE_2B )
			{
				nBlue = cAry[i];
				nBlue <<= 8;
				i++;
				nBlue |= cAry[i];
				i++;
			}
			else if ( nColorName & COL_BLUE_1B )
			{
				nBlue = cAry[i];
				nBlue <<= 8;
				i++;
			}
		}
		else
		{
			rIStream >> nRed;
			rIStream >> nGreen;
			rIStream >> nBlue;
		}

		rColor.mnColor = RGB_COLORDATA( nRed>>8, nGreen>>8, nBlue>>8 );
	}
	else
	{
		static ColorData aColAry[] =
		{
			COL_BLACK,							// COL_BLACK
			COL_BLUE,							// COL_BLUE
			COL_GREEN,							// COL_GREEN
			COL_CYAN,							// COL_CYAN
			COL_RED,							// COL_RED
			COL_MAGENTA,						// COL_MAGENTA
			COL_BROWN,							// COL_BROWN
			COL_GRAY,							// COL_GRAY
			COL_LIGHTGRAY,						// COL_LIGHTGRAY
			COL_LIGHTBLUE,						// COL_LIGHTBLUE
			COL_LIGHTGREEN, 					// COL_LIGHTGREEN
			COL_LIGHTCYAN,						// COL_LIGHTCYAN
			COL_LIGHTRED,						// COL_LIGHTRED
			COL_LIGHTMAGENTA,					// COL_LIGHTMAGENTA
			COL_YELLOW, 						// COL_YELLOW
			COL_WHITE,							// COL_WHITE
			COL_WHITE,							// COL_MENUBAR
			COL_BLACK,							// COL_MENUBARTEXT
			COL_WHITE,							// COL_POPUPMENU
			COL_BLACK,							// COL_POPUPMENUTEXT
			COL_BLACK,							// COL_WINDOWTEXT
			COL_WHITE,							// COL_WINDOWWORKSPACE
			COL_BLACK,							// COL_HIGHLIGHT
			COL_WHITE,							// COL_HIGHLIGHTTEXT
			COL_BLACK,							// COL_3DTEXT
			COL_LIGHTGRAY,						// COL_3DFACE
			COL_WHITE,							// COL_3DLIGHT
			COL_GRAY,							// COL_3DSHADOW
			COL_LIGHTGRAY,						// COL_SCROLLBAR
			COL_WHITE,							// COL_FIELD
			COL_BLACK							// COL_FIELDTEXT
		};

		if ( nColorName < (sizeof( aColAry )/sizeof(ColorData)) )
			rColor.mnColor = aColAry[nColorName];
		else
			rColor.mnColor = COL_BLACK;
	}

	return rIStream;
}

// -----------------------------------------------------------------------

SvStream& operator<<( SvStream& rOStream, const Color& rColor )
{
	DBG_ASSERTWARNING( rOStream.GetVersion(), "Color::<< - Solar-Version not set on rOStream" );

	sal_uInt16 nColorName	= COL_NAME_USER;
	sal_uInt16 nRed 		= rColor.GetRed();
	sal_uInt16 nGreen		= rColor.GetGreen();
	sal_uInt16 nBlue		= rColor.GetBlue();
	nRed	= (nRed<<8) + nRed;
	nGreen	= (nGreen<<8) + nGreen;
	nBlue	= (nBlue<<8) + nBlue;

	if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL )
	{
		unsigned char	cAry[6];
		sal_uInt16			i = 0;

		if ( nRed & 0x00FF )
		{
			nColorName |= COL_RED_2B;
			cAry[i] = (unsigned char)(nRed & 0xFF);
			i++;
			cAry[i] = (unsigned char)((nRed >> 8) & 0xFF);
			i++;
		}
		else if ( nRed & 0xFF00 )
		{
			nColorName |= COL_RED_1B;
			cAry[i] = (unsigned char)((nRed >> 8) & 0xFF);
			i++;
		}
		if ( nGreen & 0x00FF )
		{
			nColorName |= COL_GREEN_2B;
			cAry[i] = (unsigned char)(nGreen & 0xFF);
			i++;
			cAry[i] = (unsigned char)((nGreen >> 8) & 0xFF);
			i++;
		}
		else if ( nGreen & 0xFF00 )
		{
			nColorName |= COL_GREEN_1B;
			cAry[i] = (unsigned char)((nGreen >> 8) & 0xFF);
			i++;
		}
		if ( nBlue & 0x00FF )
		{
			nColorName |= COL_BLUE_2B;
			cAry[i] = (unsigned char)(nBlue & 0xFF);
			i++;
			cAry[i] = (unsigned char)((nBlue >> 8) & 0xFF);
			i++;
		}
		else if ( nBlue & 0xFF00 )
		{
			nColorName |= COL_BLUE_1B;
			cAry[i] = (unsigned char)((nBlue >> 8) & 0xFF);
			i++;
		}

		rOStream << nColorName;
		rOStream.Write( cAry, i );
	}
	else
	{
		rOStream << nColorName;
		rOStream << nRed;
		rOStream << nGreen;
		rOStream << nBlue;
	}

	return rOStream;
}
