/*
 *  Copyright 2015 the original author or authors. 
 *  @https://github.com/scouter-project/scouter
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 *
 */
package scouter.client.maria.views;

import org.csstudio.swt.xygraph.dataprovider.CircularBufferDataProvider;
import org.csstudio.swt.xygraph.dataprovider.Sample;
import org.csstudio.swt.xygraph.figures.Trace;
import org.csstudio.swt.xygraph.figures.Trace.PointStyle;
import org.csstudio.swt.xygraph.figures.Trace.TraceType;
import org.csstudio.swt.xygraph.figures.XYGraph;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import scouter.client.Images;
import scouter.client.listeners.RangeMouseListener;
import scouter.client.maria.actions.OpenDigestTableAction;
import scouter.client.model.AgentModelThread;
import scouter.client.model.RefreshThread;
import scouter.client.model.RefreshThread.Refreshable;
import scouter.client.net.TcpProxy;
import scouter.client.preferences.PManager;
import scouter.client.preferences.PreferenceConstants;
import scouter.client.server.Server;
import scouter.client.server.ServerManager;
import scouter.client.util.ChartUtil;
import scouter.client.util.ColorUtil;
import scouter.client.util.ConsoleProxy;
import scouter.client.util.ExUtil;
import scouter.client.util.ImageUtil;
import scouter.client.util.ScouterUtil;
import scouter.client.util.TimeUtil;
import scouter.lang.counters.CounterConstants;
import scouter.lang.pack.MapPack;
import scouter.lang.value.DecimalValue;
import scouter.lang.value.ListValue;
import scouter.lang.value.MapValue;
import scouter.lang.value.Value;
import scouter.net.RequestCmd;
import scouter.util.CastUtil;
import scouter.util.DateUtil;
import scouter.util.LinkedList;
import scouter.util.LinkedList.ENTRY;

public class DbRealtimeTotalActivityView extends ViewPart implements Refreshable {

public static final String ID = DbRealtimeTotalActivityView.class.getName();
	
	int serverId;
	
	RefreshThread thread;
	
	static long TIME_RANGE = DateUtil.MILLIS_PER_FIVE_MINUTE;
	static int REFRESH_INTERVAL = (int) (DateUtil.MILLIS_PER_SECOND * 2);
	static int BUFFER_SIZE = (int)(TIME_RANGE / REFRESH_INTERVAL) + 1;
	
	FigureCanvas canvas;
	XYGraph xyGraph;
	
	Trace callTrace;
	Trace selectTrace;
	Trace insertTrace;
	Trace updateTrace;
	Trace deleteTrace;
	
	boolean isStackView = true;
	
	LinkedList<ValueLog> valueLogs = new LinkedList<ValueLog>();
	
	public void init(IViewSite site) throws PartInitException {
		super.init(site);
		String secId = site.getSecondaryId();
		serverId = CastUtil.cint(secId);
	}

