/*
	Copyright 2011 Daniel Pletea

	This file is part of T-Nano.

	T-Nano 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
	(at your option) any later version.

	T-Nano 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 T-Nano.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Stroke;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JTextField;

/**
 * 
 * @author Daniel Pletea
 * 
 */
public class Singleton implements Definitions {

	private static final Singleton INSTANCE = new Singleton();

	// the exporter of pdfs
	PdfExporter pdfExporter;

	// the sounds for the circuits
	private AudioClip insertSound;
	private AudioClip deleteSound;

	// indicates the number of Untitled files opened in the program
	private int nrUntitled;

	// current theme is described by this colors
	private List<Theme> themes;
	private int crtThemeIndex;
	private Theme crtTheme;

	/**
	 * Private constructor prevents instantiation from other classes
	 */
	private Singleton() {
		try {
			// try creating a pdf exporter
			pdfExporter = new PdfExporter();

			// try creting insert sound
			URL url = getClass().getResource("sounds/insert.wav");
			insertSound = Applet.newAudioClip(url);

			// try creting delete sound
			url = getClass().getResource("sounds/delete.wav");
			deleteSound = Applet.newAudioClip(url);

		} catch (Exception e) {
			e.printStackTrace();
		}

		nrUntitled = 1;

		themes = new ArrayList<Theme>();
		themes.add(new Theme("images/theme0-tileBackground.png", Color.BLACK,
				"images/theme0-circuitBackground.png", Color.GRAY));
		themes.add(new Theme("images/theme1-tileBackground.png", Color.BLACK,
				"images/theme1-circuitBackground.png", Color.GRAY));

		crtThemeIndex = 1;
		crtTheme = themes.get(crtThemeIndex);
	}

	public static Singleton getInstance() {
		return INSTANCE;
	}

	/**
	 * this function creates a JMenuItem with the name and actionlistener
	 * specified as parameters. The paramter parent represent the component that
	 * wil incorporate the object created by this function
	 * 
	 * @param name
	 * @param parent
	 * @param al
	 * @return
	 */
	public JMenuItem createMenuItem(String name, JComponent parent,
			ActionListener al) {
		JMenuItem menuItem = new JMenuItem(name);
		menuItem.addActionListener(al);
		parent.add(menuItem);
		return menuItem;
	}

	/**
	 * this function creates a JMenuItem with size given as parameter
	 * 
	 * @param size
	 * @param x
	 *            -> the x coordinate for setting the bounds of the textField
	 * @param y
	 *            -> the y coordinate for setting the bounds of the textField
	 * @param width
	 *            -> the width for setting the bounds of the textField
	 * @param height
	 *            -> the height for setting the bounds of the textField
	 * @param parent
	 *            -> the JComponent that will have thie object created by this
	 *            function
	 * @return
	 */
	public JTextField createTextField(int size, int x, int y, int width,
			int height, Container parent) {
		JTextField textField = new JTextField(size);
		textField.setBounds(x, y, width, height);
		parent.add(textField);

		return textField;
	}

	public JButton createButton(String name, int x, int y, int width,
			int height, Container parent, ActionListener al) {
		JButton button = new JButton(name);
		button.setBounds(x, y, width, height);
		button.addActionListener(al);
		parent.add(button);

		return button;
	}

	public JLabel createLabel(String text, int x, int y, int width, int height,
			Color color, Container parent) {
		JLabel label = new JLabel();
		label.setBounds(x, y, width, height);
		if (color != null)
			label.setForeground(color);
		label.setText(text);
		parent.add(label);

		return label;
	}

	public JComboBox createComboBox(int x, int y, int width, int height,
			Container parent) {
		JComboBox comboBox = new JComboBox();
		comboBox.setBounds(x, y, width, height);
		parent.add(comboBox);
		return comboBox;
	}

	public String tranformName(String string) {
		string = string.substring(string.lastIndexOf('\\') + 1);
		string = string.substring(string.lastIndexOf('/') + 1);

		return string;
	}

