/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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


#include <vcl/salbtype.hxx>
#include <vcl/bmpacc.hxx>

#include <svtools/colctrl.hxx>

// ----------------
// - ColorControl -
// ----------------

SvColorControl::SvColorControl( Window* pParent, WinBits nStyle ) :
    Control			( pParent, nStyle ),
    mpBitmap		( NULL ),
    mpReadAccess	( NULL ),
    mnLuminance		( 50 )
{
    Initialize();
}

// -----------------------------------------------------------------------
SvColorControl::SvColorControl( Window* pParent, const ResId& rResId ) :
    Control			( pParent, rResId ),
    mpBitmap		( NULL ),
    mpReadAccess	( NULL ),
    mnLuminance		( 50 )
{
    Initialize();
}


// -----------------------------------------------------------------------
SvColorControl::~SvColorControl()
{
    delete mpBitmap;
}

// -----------------------------------------------------------------------
void SvColorControl::Initialize()
{
    SetLineColor( Color( COL_BLACK ) );
}

// -----------------------------------------------------------------------
void SvColorControl::CreateBitmap()
{
    const Size aSize( GetOutputSizePixel() );
    
    if( mpBitmap && mpBitmap->GetSizePixel() != aSize )
        delete mpBitmap, mpBitmap = NULL;

    if( !mpBitmap )
        mpBitmap = new Bitmap( aSize, 24 );

    BitmapWriteAccess* pWriteAccess = mpBitmap->AcquireWriteAccess();

    if( pWriteAccess )
    {
        sal_uInt16 nX = (sal_uInt16) aSize.Width();
        sal_uInt16 nY = (sal_uInt16) aSize.Height();

        sal_uInt16      nHue, nSat;
        ColorHSB    aColHSB( 0, 0, mnLuminance );

        for( sal_uInt16 i = 0; i < nY; i++ )
        {
            nSat = (sal_uInt16) FRound( 100 - ( 100.0 * i + 0.5 ) / nY );

            for( sal_uInt16 j = 0; j < nX; j++ )
            {
                nHue = (sal_uInt16) FRound( ( 360.0 * j + 0.5 ) / nX );

                aColHSB.SetHue( nHue );
                aColHSB.SetSat( nSat );
         
                // mpBitmap always has a bit count of 24 => use of SetPixel(...) is safe
                pWriteAccess->SetPixel( i, j, BitmapColor( aColHSB.GetRGB() ) );
            }
        }

        mpBitmap->ReleaseAccess( pWriteAccess );
    }

       SetColor( maColor );
}

// -----------------------------------------------------------------------
void SvColorControl::ShowPosition( const Point& rPos )
{
    // Explizites Abfragen des Bereichs, da schon mal ein Wert < 0 vorhanden ist
    if( mpBitmap )
    {
        long nX = rPos.X();
        long nY = rPos.Y();
        if( nX < 0L )
            nX = 0L;
        else if( nX >= mpBitmap->GetSizePixel().Width() )
            nX = mpBitmap->GetSizePixel().Width() - 1L;

        if( nY < 0L )
            nY= 0L;
        else if( nY >= mpBitmap->GetSizePixel().Height() )
            nY = mpBitmap->GetSizePixel().Height() - 1L;

        Point aPos = maPosition;
        maPosition.X() = nX - 2;
        maPosition.Y() = nY - 2;
        Invalidate( Rectangle( aPos, Size( 5, 5) ) );
        Invalidate( Rectangle( maPosition, Size( 5, 5) ) );

        if( ( mpReadAccess = mpBitmap->AcquireReadAccess() ) != NULL )
        {
            // mpBitmap always has a bit count of 24 => use of GetPixel(...) is safe
            maColor = mpReadAccess->GetPixel( nY, nX );
            mpBitmap->ReleaseAccess( mpReadAccess );
            mpReadAccess = NULL;
        }
    }
}
// -----------------------------------------------------------------------
void SvColorControl::MouseMove( const MouseEvent& rMEvt )
{
    if( rMEvt.IsLeft() )
    {
        ShowPosition( rMEvt.GetPosPixel() );
        Modify();
    }
}

// -----------------------------------------------------------------------
void SvColorControl::MouseButtonDown( const MouseEvent& rMEvt )
{
    if( rMEvt.IsLeft() && !rMEvt.IsShift() )
    {
        //ShowPointer( sal_False );
        CaptureMouse();
        ShowPosition( rMEvt.GetPosPixel() );
        Modify();
    }
}

// -----------------------------------------------------------------------
void SvColorControl::MouseButtonUp( const MouseEvent& )
{
    //ShowPointer( sal_True );
    if( IsMouseCaptured() )
        ReleaseMouse();
}