	public void createPartControl(Composite parent) {
		Server server = ServerManager.getInstance().getServer(serverId);
		this.setPartName("DB Activity[" + server.getName() + "]");
		GridLayout layout = new GridLayout(1, true);
		layout.marginHeight = 5;
		layout.marginWidth = 5;
		parent.setLayout(layout);
		parent.setBackground(ColorUtil.getInstance().getColor(SWT.COLOR_WHITE));
		parent.setBackgroundMode(SWT.INHERIT_FORCE);
		canvas = new FigureCanvas(parent);
		canvas.setLayoutData(new GridData(GridData.FILL_BOTH));
		canvas.setScrollBarVisibility(FigureCanvas.NEVER);
		canvas.addControlListener(new ControlListener() {
			public void controlMoved(ControlEvent arg0) {
			}
			
			public void controlResized(ControlEvent arg0) {
				Rectangle r = canvas.getClientArea();
				xyGraph.setSize(r.width, r.height);
			}
		});
		
		xyGraph = new XYGraph();
		xyGraph.setShowLegend(true);
		xyGraph.setShowTitle(false);
		canvas.setContents(xyGraph);

		xyGraph.primaryXAxis.setDateEnabled(true);
		xyGraph.primaryXAxis.setShowMajorGrid(true);
		xyGraph.primaryYAxis.setAutoScale(true);
		xyGraph.primaryYAxis.setShowMajorGrid(true);
		xyGraph.primaryXAxis.setFormatPattern("HH:mm:ss");
		xyGraph.primaryYAxis.setFormatPattern("#,##0");
		
		xyGraph.primaryXAxis.setTitle("");
		xyGraph.primaryYAxis.setTitle("");
		
		xyGraph.primaryYAxis.addMouseListener(new RangeMouseListener(getViewSite().getShell(), xyGraph.primaryYAxis));
		
		CircularBufferDataProvider callProvider = new CircularBufferDataProvider(true);
		callProvider.setBufferSize(BUFFER_SIZE);
		callProvider.setCurrentXDataArray(new double[] {});
		callProvider.setCurrentYDataArray(new double[] {});
		callTrace = new Trace("Call (SUM)", xyGraph.primaryXAxis, xyGraph.primaryYAxis, callProvider);
		callTrace.setPointStyle(PointStyle.NONE);
		callTrace.setTraceType(TraceType.AREA);
		callTrace.setLineWidth(PManager.getInstance().getInt(PreferenceConstants.P_CHART_LINE_WIDTH));
		callTrace.setAreaAlpha(255);
		callTrace.setTraceColor(ColorUtil.getInstance().getColor(SWT.COLOR_DARK_BLUE));
		xyGraph.addTrace(callTrace);
		
		CircularBufferDataProvider selectProvider = new CircularBufferDataProvider(true);
		selectProvider.setBufferSize(BUFFER_SIZE);
		selectProvider.setCurrentXDataArray(new double[] {});
		selectProvider.setCurrentYDataArray(new double[] {});
		selectTrace = new Trace("Select (SUM)", xyGraph.primaryXAxis, xyGraph.primaryYAxis, selectProvider);
		selectTrace.setPointStyle(PointStyle.NONE);
		selectTrace.setTraceType(TraceType.AREA);
		selectTrace.setLineWidth(PManager.getInstance().getInt(PreferenceConstants.P_CHART_LINE_WIDTH));
		selectTrace.setAreaAlpha(255);
		selectTrace.setTraceColor(ColorUtil.getInstance().getColor(SWT.COLOR_DARK_CYAN));
		xyGraph.addTrace(selectTrace);
		
		CircularBufferDataProvider insertProvider = new CircularBufferDataProvider(true);
		insertProvider.setBufferSize(BUFFER_SIZE);
		insertProvider.setCurrentXDataArray(new double[] {});
		insertProvider.setCurrentYDataArray(new double[] {});
		insertTrace = new Trace("Insert (SUM)", xyGraph.primaryXAxis, xyGraph.primaryYAxis, insertProvider);
		insertTrace.setPointStyle(PointStyle.NONE);
		insertTrace.setTraceType(TraceType.AREA);
		insertTrace.setLineWidth(PManager.getInstance().getInt(PreferenceConstants.P_CHART_LINE_WIDTH));
		insertTrace.setAreaAlpha(255);
		insertTrace.setTraceColor(ColorUtil.getInstance().getColor(SWT.COLOR_DARK_GRAY));
		xyGraph.addTrace(insertTrace);
		
		CircularBufferDataProvider updateProvider = new CircularBufferDataProvider(true);
		updateProvider.setBufferSize(BUFFER_SIZE);
		updateProvider.setCurrentXDataArray(new double[] {});
		updateProvider.setCurrentYDataArray(new double[] {});
		updateTrace = new Trace("Update (SUM)", xyGraph.primaryXAxis, xyGraph.primaryYAxis, updateProvider);
		updateTrace.setPointStyle(PointStyle.NONE);
		updateTrace.setTraceType(TraceType.AREA);
		updateTrace.setLineWidth(PManager.getInstance().getInt(PreferenceConstants.P_CHART_LINE_WIDTH));
		updateTrace.setAreaAlpha(255);
		updateTrace.setTraceColor(ColorUtil.getInstance().getColor(SWT.COLOR_DARK_GREEN));
		xyGraph.addTrace(updateTrace);
		
		CircularBufferDataProvider deleteProvider = new CircularBufferDataProvider(true);
		deleteProvider.setBufferSize(BUFFER_SIZE);
		deleteProvider.setCurrentXDataArray(new double[] {});
		deleteProvider.setCurrentYDataArray(new double[] {});
		deleteTrace = new Trace("Delete (SUM)", xyGraph.primaryXAxis, xyGraph.primaryYAxis, deleteProvider);
		deleteTrace.setPointStyle(PointStyle.NONE);
		deleteTrace.setTraceType(TraceType.AREA);
		deleteTrace.setLineWidth(PManager.getInstance().getInt(PreferenceConstants.P_CHART_LINE_WIDTH));
		deleteTrace.setAreaAlpha(255);
		deleteTrace.setTraceColor(ColorUtil.getInstance().getColor(SWT.COLOR_DARK_MAGENTA));
		xyGraph.addTrace(deleteTrace);
		
		ScouterUtil.addHorizontalRangeListener(xyGraph.getPlotArea(), new OpenDigestTableAction(serverId), false);
		
		IToolBarManager man = getViewSite().getActionBars().getToolBarManager();
		Action stackViewAct = new Action("Area Mode", IAction.AS_CHECK_BOX) {
			public void run() {
				isStackView = isChecked();
				changeMode();
			}
		};
		stackViewAct.setImageDescriptor(ImageUtil.getImageDescriptor(Images.sum));
		stackViewAct.setChecked(true);
		man.add(stackViewAct);
		Action openDailyView = new Action("Open Daily View", ImageUtil.getImageDescriptor(Images.calendar)) {
			public void run() {
				try {
					PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(DbTodayTotalActivityView.ID, ""+serverId, IWorkbenchPage.VIEW_ACTIVATE);
				} catch (PartInitException e) {
					ConsoleProxy.errorSafe(e.toString());
				}
			}
		};
		man.add(openDailyView);
		
		thread = new RefreshThread(this, REFRESH_INTERVAL);
		thread.start();
	}

