package jrummikub.server;

import java.net.BindException;
import java.net.InetAddress;

import org.apache.vysper.mina.TCPEndpoint;
import org.apache.vysper.storage.OpenStorageProviderRegistry;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.authorization.UserAuthorization;
import org.apache.vysper.xmpp.modules.extension.xep0045_muc.MUCModule;
import org.apache.vysper.xmpp.modules.extension.xep0045_muc.storage.InMemoryOccupantStorageProvider;
import org.apache.vysper.xmpp.modules.extension.xep0045_muc.storage.InMemoryRoomStorageProvider;
import org.apache.vysper.xmpp.modules.roster.persistence.MemoryRosterManager;
import org.apache.vysper.xmpp.server.XMPPServer;

/**
 * Implements a simple XMPP server with a global server password
 */
public class DedicatedServer {
	private volatile String serverPassword;
	private String hostName;

	/**
	 * Creates a new dedicated server with the specified password
	 * 
	 * @param serverPassword
	 *            the password
	 */
	public DedicatedServer(String serverPassword) {
		this.serverPassword = serverPassword;
		try {
			InetAddress addr = InetAddress.getLocalHost();
			hostName = addr.getCanonicalHostName();
		} catch (Exception e) {
			hostName = "localhost";
		}

	}

	/**
	 * Change the current server password
	 * 
	 * @param password
	 *            the new server password
	 */
	public void setServerPassword(String password) {
		serverPassword = password;
	}

	/**
	 * Get the current server password
	 * 
	 * @return the current server password
	 */
	public String getServerPassword() {
		return serverPassword;
	}

	/**
	 * Getter for host name
	 * 
	 * @return host name
	 */
	public String getHostName() {
		return hostName;
	}

	/**
	 * Result of server startup attempt
	 */
	public enum ServerStatus {
		/** Server successfully started */
		STARTED,
		/** Server already running on this machine */
		ALREADY_RUNNING,
		/** Other error */
		ERROR
	}

	/**
	 * Start the server
	 * 
	 * @return Whether we could start the server
	 */
	public ServerStatus start() {
		try {
			XMPPServer server = new XMPPServer(hostName);

			OpenStorageProviderRegistry providerRegistry = new OpenStorageProviderRegistry();
			providerRegistry.add(new ServerPasswordAuthorization());
			providerRegistry.add(new MemoryRosterManager());
			providerRegistry.add(new InMemoryRoomStorageProvider());
			providerRegistry.add(new InMemoryOccupantStorageProvider());

			server.setStorageProviderRegistry(providerRegistry);
			server.addEndpoint(new TCPEndpoint());

			server.setTLSCertificateInfo(
					getClass().getResource(
							"/jrummikub/resource/bogus_mina_tls.cert")
							.openStream(), "boguspw");

			server.start();
			MUCModule muc = new MUCModule("play");
			server.addModule(muc);
			return ServerStatus.STARTED;
		} catch (BindException e) {
			return ServerStatus.ALREADY_RUNNING;
		} catch (Exception e) {
			e.printStackTrace();
			return ServerStatus.ERROR;
		}

	}

	/**
	 * Allow authorization using a single password for all users
	 */
	public class ServerPasswordAuthorization implements UserAuthorization {
		@Override
		public boolean verifyCredentials(Entity entity, String password,
				Object credentials) {
			return password.equals(serverPassword);
		}

		@Override
		public boolean verifyCredentials(String entity, String password,
				Object credentials) {
			return password.equals(serverPassword);
		}
	}

	/**
	 * Main for a simple command line dedicated server
	 * 
	 * @param args
	 *            first argument specifies the server password, it is
	 *            "jrummikub" when none is specified
	 */
	public static void main(String[] args) {
		DedicatedServer server = new DedicatedServer(args.length >= 1 ? args[0]
				: "jrummikub");
		System.out.println("Server hostname is " + server.getHostName());
		try {
			switch (server.start()) {
			case ALREADY_RUNNING:
				System.out.println("Server already running");
				// fall through
			case ERROR:
				System.exit(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