// -----------------------------------------------------------------------
void SvColorControl::Paint( const Rectangle& rRect )
{
    if( !mpBitmap )
        CreateBitmap();

    Bitmap aOutputBitmap( *mpBitmap );

    if( GetBitCount() <= 8 )
        aOutputBitmap.Dither();

    DrawBitmap( rRect.TopLeft(), rRect.GetSize(), rRect.TopLeft(), rRect.GetSize(), aOutputBitmap );

    // Positions-Control (Fadenkreuz oder Aehnliches)
    Point aPos1( maPosition );
    Point aPos2( maPosition );
    aPos2.X() += 4;
    DrawLine( aPos1, aPos2 );
    aPos2.X() -= 4;
    aPos2.Y() += 4;
    DrawLine( aPos1, aPos2 );
    aPos1.Y() += 4;
    aPos2.X() += 4;
    DrawLine( aPos1, aPos2 );
    aPos1.X() += 4;
    aPos2.Y() -= 4;
    DrawLine( aPos1, aPos2 );
}

// -----------------------------------------------------------------------
void SvColorControl::Resize()
{
    CreateBitmap();
    Control::Resize();
}

// -----------------------------------------------------------------------
void SvColorControl::Modify()
{
    maModifyHdl.Call( this );
}

// -----------------------------------------------------------------------
void SvColorControl::SetColor( const ColorHSB& rCol, sal_Bool bSetColor )
{
    if( bSetColor )
        maColor = rCol.GetRGB();

    if( mpBitmap )
    {
        sal_uInt16  nX = (sal_uInt16) mpBitmap->GetSizePixel().Width();
        sal_uInt16  nY = (sal_uInt16) mpBitmap->GetSizePixel().Height();
        sal_Int16   nZ = rCol.GetBri();
        
        SetLuminance( nZ );
        nX = rCol.GetHue() * nX / 360; // Farbe
        nY = nY - rCol.GetSat() * nY / 100; // Saettigung
        ShowPosition( Point( nX, nY ) );
    }
}

// -----------------------------------------------------------------------
void SvColorControl::SetColor( const Color& rCol )
{
    maColor = rCol;

    if( mpBitmap )
    {
        ColorHSB aColHsb( rCol );
        SetColor( aColHsb, sal_False );
    }
}

// -----------------------------------------------------------------------
void SvColorControl::SetLuminance( short nLum )
{
    if( nLum != mnLuminance && nLum >= 0 && nLum <= 100 )
    {
        mnLuminance = nLum;

        if( mnLuminance < 40 )
            SetLineColor( Color( COL_WHITE ) );
        else
            SetLineColor( Color( COL_BLACK ) );

        CreateBitmap();

        long nX = maPosition.X() + 2;
        long nY = maPosition.Y() + 2;

        if( mpBitmap && ( ( mpReadAccess = mpBitmap->AcquireReadAccess() ) != NULL ) )
        {
            // mpBitmap always has a bit count of 24 => use of GetPixel(...) is safe
            maColor = mpReadAccess->GetPixel( nY, nX );
            mpBitmap->ReleaseAccess( mpReadAccess );
            mpReadAccess = NULL;
        }

        Invalidate();
    }
}


// -----------------------
// - ColorPreviewControl -
// -----------------------


// -----------------------------------------------------------------------
ColorPreviewControl::ColorPreviewControl( Window* pParent, WinBits nStyle ) :
    Control		( pParent, nStyle )
{
    SetFillColor( maColor );
    SetLineColor( maColor );
}

// -----------------------------------------------------------------------
ColorPreviewControl::ColorPreviewControl( Window* pParent, const ResId& rResId ) :
    Control		( pParent, rResId )
{
    SetFillColor( maColor );
    SetLineColor( maColor );
}


// -----------------------------------------------------------------------
ColorPreviewControl::~ColorPreviewControl()
{
}

// -----------------------------------------------------------------------
void ColorPreviewControl::Paint( const Rectangle& rRect )
{
    DrawRect( rRect );
}

// -----------------------------------------------------------------------
void ColorPreviewControl::SetColor( const Color& rCol )
{
    if( rCol != maColor )
    {
        maColor = rCol;
        SetFillColor( maColor );
        SetLineColor( maColor );
        Invalidate();
    }
}


// -----------------------
// - ColorMixingControl -
// -----------------------


// -----------------------------------------------------------------------
ColorMixingControl::ColorMixingControl( Window* pParent, WinBits nStyle,
                                        sal_uInt16 nRows, sal_uInt16 nColumns ) :
    ValueSet	( pParent, nStyle ),
    mnRows		( nRows ),
    mnColumns	( nColumns )
{
    Initialize();
}

