/*
 * ColourPreferenceMapper.java
 *
 * Created on 26 August 2002, 16:02
 */

package org.bodington.util;

import java.awt.Color;
import java.util.StringTokenizer;

/**
 *
 * @author  administrator
 */
public class ColourPreferenceMapper
{
    public static final int TYPE_NORMAL         = 0;
    public static final int TYPE_SOFT           = 1;
    public static final int TYPE_CONTRAST       = 2;
    public static final int TYPE_BLACK_ON_WHITE = 3;
    public static final int TYPE_WHITE_ON_BLACK = 4;
    
    Color reference_background = Color.white;
    Color reference_foreground = Color.black;
    
    boolean dark_background;
    Color adjusted_background;
    
    
    int type = TYPE_NORMAL;
    boolean valid = false;
    
    /** Creates a new instance of ColourPreferenceMapper */
    public ColourPreferenceMapper()
    {
    }

    public ColourPreferenceMapper( String code )
    {
        if ( code!=null )
        {
            StringTokenizer t = new StringTokenizer( code, "." );
            try
            {
                if ( t.hasMoreTokens() )
                    type = Integer.decode( t.nextToken() ).intValue();
                if ( type<TYPE_NORMAL || type>TYPE_WHITE_ON_BLACK )
                    type = TYPE_NORMAL;
                if ( t.hasMoreTokens() )
                    reference_background =  Color.decode( t.nextToken() );
                if ( t.hasMoreTokens() )
                    reference_foreground =  Color.decode( t.nextToken() );
            }
            catch ( NumberFormatException nfe )
            {
                // just leave properties at default if code is invalid
            }
        }
    }

    public synchronized void setType( int n )
    {
        type = n;
        valid = false;
    }

    public synchronized void setReferenceColours( Color b, Color f )
    {
    reference_background = b;
    reference_foreground = f;
    valid = false;
    }

    public synchronized Color getReferenceBackgroundColor()
    {
        return reference_background;
    }
    
    public synchronized Color getReferenceForegroundColor()
    {
        return reference_foreground;
    }
    
    public synchronized Color getPreferredBackgroundColor()
    {
        if ( !valid )
            readjust();
        
        return adjusted_background;
    }
    
    public synchronized Color getPreferredForegroundColor( Color input )
    {
        if ( !valid )
            readjust();
        
        if ( type == TYPE_NORMAL )
            return input;
        if ( type == TYPE_BLACK_ON_WHITE )
            return Color.black;
        if ( type == TYPE_WHITE_ON_BLACK )
            return Color.white;

        float value_diff;
        float[] col_components_f = new float[3], col_components_b = new float[3];

        Color.RGBtoHSB( 
                adjusted_background.getRed(), 
                adjusted_background.getGreen(), 
                adjusted_background.getBlue(),
                col_components_b );
        Color.RGBtoHSB( 
                input.getRed(), 
                input.getGreen(), 
                input.getBlue(),
                col_components_f );

        value_diff = col_components_f[2] - col_components_b[2];

        if ( type == TYPE_CONTRAST )
        {
            if ( dark_background )
            {
                if ( value_diff < 0.6 )
                    col_components_f[2] = col_components_b[2] + (float)0.6;
            }
            else
            {
                if ( value_diff > -0.6 )
                    col_components_f[2] = col_components_b[2] - (float)0.6;
            }
        }
        
        if ( type == TYPE_SOFT )
        {
            if ( col_components_f[1] > 0.5 )
                col_components_f[1] = (float)0.5;
                
            if ( dark_background )
            {
                if ( value_diff > 0.4 )
                    col_components_f[2] = col_components_b[2] + (float)0.4;
            }
            else
            {
                if ( value_diff < -0.4 )
                    col_components_f[2] = col_components_b[2] - (float)0.4;
            }
        }
        
    return Color.getHSBColor( col_components_f[0], col_components_f[1], col_components_f[2] );        
    }
    
    private synchronized void readjust()
    {
        valid = true;
        
        if ( type == TYPE_NORMAL )
        {
            adjusted_background = reference_background;
            return;
        }
        if ( type == TYPE_BLACK_ON_WHITE  )
        {
            adjusted_background = Color.white;
            return;
        }
        if ( type == TYPE_WHITE_ON_BLACK )
        {
            adjusted_background = Color.black;
            return;
        }

        float value_diff;
        float[] col_components_f = new float[3], col_components_b = new float[3];

        Color.RGBtoHSB( 
                reference_background.getRed(), 
                reference_background.getGreen(), 
                reference_background.getBlue(),
                col_components_b );
        Color.RGBtoHSB( 
                reference_foreground.getRed(), 
                reference_foreground.getGreen(), 
                reference_foreground.getBlue(),
                col_components_f );

        dark_background = col_components_b[2] < col_components_f[2];
        
        // work out if background needs to be adjusted to increase
        // contrast or reduce its colour saturation
        value_diff = col_components_f[2] - col_components_b[2];
        if ( type == TYPE_CONTRAST )
        {
            if ( dark_background )
            {
                if ( (col_components_b[2] + 0.6) <= 1.0 )
                    {
                        adjusted_background = reference_background;
                        return;
                    }
                col_components_b[2] = (float)0.4;
            }
            else
            {
                if ( (col_components_b[2] - 0.6) >= 0.0 )
                    {
                        adjusted_background = reference_background;
                        return;
                    }
                col_components_b[2] = (float)0.6;
            }
        }
        
        if ( type == TYPE_SOFT )
        {
            if ( col_components_b[1] < 0.3 )
            {
                adjusted_background = reference_background;
                return;
            }
                
            // reduce saturated colours
            if ( col_components_b[1] > 0.3 )
                col_components_b[1] = (float)0.3;
        }

        // convert altered colors back to RGB
        adjusted_background = Color.getHSBColor( col_components_b[0], col_components_b[1], col_components_b[2] );
    }
    
    
    /** Creates a string code to represent the content of the instance */
    public String toString()
    {
        StringBuffer code = new StringBuffer();
        code.append( Integer.toHexString( type ) );
        code.append( ".0x" );
        code.append( Integer.toHexString( reference_background.getRGB() & 0xffffff ) );
        code.append( ".0x" );
        code.append( Integer.toHexString( reference_foreground.getRGB() & 0xffffff ) );
        
        return code.toString();
    }
}