	/**
	 * this function creates an image with the text in it
	 * 
	 * @param text
	 * @return
	 */
	public BufferedImage createTextImage(String text) {

		BufferedImage image = createBasic(crtTheme.getTileBackground(),
				crtTheme.getCircuitBackground());
		Graphics2D g = image.createGraphics();

		g.setColor(crtTheme.getWireColor());
		g.setFont(new Font("SansSerif", Font.BOLD, FONT_SIZE));
		g.drawString(text, IMAGE_DIMENSION / 2 - (text.length() * CHAR_WIDTH)
				/ 2, IMAGE_DIMENSION / 2 + FONT_SIZE / 4);

		return image;
	}

	/**
	 * this function creates the basic image for tiles
	 * 
	 * @param background
	 * @param border
	 * @return
	 */
	public BufferedImage createBasic(String background, String border) {
		// BufferedImage image = new BufferedImage(IMAGE_DIMENSION,
		// IMAGE_DIMENSION, BufferedImage.TYPE_INT_RGB);
		URL url;

		BufferedImage image = null;
		try {

			url = getClass().getResource(border);
			image = ImageIO.read(url);
			Graphics2D g = image.createGraphics();

			url = getClass().getResource(background);
			BufferedImage img = ImageIO.read(url);
			g.drawImage(img, BORDER_TILE_DIM, BORDER_TILE_DIM, img.getWidth()
					- 2 * BORDER_TILE_DIM, img.getHeight() - 2
					* BORDER_TILE_DIM, null);

		} catch (IOException e) {
			e.printStackTrace();
		}
		;

		return image;

	}

	/**
	 * this function creates the tiles with wires
	 * 
	 * @param drawing
	 * @return
	 */
	private BufferedImage createWired(int drawing) {
		BufferedImage image = createBasic(crtTheme.getTileBackground(),
				crtTheme.getCircuitBackground());
		Graphics2D g = image.createGraphics();
		g.setColor(crtTheme.getWireColor());

		if (drawing / 8 == 1) {
			g = drawVerticalUp(g);
			drawing = drawing % 8;
		}
		if (drawing / 4 == 1) {
			g = drawHorizontalRight(g);
			drawing = drawing % 4;
		}
		if (drawing / 2 == 1) {
			g = drawVerticalDown(g);
			drawing = drawing % 2;
		}
		if (drawing > 0)
			g = drawHorizontalLeft(g);

		return image;
	}

	/**
	 * this function creates a transistor
	 * 
	 * @param text
	 *            -> is "p" or "n". It depends on the type of the transistor
	 * @return
	 */
	private BufferedImage createTransistor(String text) {
		BufferedImage image = createBasic(crtTheme.getTileBackground(),
				crtTheme.getCircuitBackground());
		Graphics2D g = image.createGraphics();

		g.setColor(crtTheme.getSemiconductorColor());
		g = drawVerticalUp(g);
		g = drawVerticalDown(g);

		g.setColor(crtTheme.getWireColor());
		g = drawHorizontalLeft(g);

		// write the text
		g.setFont(new Font("SansSerif", Font.BOLD, FONT_SIZE));
		g.drawString(text, IMAGE_DIMENSION / 2 + FONT_SIZE / 2, IMAGE_DIMENSION
				/ 2 - FONT_SIZE / 4);

		return image;
	}

	/**
	 * draw a wire from left to the middle of the tile
	 * @param g
	 * @return
	 */
	private Graphics2D drawHorizontalLeft(Graphics2D g) {
		int halWireWidth = WIRE_DIM / 2;
		int excess = 3 * WIRE_DIM / 2;
		int length = IMAGE_DIMENSION / 2;
		int border = BORDER_TILE_DIM;

		g.fillRect(border, length - halWireWidth, length + excess - border,
				WIRE_DIM);
		return g;
	}

	/**
	 * draw a wire from right to the middle of the tile
	 * @param g
	 * @return
	 */
	private Graphics2D drawHorizontalRight(Graphics2D g) {
		int halWireWidth = WIRE_DIM / 2;
		int excess = 3 * WIRE_DIM / 2;
		int length = IMAGE_DIMENSION / 2;
		int border = BORDER_TILE_DIM;

		g.fillRect(length - excess, length - halWireWidth, length + excess
				- border, WIRE_DIM);
		return g;
	}

	/**
	 * draw a wire from top to the middle of the tile
	 * @param g
	 * @return
	 */
	private Graphics2D drawVerticalUp(Graphics2D g) {
		int halWireWidth = WIRE_DIM / 2;
		int excess = 3 * WIRE_DIM / 2;
		int length = IMAGE_DIMENSION / 2;
		int border = BORDER_TILE_DIM;

		g.fillRect(length - halWireWidth, border, WIRE_DIM, length + excess
				- border);
		return g;
	}

