/*
 * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.topology.views.visualization.ui;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Map;

import javax.swing.*;
import javax.swing.border.BevelBorder;

import com.google.common.collect.Maps;

import de.tud.kom.p2psim.impl.util.guirunner.Config;

/**
 * JFrame which holds all the live plots.
 *
 * @author Unknown
 *
 * 21.03.2017 Clemens Krug:
 * Changed this class up quite a bit because when one would
 * add more and more metrics to the visualisation, they would get unreadable small. Reworked this
 * class so the charts will allways fill the view if theres is space, but if you add do many plots
 * they will stay add a minimum height of 250 px and a scrollbar will be added instead.
 */
public class PlottingView extends JFrame {
	
	/*
	 * Configuration paths
	 */
	static final String CONF_PATH = "GUIRunner/LivePlotWindow/";
	static final String CONF_PATH_POSX = CONF_PATH + "PosX";
	static final String CONF_PATH_POSY = CONF_PATH + "PosY";
	static final String CONF_PATH_WIDTH = CONF_PATH + "Width";
	static final String CONF_PATH_HEIGHT = CONF_PATH + "Height";
		
	private static final int PLOT_HEIGHT_MIN = 250;

	private static final Color PLOT_BACKGROUND_COLOR = Color.WHITE;
	
	private Map<String, XYChart> nameToPlotMap = Maps.newLinkedHashMap();
	private JPanel plotBox;
	private JScrollPane spane;
	
	public PlottingView(String name) {
		setTitle(name);
		setVisible(true);
		
		buildUI();
	}
	
	private void buildUI() {
		plotBox = new PlotBox();
		plotBox.setLayout(new BoxLayout(plotBox, BoxLayout.Y_AXIS));
		plotBox.setBackground(PLOT_BACKGROUND_COLOR);
		plotBox.setBorder(BorderFactory.createEtchedBorder());

		spane = new JScrollPane(plotBox);
		add(spane);
		
		int locationX = Config.getValue(CONF_PATH_POSX, 0);
		int locationY = Config.getValue(CONF_PATH_POSY, 0);
		
		setLocation(locationX, locationY);
		
		int winWidth = Config.getValue(CONF_PATH_WIDTH, 900);
		int winHeight = Config.getValue(CONF_PATH_HEIGHT, 800);
		
		setSize(winWidth, winHeight);
		setPreferredSize(new Dimension(winWidth, winHeight));
	}
	
	public XYChart createPlot(String name, String seriesName) {
		XYChart plot = new XYChart(PLOT_BACKGROUND_COLOR, name, seriesName);
		
		JPanel chartPanel = plot.getChartPanel();
		
		chartPanel = wrapInCollabsable(chartPanel, name);
		
		plotBox.add(chartPanel);
		plotBox.repaint();
		spane.validate();
		
		nameToPlotMap.put(name, plot);
		
		return plot;
	}
	
	public XYChart createPlot(String name) {
		return createPlot(name, name);
	}

	private JPanel wrapInCollabsable(JPanel chartPanel, String title) {
		final JPanel innerPanel = new JPanel();
		innerPanel.setLayout(new BorderLayout());
		innerPanel.add(new JLabel("  "), BorderLayout.WEST);
		innerPanel.add(chartPanel, BorderLayout.CENTER);
		innerPanel.setBackground(PLOT_BACKGROUND_COLOR);
		
		final JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		panel.setBackground(PLOT_BACKGROUND_COLOR);
		
		Box header = Box.createHorizontalBox();
		header.setBackground(PLOT_BACKGROUND_COLOR);
		
		final JLabel collapseButton = new JLabel("-");
		Dimension buttonSize = new Dimension(11, 11);
		collapseButton.setBackground(PLOT_BACKGROUND_COLOR);
		collapseButton.setPreferredSize(buttonSize);
		collapseButton.setMaximumSize(buttonSize);
		collapseButton.setMinimumSize(buttonSize);
		collapseButton.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
		collapseButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent arg0) {
				collapseButton.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
			}

			@Override
			public void mouseReleased(MouseEvent arg0) {
				collapseButton.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
				
				if (collapseButton.getText().equals("-")) {
					collapseButton.setText("+");
					panel.remove(innerPanel);
					panel.setMinimumSize(header.getPreferredSize());
					panel.repaint();
				} else {
					collapseButton.setText("-");
					panel.add(innerPanel, BorderLayout.CENTER);
					panel.setMinimumSize(new Dimension(850,PLOT_HEIGHT_MIN));
				}
			}
		});
		
		JLabel titleLabel = new JLabel(" " + title);
		Font font = titleLabel.getFont();
		titleLabel.setFont(font.deriveFont(5));
		titleLabel.setBackground(PLOT_BACKGROUND_COLOR);
		
		header.add(new JLabel(" "));
		header.add(collapseButton);
		header.add(titleLabel);
		
		panel.add(header, BorderLayout.NORTH);
		panel.add(innerPanel, BorderLayout.CENTER);
		panel.setMinimumSize(new Dimension(850,PLOT_HEIGHT_MIN));
		
		return panel;
	}

	public void removeAllPlots() {
		nameToPlotMap.clear();
		plotBox.removeAll();
		plotBox.validate();
	}

	/**
	 * Custom JPanel to implement the desired scrolling policy. Basically
	 * there won't be a scrollbar if all of the components have a greater height than
	 * {@link #PLOT_HEIGHT_MIN}. However, if you add so many components that they can't
	 * fit in while maintaining that minimum height, a scrollbar will be added.
     *
     * @author Clemens Krug
	 */
	private class PlotBox extends JPanel implements Scrollable
	{
		private boolean enableScroll = false;
		private Dimension preferredSize = new Dimension( Config.getValue(CONF_PATH_WIDTH, 900), 800);

		@Override
		public Component add(Component comp)
		{
			Component retComp = super.add(comp);
			preferredSize.height = getComponentCount() * PLOT_HEIGHT_MIN;
			checkScrollNeeded();
			return retComp;
		}

		/**
		 * Check if scrolling should be enabled and to so if it should.
		 */
		private void checkScrollNeeded()
		{
			enableScroll = getComponentCount() * PLOT_HEIGHT_MIN > getParent().getHeight();
		}

		@Override
		public Dimension getPreferredSize() {
			return preferredSize;
		}

		@Override
		public Dimension getPreferredScrollableViewportSize() {
			return getPreferredSize();
		}

		@Override
		public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
			return 50;
		}

		@Override
		public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
			return PLOT_HEIGHT_MIN;
		}

		@Override
		public boolean getScrollableTracksViewportWidth() {
			return true;
		}

		@Override
		public boolean getScrollableTracksViewportHeight() {
			return !enableScroll;
		}
	}
}