package hiro.yoshioka.sql.params;

import hiro.yoshioka.sdh.DatabaseType;
import hiro.yoshioka.sql.HSQL;
import hiro.yoshioka.util.StringUtil;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ConnectionSettingBean implements Serializable {
	private static final long serialVersionUID = 5792695663300493365L;
	private Set<String> jdbcDriverFilePathSet = new LinkedHashSet<String>();
	private Map<Long, ConnectionProperties> connectionMap = new LinkedHashMap<Long, ConnectionProperties>();
	private static final String LOCAL_HSQL_SERVER_DISP = "LocalHsqlServer";
	protected transient Log log = LogFactory.getLog(getClass());

	public ConnectionSettingBean() {
	}

	public ConnectionProperties getLocalHsqlServerConnection() {
		ConnectionProperties hsqlConnection = getConnectionPropertiesByDisplay(LOCAL_HSQL_SERVER_DISP);
		if (hsqlConnection == null) {
			createLocalHsqlServerConnetionDefinition();
			return getConnectionPropertiesByDisplay(LOCAL_HSQL_SERVER_DISP);
		}
		DBUserPass du = hsqlConnection.getAuthenticate();
		if (du == null || StringUtil.isEmpty(du.getUser())) {
			removeLocalHsqlServerConnectionDefinition();
			createLocalHsqlServerConnetionDefinition();
			return getConnectionPropertiesByDisplay(LOCAL_HSQL_SERVER_DISP);
		}
		return hsqlConnection;
	}

	public void createLocalHsqlServerConnetionDefinition() {
		ConnectionProperties hsql = new ConnectionProperties();
		hsql.setURLString(HSQL.getSuggestURL());
		hsql.setAuthenticate(null, "sa", null);
		hsql.setHost("127.0.0.1:9003");
		hsql.setDisplayString(LOCAL_HSQL_SERVER_DISP);
		hsql.setSerializableDBRoot(true);
		hsql.setDriverName(DatabaseType.HSQL.getDriverName());
		hsql.setCaptureResourceAfterTheConnectProcess(true);
		System.err.println("createLocalHsqlServerConnetionDefinition " + hsql);
		connectionMap.put(hsql.getCreated(), hsql);
	}

	private void removeLocalHsqlServerConnectionDefinition() {
		Long removeKey = null;
		for (Entry<Long, ConnectionProperties> entry : connectionMap.entrySet()) {
			ConnectionProperties p = entry.getValue();
			if (p.getDisplayString().equals(LOCAL_HSQL_SERVER_DISP)) {
				removeKey = entry.getKey();
			}
		}
		if (removeKey != null) {
			connectionMap.remove(removeKey);
		}
	}

	public ConnectionProperties getConnectionPropertiesByDisplay(
			String displayString) {
		for (ConnectionProperties p : connectionMap.values()) {
			if (p.getDisplayString().equals(displayString)) {
				return p;
			}
		}
		log.warn(String.format("mapSize[%d] DisplayString[%s] return null",
				connectionMap.size(), displayString));
		return null;
	}

	public Map<Long, ConnectionProperties> getConnectionMap() {
		return connectionMap;
	}

	public void setConnectionMap(Map<Long, ConnectionProperties> connectionMap) {
		this.connectionMap = connectionMap;
	}

	public void addConnectionProperties(ConnectionProperties properties) {
		connectionMap.put(properties.getCreated(), properties);
	}

	public void initConnectionStatus() {
		for (ConnectionProperties p : getConnectedConnectionSet()) {
			p.setConnected(false);
		}
	}

	public boolean changeConnectionProperties(
			Collection<ConnectionProperties> newProperties) throws IOException {
		boolean ret = false;
		connectionMap.clear();

		for (ConnectionProperties target : newProperties) {
			addConnectionProperties(target);
		}
		return ret;
	}

	public Collection<ConnectionProperties> getConnectionSet() {
		return connectionMap.values();
	}

	public Collection<ConnectionProperties> getConnectedConnectionSet() {
		if (connectionMap.size() == 0) {
			return Collections.EMPTY_SET;
		}
		Set<ConnectionProperties> ret = new LinkedHashSet<ConnectionProperties>();
		for (ConnectionProperties p : connectionMap.values()) {
			if (p.isConnected()) {
				ret.add(p);
			}
		}
		return ret;
	}

	public Collection<ConnectionProperties> getDisConnectedConnectionSet() {
		if (connectionMap.size() == 0) {
			return Collections.EMPTY_SET;
		}
		Set<ConnectionProperties> ret = new LinkedHashSet<ConnectionProperties>();
		for (ConnectionProperties p : connectionMap.values()) {
			if (!p.isConnected()) {
				ret.add(p);
			}
		}
		return ret;
	}

	public String[] getDisplayStrings() {
		List<String> retList = new ArrayList<String>();
		for (ConnectionProperties p : getConnectionSet()) {
			retList.add(p.getDisplayString());
		}
		return retList.toArray(new String[retList.size()]);
	}

	public Set<String> getJdbcDriverFilePathSet() {
		return jdbcDriverFilePathSet;
	}

	public void setJdbcDriverFilePathSet(Set<String> jdbcDriverFilePathSet) {
		this.jdbcDriverFilePathSet = jdbcDriverFilePathSet;
	}

	public void setJdbcDriverFilePathSet(String[] listFiles) {
		this.jdbcDriverFilePathSet.clear();
		if (listFiles == null) {
			return;
		}
		for (String file : listFiles) {
			this.jdbcDriverFilePathSet.add(file);
		}
	}

	@Override
	public String toString() {
		StringBuilder buf = new StringBuilder();
		if (connectionMap == null) {
			buf.append(String.format(
					"JAR_FILE[%s] ConnectionProperties is nothing...",
					jdbcDriverFilePathSet));
		} else {
			buf.append(String.format(
					"JAR_FILE[%s] ConnectionProperties size [%d]%n",
					jdbcDriverFilePathSet, connectionMap.size()));
			for (ConnectionProperties con : connectionMap.values()) {
				buf.append(String.format("  %s%n", con));
			}
		}
		return buf.toString();
	}

	public Set<ConnectionProperties> getCloneConnectionSet() {
		log.debug("conMap=" + connectionMap);
		Set<ConnectionProperties> ret = new LinkedHashSet<ConnectionProperties>();
		for (ConnectionProperties set : connectionMap.values()) {
			ConnectionProperties clone = (ConnectionProperties) set.clone();
			log.debug("add:" + clone + "/ " + clone.getCreated());
			ret.add(clone);
		}
		log.debug("ret size=" + ret.size());
		return ret;
	}

	// --------------------------------------------------------------
	// DeSerialize
	// --------------------------------------------------------------
	private void readObject(ObjectInputStream in) throws IOException,
			ClassNotFoundException {
		log = LogFactory.getLog(getClass());
		in.defaultReadObject();
	}

	public boolean isDefinedDisplayOf(String displayName) {
		return getConnectionPropertiesByDisplay(displayName) != null;
	}

	public boolean isDefinedDisplayOf(String displayName,
			ConnectionProperties fConnectionProperties) {
		ConnectionProperties p = getConnectionPropertiesByDisplay(displayName);
		if (p == null || fConnectionProperties == null) {
			return false;
		}
		return p.getCreated() != fConnectionProperties.getCreated();
	}

}
