package hiro.yoshioka.sql.engine;

import hiro.yoshioka.sdh.DatabaseType;
import hiro.yoshioka.sql.DataBaseFactory;
import hiro.yoshioka.sql.ITransactionSQL;
import hiro.yoshioka.sql.params.ConnectionProperties;
import hiro.yoshioka.sql.resource.DBSchema;
import hiro.yoshioka.sql.resource.DBTable;
import hiro.yoshioka.sql.resource.IDBColumn;
import hiro.yoshioka.sql.resource.IDBSchema;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.util.StringUtil;

import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

public class TwoDatabasesRequest extends Request {
	static final String USING_CONNECTED_SCHEMA = "~~USING_CONNECTED_SCHEMA~~";

	private boolean canceled;
	public boolean continuing;
	public int maxRowNum = Integer.MAX_VALUE;
	final ConnectionProperties targetConnectionProperties;
	private Map<String, String> schemaMapping = new LinkedHashMap<String, String>();
	private Map<String, Set<MirroringTableInfo>> tableMapping = new LinkedHashMap<String, Set<MirroringTableInfo>>();
	private ITransactionSQL mirroring_from_sql, mirroring_to_sql;
	private Map<String, String> columnNameSubstitutionMap;

	public TwoDatabasesRequest(SQLOperationType operationType,
			ConnectionProperties originalConnectionProperties,
			ConnectionProperties targetConnectionProperties) {
		super(operationType, originalConnectionProperties);
		this.targetConnectionProperties = targetConnectionProperties;
	}

	public String toString() {
		StringBuffer buf = new StringBuffer();
		buf.append("PATTERN [S/T/P] [");
		// buf.append(getSchemaP()).append("/");
		// buf.append(getTableP()).append("/");
		// buf.append(getProcedureP());
		buf.append("]");
		return buf.toString();
	}

	@Override
	protected void endOperation(boolean operationResult) {
		super.endOperation(operationResult);
		if (operationResult) {
			properties.setDBRoot(SQLServerThread.getSQLServer()
					.getTransactionSQL(properties).getRoot());
		}
	}

	@Override
	protected Object getDoneArgObject() {
		return SQLServerThread.getSQLServer().getTransactionSQL(properties)
				.getRoot();
	}

	public String getSubsutituteColumnName(String sourceColumnName) {
		if (this.columnNameSubstitutionMap == null) {
			return null;
		}
		if (this.columnNameSubstitutionMap.containsKey(sourceColumnName
				.toUpperCase())) {
			return this.columnNameSubstitutionMap.get(sourceColumnName
					.toUpperCase());
		}
		return null;
	}

	public void putColumnNameSubstitution(IDBColumn column,
			String substitutedName) {
		this.putColumnNameSubstitution(column.getUName(), substitutedName);
	}

	public void putColumnNameSubstitution(String sourceColumnName,
			String substitutedName) {
		if (this.columnNameSubstitutionMap == null
				|| this.columnNameSubstitutionMap.size() == 0) {
			this.columnNameSubstitutionMap = new HashMap<String, String>();
		}
		this.columnNameSubstitutionMap.put(sourceColumnName.toUpperCase(),
				substitutedName);
	}

	public void putSchemaMapping(String sourceName, String targetName) {
		if (StringUtil.isEmpty(targetName)) {
			throw new IllegalArgumentException(
					"mapping to schemaName must be not null.");
		}
		this.schemaMapping.put(sourceName, targetName);
	}

	public Map<String, String> getcolumnNameSubstitutionMap() {
		return columnNameSubstitutionMap;
	}

	public void mapToDefaultSchema(String sourceName) {
		this.putSchemaMapping(sourceName, USING_CONNECTED_SCHEMA);
	}

	public void addResultRecord(String[] rowInfo) {
		fRdh.addRow(rowInfo);
	}

	private Map<String, String> getSchemaMapping() {
		return schemaMapping;
	}

	public String getMappingToSchemaName(String mappingFrom)
			throws SQLException {
		String str = schemaMapping.get(mappingFrom);
		if (USING_CONNECTED_SCHEMA.equals(str)) {
			return getMirroringToSql().getDefaultSchemaName();
		}
		return str;
	}

	public Collection<String> getMappingFromSchemaNames() {
		Collection<String> ret = schemaMapping.keySet();
		if (ret.contains(USING_CONNECTED_SCHEMA)) {
			Set<String> ret2 = new LinkedHashSet<String>();
			ret2.addAll(ret);
			ret2.remove(USING_CONNECTED_SCHEMA);
			return ret2;
		}
		return ret;
	}

	public Collection<String> getMappingToSchemaNames() {
		Collection<String> ret = schemaMapping.values();
		if (ret.contains(USING_CONNECTED_SCHEMA)) {
			Set<String> ret2 = new LinkedHashSet<String>();
			ret2.addAll(ret);
			ret2.remove(USING_CONNECTED_SCHEMA);
			return ret2;
		}
		return ret;
	}

	public ConnectionProperties getTargetConnectionProperties() {
		return targetConnectionProperties;
	}

	public synchronized void cancel() {
		canceled = true;
	}

	public synchronized boolean isCanceled() {
		return canceled;
	}

	public ITransactionSQL getMirroringToSql() throws SQLException {
		if (this.mirroring_to_sql == null) {
			this.mirroring_to_sql = SQLServerThread.getSQLServer()
					.getTransactionSQL(targetConnectionProperties);
			if (this.mirroring_to_sql == null) {
				this.mirroring_to_sql = DataBaseFactory
						.createSQL(targetConnectionProperties);
			}
			if (this.mirroring_to_sql != null) {
				this.mirroring_to_sql.connect(targetConnectionProperties);
			}
		}
		return this.mirroring_to_sql;
	}

	public void setMirroringFromSql(ITransactionSQL sql) {
		this.mirroring_from_sql = sql;
	}

	public ITransactionSQL getMirroringFromSql() {
		return this.mirroring_from_sql;
	}

	public Map<String, Set<MirroringTableInfo>> getTableMapping() {
		return tableMapping;
	}

	public void putMirroringTable(String schemaName, String tableName,
			boolean isTable) {
		Set<MirroringTableInfo> set = tableMapping.get(schemaName);
		if (set == null) {
			set = new LinkedHashSet<MirroringTableInfo>();
			tableMapping.put(schemaName, set);
		}

		set.add(new MirroringTableInfo(tableName, isTable));
	}

	public class MirroringTableInfo {
		public String name;
		public boolean isTable;
		public IDBTable tableResource;

		public MirroringTableInfo(String tableName, boolean isTable) {
			this.name = tableName;
			this.isTable = isTable;
		}

		public IDBTable getMappingFromDBTable(String schemaName)
				throws SQLException {
			if (tableResource == null) {
				IDBSchema schema = new DBSchema(null);
				schema.setName(schemaName);
				tableResource = new DBTable(schema);
				((DBTable) tableResource).setTableType(isTable ? "TABLE"
						: "VIEW");
				tableResource.setName(this.name);
				mirroring_from_sql.setTableColumns(schemaName, tableResource);
				schema.putTable(tableResource);
			}
			return tableResource;
		}

	}

	public boolean hasUsingConnectedSchemaMapping() {
		Collection<String> ret = schemaMapping.values();
		if (ret.contains(USING_CONNECTED_SCHEMA)) {
			return true;
		}
		return false;
	}

	public DatabaseType getMirroringFromDatabaseType() {
		return properties.getDatabaseType();
	}

	public DatabaseType getMirroringToDatabaseType() {
		return targetConnectionProperties.getDatabaseType();
	}

}