/*
 * Paraselene
 * Copyright (c) 2009, 2010  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
package paraselene;

import java.net.*;
import java.io.*;
import java.util.*;
import paraselene.tag.*;
import paraselene.css.*;

/**
 * 色を示す文字列。<br>
 * 文字列から色を設定する場合、以下のフォーマットを解析できます。
<ul>
  <li>{@link WebColor} で定義されているカラーネーム
  <li>#rrggbb
  <li>#rgb
  <li>RGB(r, g, b)
  <li>RGBa(r, g, b, a)
  <li>HSL(h, s, l)
  <li>HSLa(h, s, l, a)
</ul>
 */
public class Color extends PlainText implements CSSValuable, AttributeValuable {
	private static final long serialVersionUID = 2L;
	private int red = -1, green = -1, blue = -1;
	private double alpha = 1;

	/**
	 * 複製の作成。
	 * @return 複製。
	 */
	public HTMLPart getReplica() {
		return new Color( this );
	}

	/**
	 * コンストラクタ。
	 * @param rgb Color。
	 */
	public Color( Color rgb ) {
		setColor( rgb );
	}

	/**
	 * 色の設定。
	 * @param rgb Color。
	 */
	public void setColor( Color rgb ) {
		setColor( rgb.getRed(), rgb.getGreen(), rgb.getBlue(), getAlpha() );
	}

	/**
	 * コンストラクタ。
	 * @param s 文字列。
	 */
	public Color( String s ) {
		setText( s );
	}

	static int toColor( NumberValue num ) {
		if ( num.getUnit() == NumberValue.Unit.PERCENTAGE ) {
			num.multiply( 256 ).divide( 100 );
		}
		int	v = num.toInteger();
		if ( v > 256 )	v = 255;
		return v;
	}

	private boolean set_rgb_text( String str ){
		String[]	all = str.split( "[\\(\\)]" );
		if ( all.length != 2 )	return false;
		if ( !"rgb".equalsIgnoreCase( all[0].trim() ) )	return false;
		String[]	rgb = all[1].split( "," );
		if ( rgb.length != 3 || rgb.length != 4 )	return false;
		NumberValue[]	num = new NumberValue[4];
		num[3] = new NumberValue( 1 );
		for ( int i = 0; i < rgb.length; i++ ) {
			num[i] = new NumberValue( rgb[i] );
		}
		setColor( toColor( num[0] ), toColor( num[1] ), toColor( num[2] ), num[3].toDouble() );
		return true;
	}

	private boolean set_hsl_text( String str ) {
		String[]	all = str.split( "[\\(\\)]" );
		if ( all.length != 2 )	return false;
		if ( !"hsl".equalsIgnoreCase( all[0].trim() ) )	return false;
		String[]	hsl = all[1].split( "," );
		if ( hsl.length != 3 || hsl.length != 4 )	return false;
		NumberValue[]	num = new NumberValue[3];
		num[3] = new NumberValue( 1 );
		for ( int i = 0; i < hsl.length; i++ ) {
			num[i] = new NumberValue( hsl[i] );
		}
		HSL	h = new HSL( num[0].toInteger(), num[1].toInteger(), num[2].toInteger() );
		setColor( h, num[3].toDouble() );
		return true;
	}

	private static HashMap<String, String>	color_name = new HashMap<String, String>();
	static {
		color_name.put( "GREY", "GRAY" );
		color_name.put( "DARKGREY", "DARKGRAY" );
		color_name.put( "DARKSLATEGREY", "DARKSLATEGRAY" );
		color_name.put( "DIMGREY", "DIMGRAY" );
		color_name.put( "LIGHTGREY", "LIGHTGRAY" );
		color_name.put( "LIGHTSLATEGREY", "LIGHTSLATEGRAY" );
	}

	/**
	 * 色の設定。
	 * @param s 文字列。
	 */
	public void setText( String s ) {
		if ( s == null ) {
			super.setText( s );
			return;
		}
		s = s.trim();
		if ( s.isEmpty() ) {
			super.setText( s );
			return;
		}
		if ( set_rgb_text( s ) )	return;
		if ( set_hsl_text( s ) )	return;
		if ( s.charAt( 0 ) != '#' ) {
			String	alias = color_name.get( s.toUpperCase() );
			if ( alias != null )	s = alias;
			setColor( WebColor.valueOf( s.toUpperCase() ) );
			return;
		}
		char[]	ch = s.substring( 1 ).toCharArray();
		int[]	rgb = new int[3];
		if ( ch.length == 3 ) {
			for ( int i = 0; i < 3; i++ ) {
				StringBuilder	buf = new StringBuilder( ch[i] );
				buf = buf.append( ch[i] );
				rgb[i] = Integer.parseInt( buf.toString(), 16 );
			}
		}
		else {
			for ( int i = 0; i < 3; i++ ) {
				rgb[i] = Integer.parseInt( new String( ch, i * 2, 2 ), 16 );
			}
		}
		setColor( rgb[0], rgb[1], rgb[2] );
	}

	/**
	 * コンストラクタ。
	 * @param wc 規定色。
	 */
	public Color( WebColor wc ) {
		setColor( wc );
	}