// -----------------------------------------------------------------------
ColorMixingControl::ColorMixingControl( Window* pParent, const ResId& rResId,
                                        sal_uInt16 nRows, sal_uInt16 nColumns ) :
    ValueSet	( pParent, rResId ),
    mnRows		( nRows ),
    mnColumns	( nColumns )
{
    Initialize();
}


// -----------------------------------------------------------------------
ColorMixingControl::~ColorMixingControl()
{
}

// -----------------------------------------------------------------------
void ColorMixingControl::Initialize()
{
    SetColCount( mnColumns );

    Color aColor;
    String aStr;
    for( sal_uInt16 i = 1; i <= mnRows * mnColumns; i++ )
    {
        InsertItem( i, aColor, aStr );
    }

    SetColor( CMC_TOPLEFT, Color( COL_LIGHTRED ) );
    SetColor( CMC_BOTTOMRIGHT, Color( COL_LIGHTBLUE ) );

    SetColor( CMC_TOPRIGHT, Color( COL_LIGHTGREEN ) );
    SetColor( CMC_BOTTOMLEFT, Color( COL_YELLOW ) );
}

// -----------------------------------------------------------------------
Color ColorMixingControl::CalcDifferenceColor( sal_uInt16 nCol1, sal_uInt16 nCol2,
                                                sal_uInt16 nSteps )
{
    // Die Berechnung ist noch etwas ungenau, daher sollte besser mit floats
    // gearbeitet werden...  (muss !!!)
    Color aColor( GetItemColor( nCol1 ) );
    Color aColor2( GetItemColor( nCol2 ) );

    aColor.SetRed( (sal_uInt8) ( ( aColor2.GetRed() - aColor.GetRed() ) / nSteps ) );
    aColor.SetGreen( (sal_uInt8) ( ( aColor2.GetGreen() - aColor.GetGreen() ) / nSteps ) );
    aColor.SetBlue( (sal_uInt8) ( ( aColor2.GetBlue() - aColor.GetBlue() ) / nSteps ) );

    return( aColor );
}

// -----------------------------------------------------------------------
void ColorMixingControl::FillRow( sal_uInt16 nRow )
{
    sal_uInt16 nCol1 = nRow * mnColumns + 1;
    sal_uInt16 nCol2 = ( nRow + 1 ) * mnColumns;
    Color aColor( GetItemColor( nCol1 ) );
    Color aDiffColor( CalcDifferenceColor( nCol1, nCol2, mnColumns - 1 ) );

    for( sal_uInt16 i = nCol1 + 1; i < nCol2; i++ )
    {
        aColor.SetRed( aColor.GetRed() + aDiffColor.GetRed() );
        aColor.SetGreen( aColor.GetGreen() + aDiffColor.GetGreen() );
        aColor.SetBlue( aColor.GetBlue() + aDiffColor.GetBlue() );

        SetItemColor( i, aColor );
        SetItemText( i, GetRGBString( aColor ) );
    }
}

// -----------------------------------------------------------------------
void ColorMixingControl::FillColumn( sal_uInt16 nColumn )
{
    sal_uInt16 nCol1 = nColumn + 1;
    sal_uInt16 nCol2 = nColumn + ( mnRows - 1 ) * mnColumns + 1;
    Color aColor( GetItemColor( nCol1 ) );
    Color aDiffColor( CalcDifferenceColor( nCol1, nCol2, mnRows - 1 ) );

    for( sal_uInt16 i = nCol1 + mnColumns; i < nCol2; i = i + mnColumns )
    {
        aColor.SetRed( aColor.GetRed() + aDiffColor.GetRed() );
        aColor.SetGreen( aColor.GetGreen() + aDiffColor.GetGreen() );
        aColor.SetBlue( aColor.GetBlue() + aDiffColor.GetBlue() );

        SetItemColor( i, aColor );
        SetItemText( i, GetRGBString( aColor ) );
    }
}

// -----------------------------------------------------------------------
void ColorMixingControl::SetRows( sal_uInt16 nRows )
{
    mnRows = nRows;
}

// -----------------------------------------------------------------------
void ColorMixingControl::SetColumns( sal_uInt16 nColumns )
{
    mnColumns = nColumns;
}

// -----------------------------------------------------------------------
void ColorMixingControl::SetColor( CMCPosition ePos, const Color& rCol )
{
    if( rCol != maColor[ ePos ] )
    {
        maColor[ ePos ] = rCol;
        sal_uInt16 nPos = 0;
        sal_uInt16 nColumn = 0;
        String aStr( GetRGBString( rCol ) );

        switch( ePos )
        {
            case CMC_TOPLEFT:
                nPos = 1;
                nColumn = 0;
            break;

            case CMC_TOPRIGHT:
                nPos = mnColumns;
                nColumn = mnColumns - 1;
            break;

            case CMC_BOTTOMLEFT:
                nPos = ( mnRows - 1 ) * mnColumns + 1;
                nColumn = 0;
            break;

            case CMC_BOTTOMRIGHT:
                nPos = mnRows * mnColumns;
                nColumn = mnColumns - 1;
            break;
            case CMC_OTHER:
            break;  // -Wall not handled.
        }
        SetItemColor( nPos, rCol );
        SetItemText( nPos, aStr );
        FillColumn( nColumn );

        for( sal_uInt16 i = 0; i < mnRows; i++ )
            FillRow( i );
    }
}

