/*
 * Copyright (c) 2013-2017 Chris Newland.
 * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
 * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
 */
package org.adoptopenjdk.jitwatch.ui.graphing;

import static org.adoptopenjdk.jitwatch.util.UserInterfaceUtil.fix;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.adoptopenjdk.jitwatch.model.CodeCacheEvent;
import org.adoptopenjdk.jitwatch.model.Tag;
import org.adoptopenjdk.jitwatch.ui.main.JITWatchUI;
import org.adoptopenjdk.jitwatch.util.UserInterfaceUtil;

import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;

public class CodeCacheStage extends AbstractGraphStage
{
	private boolean labelLeft = true;

	public CodeCacheStage(JITWatchUI parent)
	{
		super(parent, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT, true);

		StackPane root = new StackPane();
		Scene scene = UserInterfaceUtil.getScene(root, width, height);

		canvas.widthProperty().bind(root.widthProperty());
		canvas.heightProperty().bind(root.heightProperty());

		root.getChildren().add(canvas);

		setTitle("JITWatch Free Code Cache");

		setScene(scene);
		show();

		redraw();
	}

	@Override
	public final void redraw()
	{
		super.baseRedraw();

		labelLeft = true;

		List<CodeCacheEvent> codeCacheEvents = mainUI.getJITDataModel().getCodeCacheEvents();

		Collections.sort(codeCacheEvents, new Comparator<CodeCacheEvent>()
		{
			@Override
			public int compare(CodeCacheEvent e1, CodeCacheEvent e2)
			{
				return Long.compare(e1.getStamp(), e2.getStamp());
			}
		});

		if (codeCacheEvents.size() > 0)
		{
			CodeCacheEvent firstEvent = codeCacheEvents.get(0);
			minX = firstEvent.getStamp();

			double firstNonZeroY = 0;

			Tag endOfLogTag = mainUI.getJITDataModel().getEndOfLogTag();

			if (endOfLogTag != null)
			{
				maxX = getStampFromTag(endOfLogTag);

			}
			else
			{
				CodeCacheEvent lastEvent = codeCacheEvents.get(codeCacheEvents.size() - 1);
				maxX = lastEvent.getStamp();
			}

			minY = firstEvent.getFreeCodeCache();
			maxY = firstEvent.getFreeCodeCache();

			if (minY != 0)
			{
				firstNonZeroY = minY;
			}

			// find ranges
			for (CodeCacheEvent event : codeCacheEvents)
			{
				long freeCodeCache = event.getFreeCodeCache();

				if (freeCodeCache > 0)
				{
					if (minY == 0)
					{
						minY = freeCodeCache;

						firstNonZeroY = minY;
					}

					if (freeCodeCache > maxY)
					{
						maxY = freeCodeCache;
					}
					else if (freeCodeCache < minY)
					{
						minY = freeCodeCache;
					}
				}
			}

			drawAxes();

			double lastCX = graphGapLeft + normaliseX(minX);
			double lastCY = graphGapTop + normaliseY(firstNonZeroY);

			Color colourLine = Color.BLUE;
			double lineWidth = 2.0;
			
			for (CodeCacheEvent event : codeCacheEvents)
			{
				long stamp = event.getStamp();

				double x = graphGapLeft + normaliseX(stamp);
				double y = lastCY;

				switch (event.getEventType())
				{
				case COMPILATION:
					y = addToGraph(lastCX, lastCY, colourLine, lineWidth, event, x);
					lastCX = x;
					lastCY = y;
					break;

				case SWEEPER:
					showLabel("Sweep", Color.WHITE, x, y);
					break;

				case CACHE_FULL:
					showLabel("Code Cache Full", Color.RED, x, y);
					break;
				}
			}

			continueLineToEndOfXAxis(lastCX, lastCY, colourLine, lineWidth);
		}
		else
		{
			gc.fillText("No code cache information in log", fix(10), fix(10));
		}
	}

	private double addToGraph(double lastCX, double lastCY, Color colourLine, double lineWidth, CodeCacheEvent event, double x)
	{
		long freeCodeCache = event.getFreeCodeCache();

		double y = graphGapTop + normaliseY(freeCodeCache);

		gc.setFill(colourLine);
		gc.setStroke(colourLine);
		gc.setLineWidth(lineWidth);
		
		gc.strokeLine(fix(lastCX), fix(lastCY), fix(x), fix(y));
		return y;
	}

	private void showLabel(String text, Color background, double x, double y)
	{
		double labelX;
		double labelY;

		if (labelLeft)
		{
			labelX = x - getApproximateStringWidth(text) - 16;
			labelY = Math.min(y - getStringHeight(), graphGapTop + chartHeight - 32);

		}
		else
		{
			labelX = x + 8;
			labelY = Math.min(y, graphGapTop + chartHeight - 32);
		}

		drawLabel(text, labelX, labelY, background);

		labelLeft = !labelLeft;
	}
}