package com.ht.ui.energy.sum;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Popup;
import javafx.stage.Screen;
import javafx.util.StringConverter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ht.tags.model.index.IndexTreeNode;
import com.ht.ui.controls.MessageInfoLabel;
import com.ht.ui.controls.calendar.FXCalendar;
import com.ht.ui.energy.util.EnergyConverterUtil;
import com.ht.ui.util.HessianResponseTask;
import com.ht.ui.util.UserSecurityUtil;
import com.ht.util.DateType;
import com.ht.util.UserSession;
import com.ht.web.hessian.HessianFactoryManger;
import com.ht.web.mongo.domain.User;
import com.ht.web.mongo.domain.YMHistoryData;
import com.ht.web.remoting.HessianResponse;

public class YMHistoryDataViewer extends Group {
	private static final int chartTickUnit = 12;

	private static final Logger log = LoggerFactory
			.getLogger(YMHistoryDataViewer.class);

	private VBox container = new VBox(5);
	private XYChart.Series<Number, Number> series;
	private LineChart<Number, Number> chart;
	private FXCalendar fxCalendar;
	private Popup popup;

	private TextField textStart;
	private TextField textEnd;
	private TextField selText = null;

	private MessageInfoLabel message;
	private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

	private String energyIndex;
	private List<String> normalIndex;
	private DateType dateType;

	private NumberAxis xAxis;
	private Calendar calStart;
	private Calendar calEnd;

	private SimpleDateFormat sdf_h = new SimpleDateFormat("MM-dd\nHH:00");
	private SimpleDateFormat sdf_d = new SimpleDateFormat("MM-dd");
	private SimpleDateFormat sdf_m = new SimpleDateFormat("yyyy-MM");

	private final double timeUnit = 60 * 60 * 1000;

	public YMHistoryDataViewer() {
		// super(5);

		container.getStyleClass().addAll("window", "dataCurve");
		container.setAlignment(Pos.TOP_CENTER);
		getChildren().add(container);

		Rectangle2D rect = Screen.getPrimary().getVisualBounds();
		double maxHeight = rect.getHeight() - 270;
		double width = rect.getWidth() * 0.618;
		container.setPrefSize(width, maxHeight);
		container.setMaxSize(width, maxHeight);

		// block mouse clicks
		setOnMouseClicked(new EventHandler<MouseEvent>() {

			@Override
			public void handle(MouseEvent t) {
				t.consume();
			}
		});

		createTopContent();

		createChart();

		createMessageLabel();
		// getChildren().add(fxCalendar);
	}

	private void createTopContent() {

		createPopup();

		HBox hbox = new HBox(5);
		hbox.setAlignment(Pos.CENTER_LEFT);
		hbox.getChildren().add(new Label(" 日期范围:"));

		calEnd = Calendar.getInstance();
		calEnd.set(Calendar.HOUR_OF_DAY, 0);
		calStart = (Calendar) calEnd.clone();
		calStart.add(Calendar.DATE, -6);

		textStart = new TextField(dateFormat.format(calStart.getTime()));
		textStart.setPrefWidth(110);
		textStart.setEditable(false);
		textStart.setUserData(calEnd.getTime());
		textStart.setOnMouseClicked(new EventHandler<MouseEvent>() {
			public void handle(MouseEvent event) {
				selText = textStart;
				popup.show(textStart, event.getScreenX(), event.getScreenY());
			};
		});
		hbox.getChildren().add(textStart);

		Separator separator = new Separator(Orientation.HORIZONTAL);
		hbox.getChildren().add(separator);

		textEnd = new TextField(dateFormat.format(calEnd.getTime()));
		textEnd.setPrefWidth(120);
		textEnd.setEditable(false);
		textEnd.setUserData(calStart.getTime());
		textEnd.setOnMouseClicked(new EventHandler<MouseEvent>() {
			public void handle(MouseEvent event) {
				selText = textEnd;
				popup.show(textEnd, event.getScreenX(), event.getScreenY());
			};
		});
		hbox.getChildren().add(textEnd);

		Button showButton = new Button("查询");
		showButton.setPrefWidth(80);
		showButton.setStyle("-fx-base: orange;");
		showButton.setOnAction(new EventHandler<ActionEvent>() {

			public void handle(ActionEvent event) {
				if (textStart.getUserData() == null
						|| textEnd.getUserData() == null) {
					showMessage("请先设置查询日期");
					return;
				}

				if (calStart.after(calEnd)) {
					showMessage("起始时间应该在结束时间之前");
					return;
				}

				UserSecurityUtil.verifyUserAuthority(new Runnable() {

					@Override
					public void run() {
						User user = UserSession.INSTANCE.getUser();
						if (user.getGroup().getEnergyStatistic()) {

							queryData();
						} else {
							showMessage("您所在的用户组没有权限执行该操作");
						}

					}
				});
			};
		});
		hbox.getChildren().add(showButton);

		container.getChildren().add(hbox);
	}

