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

/**
 * HSL(HLS または HSI)での色表現。
 * {@link Color}クラスへの入出力として使用可能です。<br>
 * 色相・彩度・輝度で表されます。
 * 色相は以下ようなマンセル色相環になります。
 * <table>
 * <tbody>
 * <tr>
 * <td align="center"></td>
 * <td align="center"bgcolor="#FF0080">330°<br>#FF0080</td>
 * <td align="center"bgcolor="#FF0000">0°<br>#FF0000</td>
 * <td align="center"bgcolor="#FF8000">30°<br>#FF8000</td>
 * <td align="center"></td>
 * </tr>
 * <tr>
 * <td align="center"bgcolor="#FF00FF">300°<br>#FF00FF</td>
 * <td align="center"></td>
 * <td align="center"></td>
 * <td align="center"></td>
 * <td align="center"bgcolor="#FFFF00">60°<br>#FFFF00</td>
 * </tr>
 * <tr>
 * <td align="center"bgcolor="#8000FF"style="color:white;">270°<br>#8000FF</td>
 * <td align="center"></td>
 * <td align="center">色相</td>
 * <td align="center"></td>
 * <td align="center"bgcolor="#80FF00">90°<br>#80FF00</td>
 * </tr>
 * <tr>
 * <td align="center"bgcolor="#0000FF"style="color:white;">240°<br>#0000FF</td>
 * <td align="center"></td>
 * <td align="center"></td>
 * <td align="center"></td>
 * <td align="center"bgcolor="#00FF00">120°<br>#00FF00</td>
 * </tr>
 * <tr>
 * <td align="center"></td>
 * <td align="center"bgcolor="#0080FF"style="color:white;">210°<br>#0080FF</td>
 * <td align="center"bgcolor="#00FFFF">180°<br>#00FFFF</td>
 * <td align="center"bgcolor="#00FF80">150°<br>#00FF80</td>
 * <td align="center"></td>
 * </tr>
 * </tbody>
 * </table>
 * <dl>
 * <dt>類似色
 * <dd>0°から見た、330°、30°のような色。調和しやすい色になります。
 * <dt>補色(反対色)
 * <dd>0°から見た180°のような色。<br>
 * 例えばHTML背景色と配置画像を補色関係にすれば、画像が鮮明になります。
 * <dt>暖色・寒色
 * <dd>270°～ 0°～ 60°が暖色、90°～ 180°～ 240°が寒色になります。<br>
 * 寒色は遠くに見えるため、背景色に適しています。
 * </dl>
 */
public class HSL {
	/**
	 * 色相。値の範囲は、0 ～ 359 の角度表現です。<br>
	 */
	public int hue;
	/**
	 * 彩度。値の範囲は、0 ～ 100 です。<br>
	 * 100は純色、0は灰色です。
	 */
	public int saturation;
	/**
	 * 輝度。値の範囲は、0 ～ 100 です。<br>
	 * 0は黒、100を白、50が純色です。
	 */
	public int lightness;

	/**
	 * コンストラクタ。
	 * @param h 色相。
	 * @param s 彩度。
	 * @param l 輝度。
	 */
	public HSL( int h, int s, int l ) {
		hue = round( h );
		saturation = per( s );
		lightness = per( l );
	}

	private static int round( int r ) {
		while( true ) {
			if ( r < 0 )	r += 360;
			if ( r >= 360 )	r -= 360;
			if ( r >= 0 && r < 360 )	return r;
		}
	}

	private static int per( int p ) {
		if ( p < 0 )	return 0;
		if ( p > 100 )	return 100;
		return p;
	}

	private static int rgb_to_per( int v ) {
		return per( v * 100 / 256 );
	}

	private static int per_to_rgb( int v ) {
		v = v * 256 / 100;
		if ( v > 255 )	v = 255;
		if ( v < 0 )	v = 0;
		return v;
	}

	private static int max( int ... v ) {
		int	m = v[0];
		for ( int i = 1; i < v.length; i++ ) {
			if ( m < v[i] )	m = v[i];
		}
		return m;
	}

	private static int min( int ... v ) {
		int	m = v[0];
		for ( int  i = 1; i < v.length; i++ ) {
			if ( m > v[i] )	m = v[i];
		}
		return m;
	}

	private static int toC( int v, int max, int min ) {
		return (max - v) / (max - min);
	}

	static HSL getHSL( Color c ) {
		int	r = rgb_to_per( c.getRed() );
		int	g = rgb_to_per( c.getGreen() );
		int	b = rgb_to_per( c.getBlue() );
		int	max = max( r, g, b );
		int	min = min( r, g, b );
		int	l = (max + min) / 2, h = 0, s = 0;
		if ( max != min ) {
			s = ( l > 50 )?
				(max - min) / ( 2 - max - min ):
				(max - min) / ( max + min );
			int	cr = toC( r, max, min );
			int	cg = toC( g, max, min );
			int	cb = toC( b, max, min );
			if ( r == max ) {
				h = cb - cg;
			}
			else if ( g == max ) {
				h = 2 + cr - cb;
			}
			else {
				h = 4 + cg - cr;
			}
		}
		return new HSL( h, s, l );
	}

	private static int getH( int h, int max, int min ) {
		h = round( h );
		if ( h < 60 )	return min + (max - min) * h / 60;
		if ( h < 180 )	return max;
		if ( h < 240 )	return min + (max - min) * (240 - h) / 60;
		return min;
	}

	void toColor( Color c ) {
		int	h = round( hue );
		int	s = per( saturation );
		int	l = per( lightness );
		if ( s == 0 ) {
			l = per_to_rgb( l );
			c.setColor( l, l, l );
			return;
		}
		int	max = (l > 50)?	l * (100 - s) + s:	l * ( 100 + s );
		int	min = 2 * l - max;
		int	r = per_to_rgb( getH( h + 120, max, min ) );
		int	g = per_to_rgb( getH( h, max, min ) );
		int	b = per_to_rgb( getH( h - 120, max, min ) );
		c.setColor( r, g, b );
	}

}