	/**
	 * draw a wire from bottom to the middle of the tile
	 * @param g
	 * @return
	 */
	private Graphics2D drawVerticalDown(Graphics2D g) {
		int halWireWidth = WIRE_DIM / 2;
		int excess = 3 * WIRE_DIM / 2;
		int length = IMAGE_DIMENSION / 2;
		int border = BORDER_TILE_DIM;

		g.fillRect(length - halWireWidth, length - excess, WIRE_DIM, length
				+ excess - border);
		return g;
	}

	/**
	 * this is the function that creates all the tiles
	 * @return
	 */
	public List<BufferedImage> createTiles() {
		int[] vector = { 5, 10, 3, 9, 12, 6, 13, 7, 11, 14, 15 };

		List<BufferedImage> tiles = new ArrayList<BufferedImage>();
		BufferedImage image;

		// blank tile
		image = createBasic(crtTheme.getTileBackground(),
				crtTheme.getCircuitBackground());
		tiles.add(image);

		// tranzistor p
		image = createTransistor("p");
		tiles.add(image);

		// tranzistor n
		image = createTransistor("n");
		tiles.add(image);

		for (int i = 0; i < vector.length; i++) {
			image = createWired(vector[i]);
			tiles.add(image);
		}

		// passing wire
		image = createBasic(crtTheme.getTileBackground(),
				crtTheme.getCircuitBackground());
		Graphics2D g = image.createGraphics();

		g.setColor(crtTheme.getWireColor());

		g.fillRect(IMAGE_DIMENSION / 2 - WIRE_DIM / 2, BORDER_TILE_DIM,
				WIRE_DIM, IMAGE_DIMENSION / 4);
		g.fillRect(IMAGE_DIMENSION / 2 - WIRE_DIM / 2, 3 * IMAGE_DIMENSION / 4
				+ BORDER_TILE_DIM, WIRE_DIM, IMAGE_DIMENSION / 4);
		g = drawHorizontalLeft(g);
		g = drawHorizontalRight(g);

		Stroke oldStroke = g.getStroke();
		g.setStroke(new BasicStroke(WIRE_DIM, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND)); // Vertex join style
		g.drawArc(50, 50, 100, 100, 90, 180);
		g.setStroke(oldStroke);

		tiles.add(image);

		// create the Vccs and the Vdds
		String[] strings = { "Vcc", "Vdd", "In", "Out" };

		for (int i = 0; i < strings.length; i++) {
			image = createTextImage(strings[i]);
			tiles.add(image);
		}

		// create the tile that is used for switching to select mode
		try {
			URL url = getClass().getResource("images/19.png");
			image = ImageIO.read(url);
			tiles.add(image);
		} catch (IOException e) {
			e.printStackTrace();
		}

		image = createBasic(crtTheme.getCircuitBackground(),
				crtTheme.getTileBackground());
		tiles.add(image);

		return tiles;
	}

	/**
	 * this functions changese the theme for a gui
	 */
	public void changeTheme() {
		crtTheme = themes.get(crtThemeIndex);
		for (int i = 0; i < Main.guiList.size(); i++) {
			System.out.println(i);
			Gui gui = Main.guiList.get(i);
			gui.createToolbar();
			gui.createToolsMenu();

			if (gui instanceof GuiCircuit)
				((GuiCircuit) gui).changeTheme();

		}
	}

	/**
	 * this function writes the awtImg into a pdf
	 * @param file
	 * @param awtImg
	 * @param height
	 * @param width
	 */
	public void export(String file, Image awtImg, int height, int width) {
		pdfExporter.exportToPdf(file, awtImg, height, width);
	}

	public void insertSoundPlay() {
		insertSound.play();
	}

	public void deleteSoundPlay() {
		deleteSound.play();
	}

	public int getNrUntitled() {
		return nrUntitled;
	}

	public void setNrUntitled(int nrUntitled) {
		this.nrUntitled = nrUntitled;
	}

	public int getCrtThemeIndex() {
		return crtThemeIndex;
	}

	public void setCrtThemeIndex(int crtThemeIndex) {
		this.crtThemeIndex = crtThemeIndex;
	}

}