	public void setFocus() {
		
	}
	
	@Override
	public void dispose() {
		super.dispose();
		if (this.thread != null) {
			this.thread.shutdown();
		}
	}

	public void refresh() {
		TcpProxy tcp = TcpProxy.getTcpProxy(serverId);
		Value v = null;
		try {
			MapPack param = new MapPack();
			ListValue objHashLv = AgentModelThread.getInstance().getLiveObjHashLV(serverId, CounterConstants.MARIA_PLUGIN);
			if (objHashLv.size() > 0) {
				param.put("objHash", objHashLv);
				v = tcp.getSingleValue(RequestCmd.DB_REALTIME_ACTIVITY, param);
			}
		} catch (Exception e) {
			ConsoleProxy.errorSafe(e.toString());
		} finally {
			TcpProxy.putTcpProxy(tcp);
		}
		if (v == null) {
			ExUtil.exec(canvas, new Runnable() {
				public void run() {
					setTitleImage(Images.inactive);
					long now = TimeUtil.getCurrentTime(serverId);
					long stime = now - TIME_RANGE;
					xyGraph.primaryXAxis.setRange(stime, now + 1);
				}
			});
		} else {
			MapValue mv = (MapValue) v;
			final DecimalValue callValue = new DecimalValue(mv.getLong("call"));
			final DecimalValue selectValue = new DecimalValue(mv.getLong("select"));
			final DecimalValue insertValue = new DecimalValue(mv.getLong("insert"));
			final DecimalValue updateValue = new DecimalValue(mv.getLong("update"));
			final DecimalValue deleteValue = new DecimalValue(mv.getLong("delete"));
			
			ExUtil.exec(canvas, new Runnable() {
				public void run() {
					setTitleImage(Images.active);
					long now = TimeUtil.getCurrentTime(serverId) / REFRESH_INTERVAL * REFRESH_INTERVAL;
					long stime = now - TIME_RANGE;
					xyGraph.primaryXAxis.setRange(stime, now + 1);
					ValueLog valueLog = new ValueLog();
					valueLog.time = now;
					valueLog.delete = deleteValue.value;
					valueLog.update = updateValue.value;
					valueLog.insert = insertValue.value;
					valueLog.select = selectValue.value;
					valueLog.call = callValue.value;
					valueLogs.add(valueLog);
					if (valueLogs.size() > BUFFER_SIZE) {
						valueLogs.removeFirst();
					}
					if (isStackView) {
						updateValue.value += deleteValue.value;
						insertValue.value += updateValue.value;
						selectValue.value += insertValue.value;
						callValue.value += selectValue.value;
					}
					((CircularBufferDataProvider)callTrace.getDataProvider()).addSample(new Sample(now, callValue.value));
					((CircularBufferDataProvider)selectTrace.getDataProvider()).addSample(new Sample(now, selectValue.value));
					((CircularBufferDataProvider)insertTrace.getDataProvider()).addSample(new Sample(now, insertValue.value));
					((CircularBufferDataProvider)updateTrace.getDataProvider()).addSample(new Sample(now, updateValue.value));
					((CircularBufferDataProvider)deleteTrace.getDataProvider()).addSample(new Sample(now, deleteValue.value));
					xyGraph.primaryYAxis.setRange(0, getMaxYValue());
				}
				
				private double getMaxYValue() {
					if (isStackView) {
						return ChartUtil.getMax(((CircularBufferDataProvider)callTrace.getDataProvider()).iterator());
					}
					double value = ChartUtil.getMax(((CircularBufferDataProvider)callTrace.getDataProvider()).iterator());
					value = Math.max(value, ChartUtil.getMax(((CircularBufferDataProvider)selectTrace.getDataProvider()).iterator()));
					value = Math.max(value, ChartUtil.getMax(((CircularBufferDataProvider)insertTrace.getDataProvider()).iterator()));
					value = Math.max(value, ChartUtil.getMax(((CircularBufferDataProvider)updateTrace.getDataProvider()).iterator()));
					value = Math.max(value, ChartUtil.getMax(((CircularBufferDataProvider)deleteTrace.getDataProvider()).iterator()));
					return value;
				}
			});
		}
	}
	