	/**
	 * 色の設定。
	 * @param wc 規定色。
	 */
	public void setColor( WebColor wc ) {
		setColor( wc, alpha );
	}

	/**
	 * コンストラクタ。
	 * @param wc 既定色。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public Color( WebColor wc, double a ) {
		setColor( wc, a );
	}

	/**
	 * 色の設定。
	 * @param wc 既定色。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public void setColor( WebColor wc, double a ) {
		int	r = wc.red;
		int	g = wc.green;
		int	b = wc.blue;
		setColor( wc.red, wc.green, wc.blue, a );
		if ( r >= 0 && g >= 0 && b >= 0 )	return;
		super.setText( wc.name() );
	}

	/**
	 * コンストラクタ。全てのパラメータは下位１バイトのみ取り出して使用する。
	 * @param r 赤(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param g 緑(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param b 青(0x00 ～ 0xff)。負数の場合、transparent となります。
	 */
	public Color( int r, int g, int b ) {
		setColor( r, g, b );
	}

	/**
	 * コンストラクタ。
	 * @param r 赤(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param g 緑(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param b 青(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public Color( int r, int g, int b, double a ) {
		setColor( r, g, b, a );
	}

	/**
	 * 色の設定。全てのパラメータは下位１バイトのみ取り出して使用します。
	 * @param r 赤(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param g 緑(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param b 青(0x00 ～ 0xff)。負数の場合、transparent となります。
	 */
	public void setColor( int r, int g, int b ) {
		setColor( r, g, b, alpha );
	}

	/**
	 * 透明度の設定。RGB値は変化しません。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public void setAlpha( double a ) {
		if ( red < 0 || green < 0 || blue < 0 )	return;
		setColor( red, green, blue, a );
	}

	/**
	 * 色の設定。RGBは下位１バイトのみ取り出して使用します。
	 * @param r 赤(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param g 緑(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param b 青(0x00 ～ 0xff)。負数の場合、transparent となります。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public void setColor( int r, int g, int b, double a ) {
		red = r & 0xff;
		green = g & 0xff;
		blue = b & 0xff;
		alpha = a;
		if ( alpha < 0 )	alpha = 0;
		if ( alpha > 1 )	alpha = 1;
		if ( red < 0 || green < 0 || blue < 0 || alpha == 0 ) {
			super.setText( "TRANSPARENT" );
			return;
		}
		if ( alpha == 1 ) {
			super.setText( String.format( "#%06X", (red << (8*2)) | (green << 8) | blue ) );
		}
		else {
			super.setText( String.format( "rgba(%d,%d,%d,%f)",
				getRed(),
				getGreen(),
				getBlue(),
				alpha
			) );
		}
	}

	/**
	 * 赤の取得。
	 * @return 赤。transparent の場合、-1 を返します。
	 */
	public int getRed() {
		return red;
	}

	/**
	 * 緑の取得。
	 * @return 緑。transparent の場合、-1 を返します。
	 */
	public int getGreen() {
		return green;
	}

	/**
	 * 青の取得。
	 * @return 青。transparent の場合、-1 を返します。
	 */
	public int getBlue() {
		return blue;
	}

	/**
	 * 透明度の取得。
	 * @return 透明度。
	 */
	public double getAlpha() { return alpha; }

	/**
	 * YUVの取得。
	 * @return YUV変換した値。
	 */
	public YUV getYUV() {
		return YUV.getYUV( this );
	}

	/**
	 * コンストラクタ。
	 * 全てのパラメータは下位１バイトのみ取り出して使用します。
	 * @param yuv YUV。
	 */
	public Color( YUV yuv ) {
		setColor( yuv );
	}

	/**
	 * コンストラクタ。
	 * 全てのパラメータは下位１バイトのみ取り出して使用します。
	 * @param yuv YUV。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public Color( YUV yuv, double a ) {
		setColor( yuv, a );
	}

	/**
	 * 色の設定。
	 * 全てのパラメータは下位１バイトのみ取り出して使用します。
	 * @param yuv YUV。
	 */
	public void setColor( YUV yuv ) {
		yuv.toColor( this );
	}

	/**
	 * 色の設定。
	 * 全てのパラメータは下位１バイトのみ取り出して使用します。
	 * @param yuv YUV。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public void setColor( YUV yuv, double a ) {
		yuv.toColor( this );
		setAlpha( a );
	}

	/**
	 * HSLの取得。
	 * @return HSL変換した値。
	 */
	public HSL getHSL() {
		return HSL.getHSL( this );
	}

	/**
	 * コンストラクタ。
	 * @param hsl HSL。
	 */
	public Color( HSL hsl ) {
		setColor( hsl );
	}

	/**
	 * 色の設定。
	 * @param hsl HSL。
	 */
	public void setColor( HSL hsl ) {
		hsl.toColor( this );
	}

	/**
	 * コンストラクタ。
	 * @param hsl HSL。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public Color( HSL hsl, double a ) {
		setColor( hsl, a );
	}

	/**
	 * 色の設定。
	 * @param hsl HSL。
	 * @param a 透明度(1 ～ 0)。0は表示されなくなります。
	 */
	public void setColor( HSL hsl, double a ) {
		hsl.toColor( this );
		setAlpha( a );
	}
}