	private void createMessageLabel() {
		message = new MessageInfoLabel(3, 2);
		message.widthProperty().addListener(new ChangeListener<Number>() {
			@Override
			public void changed(ObservableValue<? extends Number> observable,
					Number oldValue, Number newValue) {
				message.setLayoutX((container.getWidth() - newValue
						.doubleValue()) / 2);
			}
		});
		message.heightProperty().addListener(new ChangeListener<Number>() {
			@Override
			public void changed(ObservableValue<? extends Number> observable,
					Number oldValue, Number newValue) {
				message.setLayoutY((container.getHeight() - newValue
						.doubleValue()) / 2);
			}
		});
		getChildren().add(message);
	}

	private void createPopup() {
		popup = new Popup();
		popup.setAutoHide(true);
		popup.setAutoFix(true);
		popup.setHideOnEscape(true);

		fxCalendar = new FXCalendar(false);
		fxCalendar.setOnDateSelected(new Runnable() {

			@Override
			public void run() {
				popup.hide();
				if (selText != null) {
					selText.setText(dateFormat.format(fxCalendar.getValue()));
					selText.setUserData(fxCalendar.getValue());

					if (selText.equals(textStart)) {
						calStart.setTime(fxCalendar.getValue());
						calStart.set(Calendar.HOUR_OF_DAY, 0);
						calStart.set(Calendar.MINUTE, 0);
					} else if (selText.equals(textEnd)) {
						calEnd.setTime(fxCalendar.getValue());
						calEnd.set(Calendar.HOUR_OF_DAY, 0);
						calEnd.set(Calendar.MINUTE, 1);
					}

					if (calStart.after(calEnd)) {
						showMessage("起始时间应该在结束时间之前");
					}
				}
			}
		});
		popup.getContent().add(fxCalendar);
	}

	private void createChart() {

		// xAxis.setLabel("X Axis");
		xAxis = new NumberAxis(0, (calEnd.getTime().getTime() - calStart
				.getTime().getTime()) / timeUnit, chartTickUnit);
		// xAxis.setTickUnit(5);
		xAxis.setMinorTickVisible(false);
		xAxis.setAutoRanging(false);
		xAxis.setForceZeroInRange(false);
		xAxis.setTickLabelFormatter(new StringConverter<Number>() {

			@Override
			public Number fromString(String string) {
				return 20;
			}

			@Override
			public String toString(Number object) {
				Date date = new Date(
						(long) (object.doubleValue() * timeUnit + calStart
								.getTime().getTime()));
				switch (dateType) {
				case DAILY:
					return sdf_h.format(date);
				case MONTHLY:
					return sdf_d.format(date);
				case YEARLY:
					return sdf_m.format(date);
				default:
					break;
				}
				return "";
			}
		});

		yAxis = new NumberAxis();
		yAxis.setForceZeroInRange(false);
		yAxis.setAutoRanging(true);
		yAxis.setTickLabelFormatter(new StringConverter<Number>() {

			private DecimalFormat df = new DecimalFormat("#0");

			@Override
			public Number fromString(String string) {
				return Double.parseDouble(string);
			}

			@Override
			public String toString(Number object) {
				return df.format(object.doubleValue());
			}
		});

		chart = new LineChart<Number, Number>(xAxis, yAxis);

		chart.setMinHeight(container.getMaxHeight() * 0.9);
		chart.setCreateSymbols(false);
		chart.setLegendVisible(false);
		chart.setAnimated(false);
		// chart.setLegendVisible(false);
		// chart.setTitleSide(Side.BOTTOM);
		// yAxis.setLabel("Y Axis");

		// add starting data
		series = new XYChart.Series<Number, Number>();
		// series.setName(title);
		chart.getData().add(series);

		container.getChildren().add(chart);

		// configChat();
	}

	private boolean isCtrlPressed = false;
	private double oldX = 0;
	private double oldY = 0;

	private NumberAxis yAxis;
	private Rectangle rect;

	private VBox messagePanel;

