package de.reinhardt_karlheinz.pcc.interfaces;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import de.reinhardt_karlheinz.pcc.enums.ConnectionType;
import de.reinhardt_karlheinz.pcc.events.PCCMessageEvent;
import de.reinhardt_karlheinz.pcc.events.PCConnectionEvent;
import de.reinhardt_karlheinz.pcc.listeners.PCCMsgListener;
import de.reinhardt_karlheinz.pcc.listeners.PCConnectionListener;

public abstract class PCCConnection {

	private ConnectionType connType;

	// ## OTHER
	private boolean connection;

	// ### LISTENER STUFF ###

	private ArrayList<PCConnectionListener> _connectionListeners = new ArrayList<PCConnectionListener>();

	private ArrayList<PCCMsgListener> _messageListeners = new ArrayList<PCCMsgListener>();

	public PCCConnection(ConnectionType c) {
		connType = c;
		connection = false;
		addShutdownHook();
	}

	protected abstract void addShutdownHook();

	// ## server
	protected abstract void sendString(String s);

	public void sendCmd(String cmd) {
		sendString("\\" + cmd);
	}

	public void sendMsg(String msg) {
		// TODO msg and cmd format
		if (isConnected()) {
			System.out.println("pccConn: " + msg);
			sendString(msg);
		}
	}

	/**
	 * evaluates input string<br>
	 * throws MsgReceivedEvent and CommandReceivedEvent for the listeners
	 * 
	 * @param s
	 *            to be evaluated String
	 */
	protected void evaluateInputString(String s) {
		if (s != null && s.isEmpty() == false && "".equals(s) == false) {
			if (s.startsWith("\\")) {
				fireCommandReceivedEvent(s.substring(1));
			} else {
				fireMsgReceivedEvent(s);
			}
		}
	}

	/**
	 * starts server Thread and will fire ConnectionEstablishedEvent when
	 * connection is established.
	 * 
	 * 
	 * @return true when connection established else false
	 * @throws IOException
	 */
	public abstract boolean startServer() throws IOException;

	/**
	 * will close connections and fire connections lost event
	 * 
	 * @throws IOException
	 */
	public abstract void stopServer() throws IOException;

	/**
	 * is called by stopServer()
	 * 
	 * @throws IOException
	 */
	protected abstract void closeConnections() throws IOException;

	// ### LISTENER STUFF ###
	public void addPCConnectionListener(PCConnectionListener listener) {
		_connectionListeners.add(listener);
	}

	public void removePCConnectionListener(PCConnectionListener listener) {
		_connectionListeners.remove(listener);
	}

	public void addPCCMsgListener(PCCMsgListener listener) {
		_messageListeners.add(listener);
	}

	public void removePCCMsgListener(PCCMsgListener listener) {
		_messageListeners.remove(listener);
	}

	/**
	 * fires ConnectionEstablishedEvent
	 */
	protected void fireConnectionEstablishedEvent() {
		PCConnectionEvent event = new PCConnectionEvent(this, connType);
		Iterator<PCConnectionListener> i = _connectionListeners.iterator();
		while (i.hasNext()) {
			i.next().onPCConnectionEstablished(event);
		}
	}

	/**
	 * fires ConnectionLostEvent<br>
	 * should only be called by method stopServer()
	 */
	protected void fireConnectionLostEvent() {
		PCConnectionEvent event = new PCConnectionEvent(this, connType);
		Iterator<PCConnectionListener> i = _connectionListeners.iterator();
		while (i.hasNext()) {
			i.next().onPCConnectionLost(event);
		}
	}

	/**
	 * fires MsgReceivedEvent
	 */
	private void fireMsgReceivedEvent(String msg) {
		PCCMessageEvent event = new PCCMessageEvent(this, msg);
		Iterator<PCCMsgListener> i = _messageListeners.iterator();
		while (i.hasNext()) {
			i.next().onMsgReceived(event);
		}
	}

	/**
	 * fires CommandReceivedEvents
	 */
	private void fireCommandReceivedEvent(String msg) {
		PCCMessageEvent event = new PCCMessageEvent(this, msg);
		Iterator<PCCMsgListener> i = _messageListeners.iterator();
		while (i.hasNext()) {
			i.next().onCommandReceived(event);
		}
	}

	// ################

	protected void setConnected(boolean b) {
		connection = b;
	}

	public boolean isConnected() {
		return connection;
	}

	protected abstract boolean checkIfConnected();

	public ConnectionType getConnectionType() {
		return connType;
	}
}
