/*******************************************************************************
 * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
 * Copyright (c) 2011 The OpenNMS Group, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *******************************************************************************/
package org.jrobin.inspector;

import org.jrobin.core.*;
import org.jrobin.graph.RrdGraphDef;
import org.jrobin.graph.RrdGraph;
import org.jrobin.graph.RrdGraphInfo;
import org.jrobin.data.LinearInterpolator;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Date;

class GraphFrame extends JFrame {
	private static final long serialVersionUID = 1L;
	private static final Color COLOR = Color.RED;
	private static final int WIDTH = 400, HEIGHT = 240;
	private int deltaWidth = 0, deltaHeight = 0;

	private Color color = COLOR;
	private GraphPanel graphPanel = new GraphPanel();
	private JComboBox graphCombo = new JComboBox();
	private RrdGraph rrdGraph;

	private String sourcePath;
	private int dsIndex, arcIndex;

	GraphFrame(String sourcePath, int dsIndex, int arcIndex) {
		this.sourcePath = sourcePath;
		this.dsIndex = dsIndex;
		this.arcIndex = arcIndex;
		fillGraphCombo();
		constructUI();
		pack();
		//createRrdGraph();
		Util.placeWindow(this);
		setVisible(true);
	}

	private void createRrdGraph() {
		//System.out.println("Creating graph...");
		try {
			RrdDb rrdDb = new RrdDb(sourcePath, true);
			RrdDef rrdDef;
			long[] timestamps;
			double[] values;
			String dsName;
			long t1, t2;
			try {
				Datasource ds = rrdDb.getDatasource(dsIndex);
				Archive arc = rrdDb.getArchive(arcIndex);
				Robin robin = arc.getRobin(dsIndex);
				dsName = ds.getDsName();
				t1 = arc.getStartTime();
				t2 = arc.getEndTime();
				long step = arc.getArcStep();
				int count = robin.getSize();
				timestamps = new long[count];
				for (int i = 0; i < count; i++) {
					timestamps[i] = t1 + i * step;
				}
				values = robin.getValues();
				rrdDef = rrdDb.getRrdDef();
			}
			finally {
				rrdDb.close();
			}
			RrdGraphDef rrdGraphDef = new RrdGraphDef();
			rrdGraphDef.setTimeSpan(t1, t2);
			rrdGraphDef.setImageFormat("png");
			rrdGraphDef.setTitle(rrdDef.getDsDefs()[dsIndex].dump() + " " +
					rrdDef.getArcDefs()[arcIndex].dump());
			LinearInterpolator linearInterpolator = new LinearInterpolator(timestamps, values);
			linearInterpolator.setInterpolationMethod(LinearInterpolator.INTERPOLATE_RIGHT);
			rrdGraphDef.datasource(dsName, linearInterpolator);
			rrdGraphDef.area(dsName, color, dsName + "\\r");
			rrdGraphDef.comment("START: " + new Date(t1 * 1000L) + "\\r");
			rrdGraphDef.comment("END: " + new Date(t2 * 1000L) + "\\r");
			int width = graphPanel.getWidth(), height = graphPanel.getHeight();
			rrdGraphDef.setWidth(width + deltaWidth);
			rrdGraphDef.setHeight(height + deltaHeight);
			rrdGraph = new RrdGraph(rrdGraphDef);
			if (deltaWidth == 0 && deltaHeight == 0) {
				RrdGraphInfo info = rrdGraph.getRrdGraphInfo();
				deltaWidth = graphPanel.getWidth() - info.getWidth();
				deltaHeight = graphPanel.getHeight() - info.getHeight();
				if (deltaWidth != 0 && deltaHeight != 0) {
					createRrdGraph(); // recursion is divine!
				}
			}
		}
		catch (IOException e) {
			Util.error(this, e);
		}
		catch (RrdException e) {
			Util.error(this, e);
		}
	}

