package com.kurukurupapa.tryandroidui.dialog;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 色選択ビュー
 *
 * 元ネタ<br>
 * Y.A.M の 雑記帳: Android　ColorPickerDialog を作った
 * http://y-anz-m.blogspot.com/2010/05/androidcolorpickerdialog.html
 */
public class HsvColorPicker extends View {
	public interface OnColorListener {
		void onColorChanging(int color);

		void onColorChanged(int color);
	}

	public enum Type {
		TRUE_COLOR, XX_COLOR
	}

	private Type type = Type.XX_COLOR;

	private Paint mPaint, mPaintC;
	private int[] mColors;
	private int[] mChroma;
	private OnColorListener mListener;
	private Shader sg, lg;
	private int selectColor;
	private float selectHue = 0;

	public HsvColorPicker(Context context) {
		super(context);
		setup();
	}

	public HsvColorPicker(Context context, AttributeSet attrs) {
		super(context, attrs);
		setup();
	}

	private void setup() {
		mColors = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF,
				0xFF00FF00, 0xFFFFFF00, 0xFFFF0000 };

		mChroma = new int[] { 0xFF000000, 0xFF888888, 0xFFFFFFFF };

		sg = new SweepGradient(0, 0, mColors, null);
		lg = new LinearGradient(OK_X0, 0, OK_X1, 0, mChroma, null,
				Shader.TileMode.CLAMP);

		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setShader(sg);
		mPaint.setStrokeWidth(CENTER_RADIUS);

		mPaintC = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaintC.setStyle(Paint.Style.FILL);
		mPaintC.setShader(lg);
		mPaintC.setStrokeWidth(2);