	/**
	 * 
	 */
	private void configChat() {
		EventHandler<KeyEvent> keyEventHandler = new EventHandler<KeyEvent>() {
			@Override
			public void handle(KeyEvent event) {
				if (event.getCode() == KeyCode.CONTROL) {
					isCtrlPressed = (event.getEventType() == KeyEvent.KEY_PRESSED);
					System.out.println("key: " + isCtrlPressed);
				}
			}
		};
		this.setOnKeyPressed(keyEventHandler);
		this.setOnKeyReleased(keyEventHandler);

		final EventHandler<MouseEvent> mouseMovedHandler = new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent event) {
				if (isCtrlPressed) {
					// System.out.println(event.getScreenX() - oldX);
					rect.setWidth(event.getSceneX() - oldX);
					rect.setHeight(event.getSceneY() - oldY);
				}
			}
		};
		chart.setOnMouseMoved(mouseMovedHandler);

		rect = new Rectangle(0, 0, null);
		// rect = new Rectangle( 0, 0, 50, 50);
		rect.setStrokeWidth(1);
		rect.setStroke(Color.BLUE);
		rect.setOnMouseMoved(mouseMovedHandler);
		getChildren().add(rect);

		// 拖拽开始
		chart.setOnMousePressed(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent event) {

				oldX = event.getSceneX();
				oldY = event.getSceneY();

				if (isCtrlPressed)
					chart.setCursor(Cursor.CROSSHAIR);
				else
					chart.setCursor(Cursor.HAND);

				rect.setVisible(true);
				rect.setWidth(0);
				rect.setHeight(0);
				rect.setX(event.getX() + chart.getLayoutX());
				rect.setY(event.getY() + chart.getLayoutY());
			}
		});

		chart.setOnMouseReleased(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent event) {

				chart.setCursor(Cursor.DEFAULT);

				if (isCtrlPressed) {
					if (event.getSceneX() - oldX < 20) {
						return;
					}
					// xAxis.getValueForDisplay(transX - xAxis.getof);
					Bounds bounds = yAxis.localToScene(yAxis.getBoundsInLocal());

					double lower = xAxis.getValueForDisplay(
							oldX - bounds.getMaxX()).doubleValue();
					double upper = xAxis.getValueForDisplay(
							event.getSceneX() - bounds.getMaxX()).doubleValue();

					setXAxisRange(lower, upper);
				}
				rect.setVisible(false);

			}
		});
	}

	private void setXAxisRange(double lower, double upper) {
		double tickCount = chartTickUnit;
		switch (dateType) {
		case DAILY:
			tickCount = Math.min(upper - lower, tickCount);
			break;
		case MONTHLY:
			tickCount = Math.min((upper - lower) / 24, tickCount);
			break;
		case YEARLY:
			tickCount = Math.min((upper - lower) / 24 / 12, tickCount);
			break;
		default:
			break;
		}
		xAxis.setTickUnit((upper - lower) / tickCount);
		xAxis.setLowerBound(lower);
		xAxis.setUpperBound(upper);
	}

	private void showMessage(String content) {
		message.show(content);
	}

	public void init(DateType dateType, IndexTreeNode energyIndexNode,
			Map<String, IndexTreeNode> normalIndexMap) {

		this.dateType = dateType;

		this.energyIndex = energyIndexNode.getPath();
		this.normalIndex = new ArrayList<String>();
		for (IndexTreeNode node : normalIndexMap.values()) {
			normalIndex.add(node.getPath());
		}

		series.getData().clear();

		StringBuilder pre = new StringBuilder();
		for (Entry<String, IndexTreeNode> entry : normalIndexMap.entrySet()) {
			pre.append(entry.getValue().getName());
		}

		pre.append(" ");

		if (energyIndexNode.getPath().contains("/")) {// 能耗分项
			pre.append(energyIndexNode.getName());
		} else {
			pre.append(EnergyConverterUtil.getEnergyTitle(energyIndexNode
					.getIndexName()));// 能耗分类
		}

		pre.append(" ");

		switch (dateType) {
		case DAILY:
			pre.append("逐时");
			break;
		case MONTHLY:
			pre.append("逐日");
			break;
		case YEARLY:
			pre.append("逐月");
			break;
		default:
			break;
		}
		pre.append(" 能耗数据");
		chart.setTitle(pre.toString());
		// series.setName(energyIndex.i);
	}

	private void queryData() {
		series.getData().clear();

		setXAxisRange(0, (calEnd.getTime().getTime() - calStart.getTime()
				.getTime()) / timeUnit);

		new HessianResponseTask() {
			
			@Override
			protected HessianResponse call() throws Exception {
				try {
					HessianResponse resp = HessianFactoryManger.INSTANCE
							.getTagVarService().getYMHistoryData(dateType, energyIndex,
									normalIndex, calStart.getTime(), calEnd.getTime());
					return resp;
				} catch (Exception e) {
					log.error("应用程序错误", e);
				}
				return null;
			}
			
			@Override
			public void successResponse(HessianResponse resp) {
				if (resp.getData() != null) {
					@SuppressWarnings("unchecked")
					List<YMHistoryData> list = (List<YMHistoryData>) resp.getData();
	
					if (list.isEmpty()) {
						showMessage("返回数据为空");
					} else {
						
						for (YMHistoryData data : list) {

							double time = (data.getId().getTime() - calStart.getTime()
									.getTime()) / timeUnit;
							series.getData().add(
									new XYChart.Data<Number, Number>(time, data
											.getValue()));
						}
					}
				} else {
					log.warn("返回数据为空, msg: {}", resp.getMsg());
					showMessage(resp.getMsg());
				}
				
			}
			
			@Override
			public void nullResponse() {
				showMessage("应用程序错误");
			}
			
			@Override
			public void failureResponse(HessianResponse resp) {
				showMessage(resp.getMsg());
			}
		}.start();
	}
}
