/*
 * 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.world;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.impl.topology.views.FiveGTopologyView;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.views.fiveg.AbstractGridBasedTopologyDatabase;
import de.tud.kom.p2psim.impl.topology.views.fiveg.FiveGTopologyDatabase.Entry;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Rate;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;

/**
 * Visualization for the {@link FiveGTopologyView}
 * 
 * Added 06.10.2016 Nils Richerzhagen, Clemens Krug Functionality to 'destroy'
 * cells using SHIFT + Left-click. Enable/Disable using the
 * enableCellDestruction
 * 
 * Update 04.07.2017 Nils Richerzhagen Visualization to work with
 * VisualizationInjector.scaleValue(...); stroke width of broken cells reduced
 * for "nicer" visuals
 * 
 * @author Bjoern Richerzhagen
 * @version 1.0, Nov 5, 2015
 */
public class FiveGVisualization extends JComponent
		implements VisualizationInjector.MouseClickListener {

	private final boolean enableCellDestruction;

	protected BufferedImage image;

	protected volatile boolean needsRedraw = true;

	private final AbstractGridBasedTopologyDatabase database;

	public FiveGVisualization(AbstractGridBasedTopologyDatabase database,
			boolean enableCellDestruction) {
		setBounds(0, 0, VisualizationInjector.getWorldX(),
				VisualizationInjector.getWorldY());
		setOpaque(true);
		setVisible(true);

		this.database = database;
		image = new BufferedImage(VisualizationInjector.getWorldX(),
				VisualizationInjector.getWorldY(), BufferedImage.TYPE_INT_ARGB);

		this.enableCellDestruction = enableCellDestruction;

		VisualizationInjector.addMouseListener(this);
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);

		if (needsRedraw) {
			redraw();
		}

		Graphics2D g2 = (Graphics2D) g;
		g2.drawImage(image, 0, 0, null);
	}

	protected void redraw() {
		needsRedraw = false;

		Graphics2D g2 = (Graphics2D) image.getGraphics();

		Composite c = g2.getComposite();
		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
		Rectangle2D.Double rect = new Rectangle2D.Double(0, 0,
				VisualizationInjector.getWorldX(),
				VisualizationInjector.getWorldY());
		g2.fill(rect);
		g2.setComposite(c);

		try {
			drawEntries(g2);
		} catch (RuntimeException e) {
			e.printStackTrace();
			System.exit(0);
			needsRedraw = true;
		}
	}

	private void drawEntries(Graphics2D g2) {
		// Iterate over grid coordinates
		int stepSize = database.getGridSize();

		double maxLatency = 0;
		double minLatency = Double.MAX_VALUE;
		boolean isUpload = false;

		try {
			for (int x = 0; x < Binder.getComponent(Topology.class)
					.getWorldDimensions().getX(); x += stepSize) {
				for (int y = 0; y < Binder.getComponent(Topology.class)
						.getWorldDimensions().getY(); y += stepSize) {
					// TODO add checkbox for cloudlets?
					Entry entry = database
							.getEntryFor(database.getSegmentID(x, y), false);
					if (entry == null) {
						continue;
					}
				}
			}

			for (int x = 0; x < Binder.getComponent(Topology.class)
					.getWorldDimensions().getX(); x += stepSize) {
				for (int y = 0; y < Binder.getComponent(Topology.class)
						.getWorldDimensions().getY(); y += stepSize) {

					// TODO add checkbox for cloudlets?
					Entry entry = database
							.getEntryFor(database.getSegmentID(x, y), false);
					if (entry == null) {
						continue;
					}

					// TODO add checkbox for upload/download toggle?

					// Latency
					double latencyFactor = (entry.getLatency(isUpload)
							- minLatency) / (maxLatency - minLatency);
					g2.setColor(new Color(255, 0, 0,
							10 + (int) (40 * latencyFactor)));
					g2.fillRect(VisualizationInjector.scaleValue(x),
							VisualizationInjector.scaleValue(y),
							VisualizationInjector.scaleValue(stepSize),
							VisualizationInjector.scaleValue(stepSize));

					// Drop-Prob
					g2.setColor(new Color(255, 0, 0, 10 + (int) (100
							* entry.getDropProbability(isUpload))));
					float strokeWidth = (float) entry
							.getDropProbability(isUpload);
					g2.setStroke(new BasicStroke((5 * strokeWidth)));
					g2.drawRect(VisualizationInjector.scaleValue(x),
							VisualizationInjector.scaleValue(y),
							VisualizationInjector.scaleValue(stepSize),
							VisualizationInjector.scaleValue(stepSize));
					g2.setColor(new Color(255, 255, 255, 255));
					g2.drawString("L: "
							+ entry.getLatency(isUpload) / Time.MILLISECOND
							+ " ms", VisualizationInjector.scaleValue(x + 10),
							VisualizationInjector.scaleValue(y + 15));
					g2.drawString("D: "
							+ (int) (entry.getDropProbability(isUpload) * 100)
							+ " %", VisualizationInjector.scaleValue(x + 10),
							VisualizationInjector.scaleValue(y + 35));
					g2.drawString("BW: "
							+ (int) (entry.getBandwidth(isUpload) / Rate.kbit_s)
							+ " kBit/s",
							VisualizationInjector.scaleValue(x + 10),
							VisualizationInjector.scaleValue(y + 55));
					if (!entry.isAvailable()) {
						g2.drawString("!DEAD!",
								VisualizationInjector.scaleValue(x + 30),
								VisualizationInjector.scaleValue(y + 70));
					}
				}
			}

		} catch (ComponentNotAvailableException e) {
			throw new AssertionError(
					"The Topology should be Binded in the TopologyFactory.");
		}

	}

	@Override
	public void mouseClicked(int x, int y, int modifier) {
		// 17 == Shift
		if (enableCellDestruction && modifier == 17) {
			int segID = database.getSegmentID(
					VisualizationInjector.originalValue(x),
					VisualizationInjector.originalValue(y));
			Entry entry = database.getEntryFor(segID, false);
			entry.setAvailability(!entry.isAvailable());
			needsRedraw = true;
		}
	}
}