		setSelectColor(Color.RED);
	}

	public void setOnColorListener(OnColorListener l) {
		mListener = l;
	}

	public void setSelectColor(int color) {
		selectColor = color;
		selectHue = getHue(selectColor);
		invalidate();
	}

	public int getSelectColor() {
		return selectColor;
	}

	private boolean mTrackingOK;
	private boolean mHighlightOK;

	private static final int CENTER_X = 100;
	private static final int CENTER_Y = 100;
	private static final int CENTER_RADIUS = 24;
	private static final float OK_X0 = -CENTER_X / 2;
	private static final float OK_X1 = CENTER_X / 2;
	private static final float OK_Y0 = (float) (CENTER_X * 1.2);
	private static final float OK_Y1 = (float) (CENTER_X * 1.5);

	private void drawGradationOval(Canvas canvas, float r) {
		canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
	}

	private void drawBoxOval(Canvas canvas, float r) {
		int num = 12;

		// 色と色の隙間（単位：角度）
		final double SPACE = 2.0;
		final double HARF_SPACE = SPACE / 2.0;

		RectF rect = new RectF(-r, -r, r, r);
		double angle = 360 / num;

		Paint paint = new Paint();
		paint.setStyle(Paint.Style.FILL_AND_STROKE);
		paint.setStrokeWidth(CENTER_RADIUS);
		paint.setAntiAlias(true);

		for (int i = 0; i < num; i++) {

			// 描画する孤の位置を指定
			// 左：赤、上：黄、右：青、下：紺とする
			// 色と色の間には隙間を作る
			Path path = new Path();
			path.addArc(rect, Math.round(angle * (i - 0.5) + HARF_SPACE + 180),
					Math.round(angle - SPACE));

			// 彩度、明度は100%
			paint.setColor(Color.HSVToColor(new float[] { (float) (angle * i),
					1.0f, 1.0f }));

			canvas.drawPath(path, paint);
		}
	}

	private void drawGradationSVRegion(Canvas canvas) {
		final float RESOLUTION = (float) 0.01;

		for (float y = 0; y < 1; y += RESOLUTION) {
			mChroma = new int[10];

			int i = 0;
			for (float x = 0; i < 10; x += 0.1, i += 1) {
				mChroma[i] = setHSVColor(selectHue, x, y);
			}
			lg = new LinearGradient(OK_X0, 0, OK_X1, 0, mChroma, null,
					Shader.TileMode.CLAMP);
			mPaintC.setShader(lg);

			// canvas.drawRect(OK_X0, OK_X0 + (CENTER_X * y), OK_X1, OK_X0 +
			// (float)(CENTER_X * (y)), mPaintC);
			canvas.drawLine(OK_X0, OK_X0 + (CENTER_X * y), OK_X1, OK_X0
					+ (float) (CENTER_X * (y)), mPaintC);
		}
	}

	private void drawBoxSVRegion(Canvas canvas) {
		final int num = 5;
		final double SPACE = 2.0;
		final double HARF_SPACE = SPACE / 2.0;

		Paint paint = new Paint();
		paint.setStyle(Paint.Style.FILL);

		double dx = (OK_X1 - OK_X0) / num;
		double dy = dx;

		for (int y = 0; y < num; y++) {
			for (int x = 0; x < num; x++) {
				// 位置
				RectF rect = new RectF();
				rect.left = (float) (x * dx + OK_X0 + HARF_SPACE);
				rect.right = (float) ((x + 1) * dx + OK_X0 - HARF_SPACE);
				rect.top = (float) (OK_X1 - y * dy - HARF_SPACE);
				rect.bottom = (float) (OK_X1 - (y + 1) * dy + HARF_SPACE);

				// 色
				int color = setHSVColor(selectHue, (float) x / (num - 1),
						(float) y / (num - 1));
				paint.setColor(color);

				// 描画
				canvas.drawRect(rect, paint);
			}
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		float r = CENTER_X - mPaint.getStrokeWidth() * 0.5f;

		canvas.translate(CENTER_X, CENTER_X);

		if (type.equals(Type.TRUE_COLOR)) {
			drawGradationOval(canvas, r);
			drawGradationSVRegion(canvas);
		} else {
			drawBoxOval(canvas, r);
			drawBoxSVRegion(canvas);
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		setMeasuredDimension(CENTER_X * 2, CENTER_Y * 2);
	}

	private int floatToByte(float x) {
		int n = java.lang.Math.round(x);
		return n;
	}

	private int pinToByte(int n) {
		if (n < 0)
			n = 0;
		else if (n > 255)
			n = 255;
		return n;
	}

	private float getHue(int color) {
		float hsv[] = new float[3];
		Color.colorToHSV(color, hsv);
		return hsv[0];
	}

	private int ave(int s, int d, float p) {
		return s + java.lang.Math.round(p * (d - s));
	}

	private int interpColor(int colors[], float unit) {
		if (unit <= 0) {
			return colors[0];
		}
		if (unit >= 1) {
			return colors[colors.length - 1];
		}

		float p = unit * (colors.length - 1);
		int i = (int) p;
		p -= i;

		// now p is just the fractional part [0...1) and i is the index
		int c0 = colors[i];
		int c1 = colors[i + 1];
		int a = ave(Color.alpha(c0), Color.alpha(c1), p);
		int r = ave(Color.red(c0), Color.red(c1), p);
		int g = ave(Color.green(c0), Color.green(c1), p);
		int b = ave(Color.blue(c0), Color.blue(c1), p);

		return Color.argb(a, r, g, b);
	}

	private int rotateColor(int color, float rad) {
		float deg = rad * 180 / PI;
		int r = Color.red(color);
		int g = Color.green(color);
		int b = Color.blue(color);

		ColorMatrix cm = new ColorMatrix();
		ColorMatrix tmp = new ColorMatrix();

		cm.setRGB2YUV();
		tmp.setRotate(0, deg);
		cm.postConcat(tmp);
		tmp.setYUV2RGB();
		cm.postConcat(tmp);

		final float[] a = cm.getArray();

		int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b);
		int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b);
		int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);

		return Color.argb(Color.alpha(color), pinToByte(ir), pinToByte(ig),
				pinToByte(ib));
	}

	private int setHSVColor(float hue, float saturation, float value) {
		float[] hsv = new float[3];
		if (hue >= 360)
			hue = 359;
		else if (hue < 0)
			hue = 0;

		if (saturation > 1)
			saturation = 1;
		else if (saturation < 0)
			saturation = 0;

		if (value > 1)
			value = 1;
		else if (value < 0)
			value = 0;

		hsv[0] = hue;
		hsv[1] = saturation;
		hsv[2] = value;

		return Color.HSVToColor(hsv);
	}

	private static final float PI = 3.1415927f;

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX() - CENTER_X;
		float y = event.getY() - CENTER_Y;
		float r = (float) (java.lang.Math.sqrt(x * x + y * y));
		boolean inOK = false;
		boolean inOval = false;
		boolean inRect = false;

		if (r <= CENTER_X) {
			if (r > CENTER_X - CENTER_RADIUS)
				inOval = true;
			else if (x >= OK_X0 && x < OK_X1 && y >= OK_X0 && y < OK_X1)
				inRect = true;
		} else if (x >= OK_X0 && x < OK_X1 && y >= OK_Y0 && y < OK_Y1) {
			inOK = true;
		}

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mTrackingOK = inOK;
			if (inOK) {
				mHighlightOK = true;
				invalidate();
				break;
			}
			// 続けてACTION_MOVEの処理を実施する

		case MotionEvent.ACTION_MOVE:
			if (mTrackingOK) {
				if (mHighlightOK != inOK) {
					mHighlightOK = inOK;
					invalidate();
				}
			} else if (inOval) {
				float angle = (float) java.lang.Math.atan2(y, x);
				// need to turn angle [-PI ... PI] into unit [0....1]
				float unit = angle / (2 * PI);
				if (unit < 0) {
					unit += 1;
				}
				selectColor = interpColor(mColors, unit);
				// mChroma[1] = selectColor;
				selectHue = getHue(selectColor);
				// lg = new LinearGradient(OK_X0, 0, OK_X1, 0, mChroma,
				// null, Shader.TileMode.CLAMP);
				// mPaintC.setShader(lg);
				invalidate();
			} else if (inRect) {
				int selectColor2 = setHSVColor(selectHue, (x - OK_X0)
						/ CENTER_X, (y - OK_X0) / CENTER_Y);
				selectColor = selectColor2;
				invalidate();
			}

			// リスナーへ通知
			if (mListener != null) {
				mListener.onColorChanging(selectColor);
			}
			break;

		case MotionEvent.ACTION_UP:
			if (mTrackingOK) {
				if (inOK) {
					mListener.onColorChanged(selectColor);
				}
				mTrackingOK = false; // so we draw w/o halo
				invalidate();
			}

			// リスナーへ通知
			if (mListener != null) {
				mListener.onColorChanged(selectColor);
			}
			break;
		}
		return true;
	}

}