// -----------------------------------------------------------------------
String ColorMixingControl::GetRGBString( const Color& rColor )
{
    String 	aStr( String::CreateFromInt32(rColor.GetRed()) );
            aStr += ' ';
            aStr += String::CreateFromInt32(rColor.GetGreen());
            aStr += ' ';
            aStr += String::CreateFromInt32(rColor.GetBlue());

    return aStr;
}
// -----------------------------------------------------------------------
CMCPosition	ColorMixingControl::GetCMCPosition() const
{
    CMCPosition ePos = CMC_OTHER;
    sal_uInt16 nPos = GetSelectItemId();

    if( nPos == 1 )
        ePos = CMC_TOPLEFT;
    else if( nPos == mnColumns )
        ePos = CMC_TOPRIGHT;
    else if( nPos == ( mnRows - 1 ) * mnColumns + 1 )
        ePos = CMC_BOTTOMLEFT;
    else if( nPos == mnRows * mnColumns )
        ePos = CMC_BOTTOMRIGHT;

    return( ePos );
}


// ------------
// - ColorHSB -
// ------------

// Erste Ansaetze gingen auf die Berechnung von Sven Hannover zurueck
// Der jetzige Algorithmus stammt im weitesten Sinne aus dem Foley/VanDam


/**************************************************************************
|*
|*    ColorHSB::ColorHSB()
|*
**************************************************************************/

ColorHSB::ColorHSB( const Color& rColor )
{
    sal_uInt8 c[3];
    sal_uInt8 cMax, cMin;

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

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

    // Brightness = max(R, G, B);
    mnBri = 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( mnBri > 0 )
        mnSat = cDelta * 100 / cMax;
    else
        mnSat = 0;

    if( mnSat == 0 )
        mnHue = 0; // Default = undefined
    else
    {
        double dHue = 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;
        }
        // else dHue = ???   -Wall   FIXME
        dHue *= 60.0;

        if( dHue < 0.0 )
            dHue += 360.0;

        mnHue = (sal_uInt16) dHue;
    }
}

/**************************************************************************
|*
|*    ColorHSB::GetRGB()
|*
**************************************************************************/

Color ColorHSB::GetRGB() const
{
    sal_uInt8 cR,cG,cB;
    sal_uInt8 nB = (sal_uInt8) ( mnBri * 255 / 100 );

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

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

        // #107375# Doing the calculation completely in floating
        // point, the former optimization gave sometimes negative
        // results for c and was pointless anyway
        sal_uInt8 a = static_cast<sal_uInt8>( nB * ( 100.0 - mnSat ) / 100.0 );
        sal_uInt8 b = static_cast<sal_uInt8>( nB * ( 100.0 - mnSat * f ) / 100.0 );
        sal_uInt8 c = static_cast<sal_uInt8>( nB * ( 100.0 - mnSat * ( 1.0 - f ) ) / 100.0 );

        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;
            default: cR = 0; 	cG = 0;		cB = 0;		break;  // -Wall ????
        }
    }

    return( Color( cR, cG, cB ) );
}

// ------------
// - ColorCMYK -
// ------------


// -----------------------------------------------------------------------
ColorCMYK::ColorCMYK( const Color& rColor )
{
    mnCyan    = 255 - rColor.GetRed();
    mnMagenta = 255 - rColor.GetGreen();
    mnYellow  = 255 - rColor.GetBlue();

    mnKey = Min( Min( mnCyan, mnMagenta ), mnYellow );

    mnCyan    = mnCyan - mnKey;
    mnMagenta = mnMagenta - mnKey;
    mnYellow  = mnYellow - mnKey;
}

// -----------------------------------------------------------------------
Color ColorCMYK::GetRGB() const
{
    int nTmp = Max( 0, 255 - ( mnCyan + mnKey ) );
    sal_uInt8 cR = (sal_uInt8) nTmp;
          nTmp = Max( 0, 255 - ( mnMagenta + mnKey ) );
    sal_uInt8 cG = (sal_uInt8) nTmp;
          nTmp = Max( 0, 255 - ( mnYellow + mnKey ) );
    sal_uInt8 cB = (sal_uInt8) nTmp;

    return( Color( cR, cG, cB ) );
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
