package fr.insa.clavardator.client.config;

import fr.insa.clavardator.client.server.ServerType;
import org.json.JSONException;
import org.json.JSONObject;

@SuppressWarnings("FieldCanBeLocal")
public class Config {

	private final String SERVER_KEY = "serveur";
	private final String LOCAL_KEY = "local";
	private ServerConfig serverConfig;
	private LocalConfig localConfig;

	public Config() {
		serverConfig = new ServerConfig();
		localConfig = new LocalConfig();
	}

	public Config(JSONObject obj) {
		this();
		if (obj.has(SERVER_KEY)) {
			this.serverConfig = new ServerConfig(obj.getJSONObject(SERVER_KEY));
		}
		if (obj.has(LOCAL_KEY)) {
			this.localConfig = new LocalConfig(obj.getJSONObject(LOCAL_KEY));
		}
	}

	public ServerConfig getServerConfig() {
		return serverConfig;
	}

	public LocalConfig getLocalConfig() {
		return localConfig;
	}

	@SuppressWarnings("FieldCanBeLocal")
	public static class ServerConfig {
		public static final int DEFAULT_PRESENCE_PORT = 35650;
		public static final int DEFAULT_PROXY_PORT = 35750;
		private final String ENABLED_KEY = "actif";
		private final String URI_KEY = "uri";
		private final String TYPE_KEY = "type";
		private final String PORTS_KEY = "ports";
		private final String PORT_PRESENCE_KEY = "presence";
		private final String PORT_PROXY_KEY = "proxy";
		private JSONObject obj;
		private boolean enabled;
		private String uri;
		private ServerType type;
		private int presencePort;
		private int proxyPort;

		/**
		 * Basic constructor setting the default server configuration
		 */
		public ServerConfig() {
			enabled = false;
			uri = "";
			type = ServerType.INSA;
		}

		/**
		 * Tries to read the given JSON object to extract custom user configuration
		 *
		 * @param obj THe JSON object to read config from
		 */
		public ServerConfig(JSONObject obj) {
			this();
			this.obj = obj;
			readServerEnabled();
			if (enabled) {
				readServerUri();
				if (uri.isBlank()) {
					enabled = false;
				} else {
					readServerType();
					readServerPorts();
				}
			}
		}

		/**
		 * Reads if the server is enabled.
		 * Uses false by default.
		 */
		private void readServerEnabled() {
			try {
				enabled = obj.getInt(ENABLED_KEY) == 1;
			} catch (JSONException e) {
				enabled = false;
			}
		}

		/**
		 * Reads the server URI.
		 * Uses the empty string as default
		 */
		private void readServerUri() {
			try {
				uri = obj.getString(URI_KEY);
			} catch (JSONException e) {
				uri = "";
			}
		}

		/**
		 * Reads the server type.
		 * Uses INSA as default.
		 */
		private void readServerType() {
			try {
				type = obj.getEnum(ServerType.class, TYPE_KEY);
			} catch (JSONException e) {
				type = ServerType.INSA;
			}
		}

		private void readServerPorts() {
			try {
				JSONObject portsObj = obj.getJSONObject(PORTS_KEY);
				presencePort = portsObj.getInt(PORT_PRESENCE_KEY);
				proxyPort = portsObj.getInt(PORT_PROXY_KEY);
			} catch (JSONException e) {
				presencePort = DEFAULT_PRESENCE_PORT;
				proxyPort = DEFAULT_PROXY_PORT;
			}
		}

		/**
		 * Checks if the presence server is enabled.
		 * If this returns false, all other data returned by this object can be ignored.
		 *
		 * @return True if enabled, false otherwise.
		 */
		public boolean isEnabled() {
			return enabled;
		}

		public String getUri() {
			return uri;
		}

		public ServerType getType() {
			return type;
		}

		public int getPresencePort() {
			return presencePort;
		}

		public int getProxyPort() {
			return proxyPort;
		}
	}

	@SuppressWarnings("FieldCanBeLocal")
	public static class LocalConfig {
		public static final int DEFAULT_TCP_PORT = 31598;
		private final String ENABLED_KEY = "actif";
		private final String PORT_KEY = "port";
		private JSONObject obj;
		private boolean enabled;
		private int port;

		/**
		 * Basic constructor setting the default local configuration
		 */
		public LocalConfig() {
			enabled = true;
			port = DEFAULT_TCP_PORT;
		}

		/**
		 * Tries to read the given JSON object to extract custom user configuration
		 *
		 * @param obj THe JSON object to read config from
		 */
		public LocalConfig(JSONObject obj) {
			this();
			this.obj = obj;
			readEnabled();
			if (enabled) {
				readServerPort();
			}
		}

		/**
		 * Reads if local mode is enabled.
		 * Uses true by default.
		 */
		private void readEnabled() {
			try {
				enabled = obj.getInt(ENABLED_KEY) == 1;
			} catch (JSONException e) {
				enabled = true;
			}
		}

		private void readServerPort() {
			try {
				port = obj.getInt(PORT_KEY);
			} catch (JSONException e) {
				port = DEFAULT_TCP_PORT;
			}
		}

		/**
		 * Checks if the local mode is enabled.
		 * If this returns false, all other data returned by this object can be ignored.
		 *
		 * @return True if enabled, false otherwise.
		 */
		public boolean isEnabled() {
			return enabled;
		}

		public int getPort() {
			return port;
		}
	}
}
