package hiro.yoshioka.sql.resource;

import hiro.yoshioka.util.StringUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DBRoot extends DBResource {
	private static final long serialVersionUID = 99992512026049888L;

	static final Pattern VERSION_PATTERN = Pattern.compile("\\d+[.]\\d+");
	private static final double DEFAULT_DATABASE_VERSION = 0.0;

	private static final IDBColumn[] DEFAULT_COLUMNS = new IDBColumn[0];

	private DBIndexRoot fIndexRoot;

	private DBConstraintRoot fDBConstraintRoot;

	private DBDictionaryRoot fDBDictionaryRoot;

	public final String fResourceCaptionUser;

	private IDBSchema fDefaultSchema;

	private IDBSchema fCurrentSchema;

	private RecentryUsedResource fRecentryUsedResource = new RecentryUsedResource(
			this);

	public DBRoot() {
		this((String) null);
	}

	public DBRoot(String user) {
		super(null);
		if (user == null) {
			fResourceCaptionUser = StringUtil.EMPTY_STRING;
		} else {
			fResourceCaptionUser = user.toUpperCase();
		}
		setName(user);
		fIndexRoot = new DBIndexRoot(this);
		fIndexRoot.setName("INDEXES");
		putResource(fIndexRoot.getName(), fIndexRoot);
		fDBConstraintRoot = new DBConstraintRoot(this);
		fDBConstraintRoot.setName("CONSTRAINT");
		putResource(fDBConstraintRoot.getName(), fDBConstraintRoot);
		fDBDictionaryRoot = new DBDictionaryRoot(this);
		fDBDictionaryRoot.setName("Dictionary");
		putResource(fDBDictionaryRoot.getName(), fDBDictionaryRoot);
	}

	public double getDatabaseVersion() {
		if (fProperties == null) {
			return DEFAULT_DATABASE_VERSION;
		}
		String ver = fProperties.getProperty("DatabaseProductVersion");
		try {
			if (!StringUtil.isEmpty(ver)) {
				Matcher m = VERSION_PATTERN.matcher(ver);
				if (m.find()) {
					return Double.parseDouble(m.group());
				}
			}
		} catch (NumberFormatException e) {
		}
		return DEFAULT_DATABASE_VERSION;
	}

	// public void clearAlias() {
	// if (aliasList != null) {
	// aliasList.clear();
	// }
	// }
	//
	// public String[] getAlias(IDBTable table) {
	// if (aliasList == null) {
	// return StringUtil.EMPTY_STRING_ARRAY;
	// }
	// Set<String> list = aliasList.get(table);
	// if (list == null) {
	// return new String[0];
	// }
	// return list.toArray(new String[list.size()]);
	// }
	//
	// public void addAlias(IDBTable table, String alias) {
	// if (aliasList == null) {
	// aliasList = new HashMap<IDBTable, Set<String>>();
	// }
	// Set<String> list = aliasList.get(table);
	// if (list == null) {
	// list = new HashSet<String>();
	// aliasList.put(table, list);
	// }
	// list.add(alias);
	// }

	public DBRoot getRoot() {
		return this;
	}

	public boolean supportJoin() {
		String left = fProperties.getProperty("OuterJoins");
		if ("true".equalsIgnoreCase(left)) {
			return true;
		}
		return false;
	}

	public DBRoot(DBRoot res) {
		super(res);
		fResourceCaptionUser = "";
	}

	public boolean setDefaultSchema(IDBSchema schema) {
		fDefaultSchema = schema;
		return _children.containsValue(schema);
	}

	public IDBSchema getDefaultSchema() {
		if (fDefaultSchema != null) {
			return fDefaultSchema;
		}
		// fLogger.info("fResourceCaptionUser[" + fResourceCaptionUser + "]");
		return (IDBSchema) getResource(fResourceCaptionUser);
	}

	public DBIndexRoot getDBIndexRoot() {
		return fIndexRoot;
	}

	public DBDictionaryRoot getDBDictionaryRoot() {
		return fDBDictionaryRoot;
	}

	public DBConstraintRoot getDBConstraintRoot() {
		return fDBConstraintRoot;
	}

	public IDBTable getProcedureOrFunction(String schema, String table) {
		IDBSchema ischema = (IDBSchema) getResource(schema);
		if (ischema == null) {
			ischema = getCurrentSchema();
		}
		if (ischema == null) {
			if (fLogger.isWarnEnabled()) {
				fLogger.warn("ischema[" + ischema + "]schema[" + schema
						+ "] TABLE[" + table + "]");
			}
			return null;
		}
		return ischema.getProcedure(table);
	}

	public IDBTable getTable(String schema, String table) {
		IDBSchema ischema = (IDBSchema) getResource(schema);
		if (ischema == null) {
			ischema = getCurrentSchema();
		}
		if (ischema == null) {
			if (fLogger.isWarnEnabled()) {
				fLogger.warn("ischema[" + ischema + "]schema[" + schema
						+ "] TABLE[" + table + "]");
			}
			return null;
		}
		return ischema.getTableResource(table);
	}

	public IDBColumn getColumn(String schema, String table, String column) {
		return getColumn(schema, table, column, false);
	}

	public IDBColumn getColumn(String schema, String table, String column,
			boolean considerAlias) {
		IDBSchema ischema = (IDBSchema) getResource(schema, considerAlias);
		if (ischema == null) {
			ischema = getCurrentSchema();
		}
		if (ischema == null) {
			if (fLogger.isWarnEnabled()) {
				fLogger.warn("ischema[" + ischema + "]schema[" + schema
						+ "] TABLE[" + table + "]");
			}
			return null;
		}
		IDBTable t = ischema.getTableResource(table, considerAlias);
		if (t == null) {
			if (fLogger.isWarnEnabled()) {
				fLogger.warn("ischema[" + ischema + "]schema[" + schema
						+ "] TABLE[" + table + "]");
			}
			return null;
		}
		return (IDBColumn) t.getResource(column, considerAlias);
	}

	public List listResources() {
		ArrayList list = new ArrayList();
		for (Iterator ite = _children.entrySet().iterator(); ite.hasNext();) {
			DBResource resource = (DBResource) ((Map.Entry) ite.next())
					.getValue();
			if ((resource instanceof DBIndexRoot)
					|| (resource instanceof DBConstraintRoot)
					|| (resource instanceof DBDictionaryRoot)) {
				if (!resource.hasChildren()) {
					continue;
				}
			}

			list.add(resource);
		}

		return list;
	}

	// public IDBColumn getColumn(ASTEditorTableColumn column) {
	// IDBTable dbtable = ((IDBSchema) getResource(column.getUpperSchema()))
	// .getTable(column.getUpperTable());
	//
	// return (IDBColumn) dbtable.getResource(column.getUpperColumn());
	// }
	// public IDBColumn[] getColumns(ASTEditorTable table) {
	// IDBSchema schema = (IDBSchema) getResource(table.getUpperSchema());
	// IDBTable dbtable = null;
	// if (schema == null) {
	// IDBResource[] lists = listArrayResources();
	// for (int i = 0; i < lists.length; i++) {
	// dbtable = ((IDBSchema) lists[i])
	// .getTable(table.getUpperTable());
	// if (dbtable != null) {
	// break;
	// }
	// }
	// } else {
	// dbtable = schema.getTable(table.getUpperTable());
	// }
	// if (dbtable == null) {
	// return DEFAULT_COLUMNS;
	// }
	// return dbtable.getColumns();
	// }

	public boolean contain(String pattern) {
		return true;
	}

	public IDBSchema getSchema(String name) {
		for (Iterator<Map.Entry<String, IDBResource>> ite = _children
				.entrySet().iterator(); ite.hasNext();) {
			IDBResource resource = ite.next().getValue();
			if (resource instanceof IDBSchema
					&& (name.equals(resource.getName()))) {
				return (IDBSchema) resource;
			}
		}
		return null;
	}

	/**
	 * @return
	 */
	public IDBSchema[] getSchemas() {
		List<IDBSchema> list = new ArrayList<IDBSchema>();
		for (Iterator<Map.Entry<String, IDBResource>> ite = _children
				.entrySet().iterator(); ite.hasNext();) {
			IDBResource resource = ite.next().getValue();
			if (resource instanceof IDBSchema) {
				list.add((IDBSchema) resource);
			}

		}

		return list.toArray(new IDBSchema[list.size()]);
	}

	public IDBSchema[] getSchemas(Collection<String> schemaNames) {
		List<IDBSchema> list = new ArrayList<IDBSchema>();
		for (Iterator<Map.Entry<String, IDBResource>> ite = _children
				.entrySet().iterator(); ite.hasNext();) {
			IDBResource resource = ite.next().getValue();
			if (resource instanceof IDBSchema) {
				if (schemaNames.contains(resource.getName())) {
					list.add((IDBSchema) resource);
				}
			}

		}

		return list.toArray(new IDBSchema[list.size()]);
	}

	/**
	 * @param tableNameString
	 */
	public List<IDBResource> getSchemaListStartsWith(String schemaNameString) {
		List<IDBResource> list = new ArrayList<IDBResource>();
		for (Iterator ite = _children.entrySet().iterator(); ite.hasNext();) {
			DBResource resource = (DBResource) ((Map.Entry) ite.next())
					.getValue();
			if (resource instanceof IDBSchema
					&& resource.startsNameWith(schemaNameString)) {
				list.add(resource);
			}

		}
		return list;
	}

	public boolean isOracle() {
		Properties prop = getProperties();
		if (prop != null) {
			return "Oracle".equalsIgnoreCase(prop
					.getProperty("DatabaseProductName"));
		}
		return false;
	}

	public boolean isDomino() {
		Properties prop = getProperties();
		if (prop != null) {
			return "Lotus".equalsIgnoreCase(prop
					.getProperty("DatabaseProductName"));
		}
		return false;
	}

	public boolean isMongo() {
		Properties prop = getProperties();
		if (prop != null) {
			return "Mongo".equalsIgnoreCase(prop
					.getProperty("DatabaseProductName"));
		}
		return false;
	}

	public RecentryUsedResource getRecentryUsedResource() {
		return fRecentryUsedResource;
	}

	/**
	 * @param schema
	 */
	public IDBSchema getCurrentSchema() {
		if (fCurrentSchema == null) {
			// if (fLogger.isWarnEnabled()) {
			// fLogger.warn("nothing current schema call default schema...");
			// }
			return getDefaultSchema();
		}
		return fCurrentSchema;
	}

	/**
	 * @param schema
	 */
	public void setCurrentSchema(IDBSchema schema) {
		fCurrentSchema = schema;
	}

	public void slimUp() {
		for (IDBResource resource : _children.values()) {
			resource.slimUp();
		}
	}

	public IDBResource[] listArrayResourcesWithMisc() {
		List<IDBResource> list = listResources();

		list.add(0, fRecentryUsedResource);

		return (IDBResource[]) list.toArray(new IDBResource[list.size()]);
	}

	public void addRecentryUsedTable(IDBTable table) {
		fRecentryUsedResource.add(table);
	}

	public void clearRecentryUsedTable() {
		fRecentryUsedResource.removeAll();
	}

	public boolean isRecentryUsed(IDBResource resource) {
		return fRecentryUsedResource.contains(resource);
	}

	public void dump() {
		int i = 0;
		for (IDBSchema schema : getSchemas()) {
			i++;
			int j = 0;
			System.out.println(String.format("  [%2d] SCHEMA[%s]", i,
					schema.getNameWithAsComment()));
			for (IDBTable table : schema.getTableOrSystemTables()) {
				j++;
				System.out.println(String.format("    [%2d] TABLE[%s]", j,
						table.getNameWithAsComment()));
			}
		}
	}

}