/*
 * Copyright 2008-2019 by Emeric Vernat
 *
 *     This file is part of Java Melody.
 *
 * 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 net.bull.javamelody;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JToolTip;
import javax.swing.UIManager;
import javax.swing.plaf.metal.MetalToolTipUI;

import net.bull.javamelody.internal.model.Counter;
import net.bull.javamelody.internal.model.CounterRequest;
import net.bull.javamelody.internal.model.RemoteCollector;
import net.bull.javamelody.swing.table.MTable;

/**
 * Tableau de requêtes avec graphiques en toolTip
 * @author Emeric Vernat
 */
class CounterRequestTable extends MTable<CounterRequest> {
	private static final long serialVersionUID = 1L;

	private static final int IMAGE_HEIGHT = 50;
	private static final int IMAGE_WIDTH = 100;

	@SuppressWarnings("all")
	private final RemoteCollector remoteCollector;

	@SuppressWarnings("all")
	private final Map<String, CounterRequest> counterRequestByRequestName = new HashMap<>();
	@SuppressWarnings("all")
	private final Map<String, BufferedImage> requestChartByRequestName = new HashMap<>();

	private class ImageToolTip extends JToolTip {
		private static final long serialVersionUID = 1L;

		ImageToolTip() {
			super();
			setUI(new ImageToolTipUI());
		}
	}

	private class ImageToolTipUI extends MetalToolTipUI {
		ImageToolTipUI() {
			super();
			// parce que ImageToolTipUI hérite de MetalToolTipUI et non de SynthToolTipUI
			// on fixe la couleur pour la propriété "ToolTip.background"
			// sinon ce tooltip aura une moche couleur grise à la place d'un jaune beige
			// (quand il n'y pas d'image jrobin)
			UIManager.put("ToolTip.background", UIManager.getColor("info"));
		}

		@Override
		public void paint(Graphics g, JComponent c) {
			try {
				final String tipText = ((JToolTip) c).getTipText();
				final BufferedImage image = getRequestChartByRequestName(tipText);
				// on affiche que l'image et pas le text dans le tooltip
				//		    FontMetrics metrics = c.getFontMetrics(g.getFont());
				//		    g.setColor(c.getForeground());
				//		    g.drawString(tipText, 1, 1);
				if (image != null) {
					g.drawImage(image, 0, 0, c);
				} else {
					super.paint(g, c);
				}
			} catch (final IOException e) {
				// s'il y a une erreur dans la récupération de l'image tant pis
				super.paint(g, c);
			}
		}

		@Override
		public Dimension getPreferredSize(JComponent c) {
			try {
				final String tipText = ((JToolTip) c).getTipText();
				final BufferedImage image = getRequestChartByRequestName(tipText);
				if (image != null) {
					// pas besoin de calculer la dimension de l'image, puisqu'elle est fixée avant d'avoir l'image
					return new Dimension(image.getWidth(), image.getHeight());
				}
				return super.getPreferredSize(c);
			} catch (final IOException e) {
				// s'il y a une erreur dans la récupération de l'image tant pis
				return super.getPreferredSize(c);
			}

			//			FontMetrics metrics = c.getFontMetrics(c.getFont());
			//		    String tipText = ((JToolTip) c).getTipText();
			//		    BufferedImage image = getRequestChartByRequestName(tipText);
			//		    if (tipText == null) {
			//		      tipText = "";
			//		    }
			//		    int width = SwingUtilities.computeStringWidth(metrics, tipText);
			//		    int height = metrics.getHeight() + image.getHeight(c);
			//
			//		    if (width < image.getWidth(c)) {
			//		      width = image.getWidth(c);
			//		    }
			//		    return new Dimension(width, height);
		}
	}

	CounterRequestTable(RemoteCollector remoteCollector) {
		super();
		this.remoteCollector = remoteCollector;
	}

	static boolean isRequestGraphDisplayed(Counter parentCounter) {
		return !(parentCounter.isErrorCounter() && !parentCounter.isJobCounter())
				&& !parentCounter.isJspOrStrutsCounter();
	}

	@Override
	public JToolTip createToolTip() {
		final ImageToolTip imageToolTip = new ImageToolTip();
		imageToolTip.setComponent(this);
		return imageToolTip;
	}

	@Override
	public void setList(List<CounterRequest> requests) {
		super.setList(requests);
		requestChartByRequestName.clear();
		counterRequestByRequestName.clear();
	}

	BufferedImage getRequestChartByRequestName(String requestName) throws IOException {
		BufferedImage requestChart;
		if (requestChartByRequestName.containsKey(requestName)) {
			requestChart = requestChartByRequestName.get(requestName);
		} else {
			requestChart = null;
			final CounterRequest counterRequest = getCounterRequestByRequestName(requestName);
			if (counterRequest != null) {
				final byte[] imageData = remoteCollector.collectJRobin(counterRequest.getId(),
						IMAGE_WIDTH, IMAGE_HEIGHT);
				if (imageData != null) {
					requestChart = ImageIO.read(new ByteArrayInputStream(imageData));
					// requestChart sera mis dans la map par le put ci-dessous
				}
			}
			// si on n'a pas trouvé la requête dans la liste des requêtes
			// ou si on n'arrive pas à charger l'image depuis le serveur
			// alors il y aura null mis comme image dans la map
			// et au moins on ne cherchera plus inutilement dans cette liste
			// ou on n'essayera plus à chaque fois de charger l'image depuis le serveur
			// et sinon il y aura une image dans la map si tout va bien
			requestChartByRequestName.put(requestName, requestChart);
		}
		return requestChart;
	}

	private CounterRequest getCounterRequestByRequestName(String requestName) {
		// si cela n'a pas déjà été fait précédemment, on met tous les résultats de cette méthode dans une map indexée
		// pour éviter de créer une ArrayList et de reparcourir toutes les requêtes à chaque fois
		if (counterRequestByRequestName.isEmpty()) {
			final List<CounterRequest> requests = getList();
			for (final CounterRequest request : requests) {
				counterRequestByRequestName.put(request.getName(), request);
			}
		}
		return counterRequestByRequestName.get(requestName);
	}
}
