package com.notification.types;

import java.awt.Component;
import java.awt.Container;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JPanel;
import javax.swing.JWindow;

import com.notification.Notification;
import com.theme.WindowTheme;
import com.utils.MathUtils;

/**
 * A Notification which displays in a JWindow, handles click events, and allows subclasses to supply a JPanel. The
 * default Notification dimensions are set; if subclasses want to override this, they can do so in their constructors.
 */
public abstract class WindowNotification extends Notification {
	private JWindow m_window;
	private JPanel m_panel;
	private boolean m_closeOnClick;
	private MouseAdapter m_listener;

	private WindowTheme m_theme;

	private static final int DEFAULT_WIDTH = 300;
	private static final int DEFAULT_HEIGHT = 100;
	public static final String CLICKED = "clicked";
	public static final String SHOWN = "shown";
	public static final String HIDDEN = "hidden";

	public WindowNotification() {
		m_window = new JWindow();
		m_window.setAlwaysOnTop(true);

		m_listener = new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				fireListeners(CLICKED);
				if (m_closeOnClick)
					removeFromManager();
			}
		};

		setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
		setPanel(new JPanel());
	}

	protected JWindow getWindow() {
		return m_window;
	}

	protected void setPanel(JPanel panel) {
		if (m_panel != null) {
			m_window.remove(m_panel);
			m_panel.removeMouseListener(m_listener);
		}

		m_panel = panel;

		m_window.add(m_panel);
		m_panel.addMouseListener(m_listener);
	}

	/**
	 * @return whether or not the Notification should close when it's clicked
	 */
	public boolean isCloseOnClick() {
		return m_closeOnClick;
	}

	/**
	 * @param close
	 *
	 *            whether or not the Notification should close when it's clicked
	 */
	public void setCloseOnClick(boolean close) {
		m_closeOnClick = close;
	}

	protected WindowTheme getWindowTheme() {
		return m_theme;
	}

	/**
	 * Sets the theme of the WindowNotification. It is up to the subclasses how they want to interpret the "image"
	 * attribute of the theme.
	 *
	 * @param theme
	 *            the WindowTheme to set
	 */
	public void setWindowTheme(WindowTheme theme) {
		m_theme = theme;

		m_window.setBackground(theme.background);
		m_window.setForeground(theme.foreground);
		m_window.setOpacity((float) theme.opacity);
		m_window.setSize(theme.width, theme.height);

		m_panel.setBackground(theme.background);
		m_panel.setForeground(theme.foreground);

		for (Component comp : m_panel.getComponents()) {
			recursiveSetTheme(theme, comp);
		}
	}

	private void recursiveSetTheme(WindowTheme theme, Component comp) {
		comp.setBackground(theme.background);
		comp.setForeground(theme.foreground);

		if (comp instanceof Container) {
			Container container = (Container) comp;
			for (Component component : container.getComponents()) {
				recursiveSetTheme(theme, component);
			}
		}
	}

	@Override
	public int getX() {
		return m_window.getX();
	}

	@Override
	public int getY() {
		return m_window.getY();
	}

	@Override
	public void setLocation(int x, int y) {
		m_window.setLocation(x, y);
	}

	@Override
	public int getWidth() {
		return m_window.getWidth();
	}

	@Override
	public int getHeight() {
		return m_window.getHeight();
	}

	@Override
	public void setSize(int width, int height) {
		m_window.setSize(width, height);
	}

	/**
	 * Gets the opacity of the window between 0 and 1.
	 *
	 * @return the opacity of the window
	 */
	@Override
	public double getOpacity() {
		return m_window.getOpacity();
	}

	/**
	 * Sets the opacity, overriding the value given in the window theme.
	 *
	 * @param opacity
	 *            the opacity (clamped to between 0 and 1)
	 */
	@Override
	public void setOpacity(double opacity) {
		m_window.setOpacity((float) MathUtils.clamp(opacity, 0, 1));
	}

	@Override
	public void show() {
		m_window.setVisible(true);
		fireListeners(SHOWN);
	}

	@Override
	public void hide() {
		m_window.dispose();
		fireListeners(HIDDEN);
	}

	@Override
	public boolean isShown() {
		return m_window.isVisible();
	}
}