	protected void changeMode() {
		CircularBufferDataProvider delProvider = (CircularBufferDataProvider)deleteTrace.getDataProvider();
		CircularBufferDataProvider upProvider = (CircularBufferDataProvider)updateTrace.getDataProvider();
		CircularBufferDataProvider inProvider = (CircularBufferDataProvider)insertTrace.getDataProvider();
		CircularBufferDataProvider selProvider = (CircularBufferDataProvider)selectTrace.getDataProvider();
		CircularBufferDataProvider callProvider = (CircularBufferDataProvider)callTrace.getDataProvider();
		delProvider.clearTrace();
		upProvider.clearTrace();
		inProvider.clearTrace();
		selProvider.clearTrace();
		callProvider.clearTrace();
		int size = valueLogs.size();
		if (size > 0) {
			ENTRY<ValueLog> entry = valueLogs.getFirst();
			if (isStackView) {
				do {
					ValueLog log = entry.item;
					double x = log.time;
					double delValue = log.delete;
					double upValue = delValue + log.update;
					double inValue = upValue + log.insert;
					double selValue = inValue + log.select;
					double callValue = selValue + log.call;
					callProvider.addSample(new Sample(x, callValue));
					selProvider.addSample(new Sample(x, selValue));
					inProvider.addSample(new Sample(x, inValue));
					upProvider.addSample(new Sample(x, upValue));
					delProvider.addSample(new Sample(x, delValue));
				} while ((entry = entry.next) != null);
			} else {
				do {
					ValueLog log = entry.item;
					double x = log.time;
					double delValue = log.delete;
					double upValue = log.update;
					double inValue = log.insert;
					double selValue = log.select;
					double callValue = log.call;
					callProvider.addSample(new Sample(x, callValue));
					selProvider.addSample(new Sample(x, selValue));
					inProvider.addSample(new Sample(x, inValue));
					upProvider.addSample(new Sample(x, upValue));
					delProvider.addSample(new Sample(x, delValue));
				} while ((entry = entry.next) != null);
			}
		}
		if (isStackView) {
			deleteTrace.setTraceType(TraceType.AREA);
			updateTrace.setTraceType(TraceType.AREA);
			insertTrace.setTraceType(TraceType.AREA);
			selectTrace.setTraceType(TraceType.AREA);
			callTrace.setTraceType(TraceType.AREA);
		} else {
			deleteTrace.setTraceType(TraceType.SOLID_LINE);
			updateTrace.setTraceType(TraceType.SOLID_LINE);
			insertTrace.setTraceType(TraceType.SOLID_LINE);
			selectTrace.setTraceType(TraceType.SOLID_LINE);
			callTrace.setTraceType(TraceType.SOLID_LINE);
		}
	}
	
	private static class ValueLog {
		long time;
		double delete;
		double update;
		double insert;
		double select;
		double call;
	}
}
