package jp.ehobby.ui.gui.graph.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

import jp.ehobby.ui.gui.graph.model.BarGraphModelIf;
import jp.ehobby.ui.gui.graph.model.DefaultBarGraphModel;


/**
 * 棒グラフ View.
 * @author kei-n
 */
public class BarGraphView extends AbstractGraphView {

	/** シリアルバージョンUID.	*/
	private static final long serialVersionUID = 3109586589260726692L;

	/** 破線パターン.			*/
	private static final float[] DASH = { 3.0f, 3.0f };

	/** 破線.					*/
	private static final BasicStroke DASH_STROKE = new BasicStroke(
			0.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, DASH, 10.0f);

	/** デフォルトの色.			*/
	private static final Color[] DEFAULT_COLORS = {
		Color.RED,
		Color.BLUE,
		Color.YELLOW,
		Color.GREEN,
		Color.PINK,
		Color.CYAN,
		Color.ORANGE,
		Color.GRAY
	};


	/** グラフデータ.			*/
	private final List<BarGraphModelIf> graphData;


	/**
	 * BarGraphView を構築します.
	 */
	public BarGraphView() {
		this.graphData  = new ArrayList<>();
		int[] m = { 10, 120, 100, 50 };
		setMargin(m);
	}


	/**
	 * データを追加します.
	 *
	 * @param data 追加するデータ
	 */
	public void addData(final BarGraphModelIf data) {
		this.graphData.add(data);
	}


	/**
	 * 描画時に実行されます.
	 *
	 * @param g Graphics
	 */
	@Override
	public void paintGraph(Graphics2D g) {
		// グラフ枠の生成
		paintGraphFrame(g);

		// グラフデータの描画
		paintGraphData(g);
	}


	/**
	 * グラフの枠を設定します.
	 *
	 * @param g Graphics
	 */
	private void paintGraphFrame(final Graphics2D g) {
		// 縦軸の作成
		int height = this.graphScale.height / 10;

		g.setColor(Color.BLACK);
		g.setStroke(DASH_STROKE);
		int minX = this.graphScale.x;
		int maxX = this.graphScale.width + this.graphScale.x;
		for (int y = this.graphScale.y + height; y < this.graphScale.y + this.graphScale.width; y += height) {
			lineTo(g, minX, y, maxX, y);
			g.drawString(String.valueOf(y), (int) toRealPointX(minX) - 30, (int) toRealPointY(y));
		}

		// 0 の部分は実践で引く
		g.setStroke(new BasicStroke());
		lineTo(g, minX, 0, maxX, 0);
		g.drawString(String.valueOf(0), (int) toRealPointX(minX) - 30, (int) toRealPointY(0));
	}


	/**
	 * グラフのデータを描画します.
	 *
	 * @param g Graphics
	 */
	// 超ながくてすみませぬ.
	private void paintGraphData(final Graphics2D g) {
		int size = this.graphData.size();
		if (size == 0) { return; }

		//        +---+    +--+--+
		//    +---+   |    |  |  |
		//    |   |   |    |  |  |
		//--------------------------
		// |<-- 1 --->|<-- 1 --->|
		//    |<2>|
		// 1. blockWidth
		// 2. barWidth
		BarGraphModelIf data = this.graphData.get(0);
		int blockWidth = this.graphScale.width / (data.size() + 1);
		int barWidth   = (blockWidth / 4) / size;

		// 棒グラフを描画
		paintBarGraph(g, blockWidth, barWidth);

		// ラベルを描画
		paintBarGraphLabel(g, blockWidth, barWidth);

		// 凡例を描画
		printBarGraphExample(g);
	}