	private void fillGraphCombo() {
		try {
			RrdDb rrdDb = new RrdDb(sourcePath, true);
			try {
				RrdDef rrdDef = rrdDb.getRrdDef();
				final DsDef[] dsDefs = rrdDef.getDsDefs();
				final ArcDef[] arcDefs = rrdDef.getArcDefs();
				GraphComboItem[] items = new GraphComboItem[rrdDef.getDsCount() * rrdDef.getArcCount()];
				int selectedItem = -1;
				for (int i = 0, k = 0; i < rrdDef.getDsCount(); i++) {
					for (int j = 0; j < rrdDef.getArcCount(); k++, j++) {
						String description = dsDefs[i].dump() + " " + arcDefs[j].dump();
						items[k] = new GraphComboItem(description, i, j);
						if (i == dsIndex && j == arcIndex) {
							selectedItem = k;
						}
					}
				}
				graphCombo.setModel(new DefaultComboBoxModel(items));
				graphCombo.setSelectedIndex(selectedItem);
			}
			finally {
				rrdDb.close();
			}
		}
		catch (IOException e) {
			Util.error(this, e);
		}
		catch (RrdException e) {
			Util.error(this, e);
		}
	}

	private void constructUI() {
		setTitle(new File(sourcePath).getName());
		JPanel content = (JPanel) getContentPane();
		content.setLayout(new BorderLayout(0, 0));
		content.add(graphCombo, BorderLayout.NORTH);
		graphPanel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
		content.add(graphPanel, BorderLayout.CENTER);
		JPanel southPanel = new JPanel();
		southPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
		JButton colorButton = new JButton("Change graph color");
		southPanel.add(colorButton);
		colorButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				changeColor();
			}
		});
		JButton saveButton = new JButton("Save graph");
		saveButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				saveGraph();
			}
		});
		southPanel.add(Box.createHorizontalStrut(3));
		southPanel.add(saveButton);
		content.add(southPanel, BorderLayout.SOUTH);
		// EVENT HANDLERS
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				closeWindow();
			}
		});
		addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				createRrdGraph();
				graphPanel.repaint();
			}
		});
		graphCombo.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (e.getStateChange() == ItemEvent.SELECTED) {
					GraphComboItem item = (GraphComboItem) e.getItem();
					dsIndex = item.getDsIndex();
					arcIndex = item.getArcIndex();
					createRrdGraph();
					graphPanel.repaint();
				}
			}
		});
	}

	private void closeWindow() {
		Util.dismissWindow(this);
	}

	private void changeColor() {
		final JColorChooser picker = new JColorChooser(color);
		ActionListener okListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				color = picker.getColor();
				createRrdGraph();
				repaint();
			}
		};
		JColorChooser.createDialog(this, "Select color", true, picker, okListener, null).setVisible(true);
	}

	private void saveGraph() {
		JFileChooser chooser = new JFileChooser();
		FileFilter filter = new FileFilter() {
			public boolean accept(File f) {
				return f.isDirectory() || f.getAbsolutePath().toLowerCase().endsWith(".png");
			}

			public String getDescription() {
				return "PNG images";
			}
		};
		chooser.setFileFilter(filter);
		int returnVal = chooser.showSaveDialog(this);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			try {
				File selectedFile = chooser.getSelectedFile();
				String path = selectedFile.getAbsolutePath();
				if (!path.toLowerCase().endsWith(".png")) {
					path += ".png";
					selectedFile = new File(path);
				}
				if (selectedFile.exists()) {
					// ask user to overwrite
					String message = "File [" + selectedFile.getName() +
							"] already exists. Do you want to overwrite it?";
					int answer = JOptionPane.showConfirmDialog(this,
							message, "File exists", JOptionPane.YES_NO_OPTION);
					if (answer == JOptionPane.NO_OPTION) {
						return;
					}
				}
				String absolutePath = selectedFile.getAbsolutePath();
				byte[] data = rrdGraph.getRrdGraphInfo().getBytes();
				RandomAccessFile f = new RandomAccessFile(absolutePath, "rw");
				try {
					f.write(data);
				}
				finally {
					f.close();
				}
			}
			catch (IOException e) {
				Util.error(this, "Could not save graph to file:\n" + e);
			}
		}
	}

	class GraphPanel extends JPanel {
		private static final long serialVersionUID = 1L;
		public void paintComponent(Graphics g) {
			rrdGraph.render(g);
		}
	}

	private static final class GraphComboItem {
		private String description;
		private int dsIndex, arcIndex;

		GraphComboItem(String description, int dsIndex, int arcIndex) {
			this.description = description;
			this.dsIndex = dsIndex;
			this.arcIndex = arcIndex;
		}

		public String toString() {
			return description;
		}

		int getDsIndex() {
			return dsIndex;
		}

		int getArcIndex() {
			return arcIndex;
		}
	}
}