	/**
	 * 棒グラフの棒を描画します.
	 *
	 * @param g Graphics2D
	 * @param blockWidth ブロック幅
	 * @param barWidth   棒の幅
	 */
	private void paintBarGraph(final Graphics2D g, int blockWidth, int barWidth) {

		// 初めの棒を描く位置調整用
		double firstX  = blockWidth * 0.75;

		for (int kei = 0; kei < this.graphData.size(); kei++) {
			double drawX = firstX + this.graphScale.x + (kei * barWidth);
			BarGraphModelIf data = this.graphData.get(kei);
			for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) {
				// 値 = 棒グラフの高さ
				double height = data.getData(dataIndex);
				Color  color  = getColor(kei, data);
				fillRect(g, drawX, 0, barWidth, height, color, Color.BLACK);
				drawX += blockWidth;
			}
		}
	}


	/**
	 * 棒グラフのラベルを描画します.
	 *
	 * @param g Graphics2D
	 * @param blockWidth ブロック幅
	 * @param barWidth   棒の幅
	 */
	private void paintBarGraphLabel(final Graphics2D g, int blockWidth, int barWidth) {

		// 初めの棒を描く位置調整用
		double firstX  = blockWidth * 0.75;

		// 文字回転処理
		AffineTransform af   = new AffineTransform();
		AffineTransform bkup = g.getTransform();

		// kei = 0 の値でラベルを描画する.
		int             kei    = 0;
		double          drawX  = firstX + this.graphScale.x + (kei * barWidth);
		BarGraphModelIf data   = this.graphData.get(kei);
		for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) {
			String label = data.getDataLabel(dataIndex);
			int    realX = (int) toRealPointX(drawX);
			int    realY = (int) toRealPointY(-10);
			af.setToRotation(30 * Math.PI / 180, realX, realY);
			g.setTransform(af);
			g.drawString(label, realX, realY);
			drawX += blockWidth;
		}

		// 回転処理をもとに戻しておく
		g.setTransform(bkup);
	}


	/**
	 * 凡例を描画します.
	 *
	 * @param g
	 */
	private void printBarGraphExample(final Graphics2D g) {
		//
		// +--------------------------------+
		// | +----------------+(minX, minY) |
		// | |                |             |
		// | |                |             |
		// | +----------------+(minX, maxY) |
		// +--------------------------------+
		int minX = this.graphArea.x + this.graphArea.width;
		int minY = this.graphArea.y;
		int maxY = this.graphArea.y + this.graphArea.height;

		int hanreiX = minX + 20;
		int hanreiH = (maxY - minY) / (this.graphData.size() + 1);
		int hanreiY = minY + hanreiH;
		int hanreiW = hanreiH / 5;

		for (int kei = 0; kei < this.graphData.size(); kei++) {
			BarGraphModelIf data      = this.graphData.get(kei);
			String          dataName  = data.getDataName();
			Color           dataColor = getColor(kei, data);

			g.setColor(dataColor);
			g.fillRect(hanreiX,  hanreiY, 10, hanreiW);
			g.setColor(Color.BLACK);
			g.drawRect(hanreiX, hanreiY, 10, hanreiW);
			g.drawString(dataName, hanreiX + 10 + 10, hanreiY + hanreiW);
			hanreiY += hanreiH;
		}
	}


	/**
	 * 色を取得する.
	 *
	 * @param kei  系
	 * @param data データ
	 * @return 色
	 */
	private static Color getColor(final int kei, final BarGraphModelIf data) {
		Color color = data.getColor();
		if (color == null) {
			color = DEFAULT_COLORS[kei % DEFAULT_COLORS.length];
		}
		return color;
	}


	public static void main(final String[] args) {
		JFrame frame = new JFrame();

		double[] d = {
				10, 20, 30, 40, 50
		};
		double[] d2 = {
				17, 25, 66, 40, 30
		};
		String[] l = {
				"2012/12/22 (Mon)",
				"2012/12/23 (Tus)",
				"2012/12/23 (Tus)",
				"2012/12/23 (Tus)",
				"2012/12/23 (Tus)"
		};
		DefaultBarGraphModel data = new DefaultBarGraphModel("NAME");
		data.setData(d);
		data.setDataLabel(l);
		DefaultBarGraphModel data2 = new DefaultBarGraphModel("NAME");
		data2.setData(d2);

		Rectangle  rect = new Rectangle();
		rect.x      =  0;
		rect.y      =  0;
		rect.width  =  200;
		rect.height =  200;

		BarGraphView view = new BarGraphView();
		view.setBackgroundColor(Color.WHITE);
		view.addData(data);
		view.addData(data2);

		view.setGraphScale(rect);

		frame.getContentPane().add(view);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setBounds(10, 10, 300, 200);
		frame.setVisible(true);

	}
}

