/**
 * Copyright © 2013 by Data Virtuality GmbH
 */
package com.datavirtuality.dv.core.interfaces;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import java.util.Set;
import javax.sql.DataSource;




/**
 * Interface for a generic DWH platform
 * 
 * @author Salvatore Raunich
 * @date 2014.09.03
 */
public interface DwhPlatform {
	Object createPlatformInstance(DataSource ds);

	Object getPlatform(String schemaNameInDV);

	Object getPlatform(boolean delimitedIdentifierModeOn, String schemaNameInDV);

	DataSource getDataSource(String name);

	Object getExecutionFactory();

	void setExecutionFactory(Object ef);

	boolean supportsIndexes();

	boolean supportsSortKeys();
	
	boolean supportsDistKeys();

	String getSQLForGatheringNativeStatistics(String fullyQualifiedTableNameInSource);

	/**
	 * Updates the native type mapping
	 */
	void fixNativeTypes(Object platform);

	<T extends Exception> String createTable(String targetTableName, Object tableMetadata, String schemaName, Map<String, String> optionalCreationParams, Class<? extends T> clazz, Object connection) throws T;

	<T extends Exception> String createTable(String targetTableName, Object tableMetadata, String schemaName, Map<String, String> optionalCreationParams, Class<? extends T> clazz, Object connection, boolean generateScriptOnly) throws T;

	<T extends Exception> String createTable(String targetTableName, List<? extends Object> objects, String schemaName,
													Class<? extends T> clazz, Object connection, Object createTableCallback, boolean generateScriptOnly) throws T;

	<T extends Exception> String createTable(String targetTableName, List<? extends Object> objects, String schemaName, Map<String, String> optionalCreationParams,
													Class<? extends T> clazz, Object connection, Object createTableCallback, boolean generateScriptOnly) throws T;
	
	void dropTable(String tableName, String targetSchemaNameInTeiid, String schemaNameOriginal, String catalog, boolean printException, int dropTimeout) throws Exception;

	void dropTable(String tableName, String targetSchemaNameInTeiid, String schemaNameOriginal, String catalog) throws Exception;

	List<String> getAlterTableSql(Object targetTable, Object tableMetadata, final String schemaName) throws Exception;

	List<String> getAlterColumnsSql(Object targetTable, Object tableMetadata, String template) throws Exception;

	/**
	 * Generates a name unique name for a column/index.
	 * 
	 * @param sourceColumnShortName
	 *            the short name of the source column
	 * @param names
	 *            a map of names
	 * @return the quoted generated column name
	 */
	String generateColumnName(Object platform, String sourceColumnShortName, Map<String, String> names);

	/**
	 * takes a non-quoted column name and returns it quoted with rdbms-specific Quotes
	 * 
	 * @param nonQuotedColumnName
	 *            non-quoted column name like "column1"
	 * @param escapeQuotes
	 *            if quotes should be escaped or not
	 * @param schemaName
	 *            the target schema name
	 * @return quoted column name like "`column1`"
	 */
	String toQuotedColumnName(String nonQuotedColumnName, boolean escapeQuotes, String schemaName);

	/**
	 * takes a non-quoted column name and returns it quoted with rdbms-specific Quotes
	 * 
	 * @param nonQuotedColumnName
	 *            non-quoted column name like "column1"
	 * @param schemaName
	 *            the target schema name
	 * @return quoted column name like "`column1`"
	 */
	String toQuotedColumnName(String nonQuotedColumnName, String schemaName);

	/**
	 * takes a quoted identifier and returns it quoted with rdbms-specific Quotes
	 * 
	 * @param quotedColumnName
	 *            quoted identifier
	 * @param schemaName
	 *            the target schema name
	 * @return unquoted identifier
	 */
	String toUnquotedName(String quotedColumnName, String schemaName);

	boolean createIndex(Object db, String indexNameSuffix, List<String> columnNames, String expression,
			Object dwhTable, Object table, String targetSchemaNameOriginal, String targetSchemaNameInDV, boolean exceptionOnBadColumn) throws Exception;

	void dropIndex(Object matTable, Object recIndexObject, String schemaName) throws Exception;

	void dropIndex(Object table, Object recIndexObject, String dataSource, String schema) throws Exception;

	/**
	 * execute a SQL query
	 * 
	 * @param sql
	 *            the SQL query to execute
	 * @throws Exception
	 *             if an error occurs executing the query
	 */
	void executeQuery(String sql) throws Exception ;

	/**
	 * get the catalog name
	 * 
	 * @return the catalog name
	 */
	String getCatalog(Object platform);

	void setRawCatalog(String rawCatalog);

	String getRawCatalog();

	String getSchema(Object platform);

	public Object prepareModelWithTable(String schemaDV, String schemaOriginal, String tableName);

	String determineSchemaOf(String schemaDV, String tableName, List<String> columnNames);

	String caseIndexExtraProcessing(Object expr, String nativeExpr);

	String escapeStringValue(String value);

	void setMaxTableNameLangth(int value);

	int getMaxTableNameLength();

	void setMaxColumnNameLength(int value);
	
	int getMaxColumnNameLength();
	
	void setDefaultSchema(String defaultSchema);

	String getDefaultSchema();

	void setDefaultCatalog(String defaultCatalog);

	String getDefaultCatalog();

    String getDefaultSchemaOrThrow() throws Exception;

	String mapColumn(Object matTable, int sqlType, Object column, Object col);

	boolean requiresDataSource();

	void setName(String name);

	String getName();

	String getShortTableName(String fullyQualifiedName);
	
	Collection<String> getSupportedOptions();
	
	void addTableDefForCreateTableAsSelect(Object selectInto, Object visitor);

	String generateCreationalParams(Object selectInto);

	String setTableNameCaseType(String tableName);

	<T extends Exception> TableNameInSource getTableNameInSource(Object platform, String tableName, Class<? extends T> exc) throws T;

	<T extends Exception> TableNameInSource parseTableNameInSource(Object platform, String tableName, Class<? extends T> exc) throws T;

	TableNameInSource getTableNameInSource(Object platform, String schemaName, String tableName);

	String getTableNameByDwhPlatform(DwhPlatform dwhPlatform, String schemaName, String tableName);

	String normalizeTableName(String tableName);

	void setUseFullSchemaName(boolean useFullSchemaName);

	void setUseCatalogName(boolean useCatalogName);

	void setSchemaPatterns(Set<String> schemaPatterns);

	void setExcludeSchemas(Set<String> excludeSchemas);
	
	boolean isUseFullSchemaName();

	boolean isUseCatalogName();
	
	void setCreateStringsAsClobs(boolean createStringsAsClobs);
	
	boolean isCreateStringsAsClobs();

	void setStringClobsLengthThreshold(int stringClobsLengthThreshold);

	int getStringClobsLengthThreshold();

	/**
	 * Validates the options specified in Select into command
	 * (So far only for Redshift platform) 
	 * 
	 * @throws Exception
	 */
	void validateOptions(Object table, Map<String, String> params) throws Exception;

	boolean validateTypesForInsert(String dataType, String columnType);
	
	Class<?> fixColumnType(Object column, Object otherCol);
	
	String fixRuntimeType(Object column);

	boolean supportsCreateTableAsSelect();

	int setColumnProps(Object col, Object column, boolean alter);

	void setTableColumnsProps(Object table);

	void buildNativeType(Object platformInfo, Object col);

	boolean isSchemaAllowed(Object platform, String schemaName);

	boolean isTableExists(String teiidSchema, String defaultSchema, String tableName);

	String getTruncateSql(String teiidSchema, String fullyQualifiedTableNameInSource);

    void recreateMappings(Object matTable, Object matchDescr, Object table);

	void setDistribute(boolean distribute);

	class TableNameInSource {
		private String defaultSchema;
		private String schemaByPlatform;
		private char delim;
		private String tableName;
		private String quote;
		private String prefix;
		private boolean useFullSchemaName;
		private boolean useCatalogName;
		private final boolean useSchemaInSource;
		private boolean customDefinedCatalog;

		public TableNameInSource(DwhPlatform dwhPlatform, String defaultSchema, String schemaByPlatform, char delim, String tableName, String quote, String prefix, boolean useFullSchemaName, boolean useCatalogName, boolean useSchemaInSource, boolean customDefinedCatalog) {
			this.defaultSchema = defaultSchema;
			this.schemaByPlatform = (schemaByPlatform == null || "".equals(schemaByPlatform)) ? "" : schemaByPlatform;
			this.delim = delim;
			this.tableName = tableName;
			this.quote = quote;
			this.prefix =  (prefix == null || "".equals(prefix)) ? "" : prefix;
			this.useFullSchemaName = useFullSchemaName;
			this.useCatalogName = useCatalogName;
			this.useSchemaInSource = useSchemaInSource;
			this.customDefinedCatalog = customDefinedCatalog;
		}

		public String getPrefix(boolean quote) {
			return quote ? getPrefix() : prefix;
		}

		public String getSchema(boolean quote) {
			return quote ? getSchema() : defaultSchema;
		}

		public String getSchemaByPlatform(boolean quote) {
			return quote ? getSchemaByPlatform() : schemaByPlatform;
		}

		public String getTableName(boolean quote) {
			return quote ? getTableName() : tableName;
		}

		public String getPrefix() {
			return prefix.length() > 0 ? quote + prefix + quote : prefix;
		}

		public String getSchema() {
			return quote + defaultSchema + quote;
		}

		public String getSchemaByPlatform() {
			return schemaByPlatform.length() > 0 ? quote + schemaByPlatform + quote : schemaByPlatform;
		}

		public String getSchema(boolean quote, boolean defaultSchema) {
			String schemaByPlatform = getSchemaByPlatform(quote);
			String schema = getSchema(quote);
			// case when platform supports schemas + schemaByPlatform and defaultSchema are set -
			// then we should choose defaultSchema because it can be set explicitly outside via tableName
			boolean shouldUseDefaultSchema = schemaByPlatform.length() > 0 && !schema.equals(schemaByPlatform);

			return (defaultSchema || shouldUseDefaultSchema ?
					schema : (schemaByPlatform.length() > 0 ? schemaByPlatform : ""));
		}

		public String getTableName() {
			return quote + tableName + quote;
		}

		public String getFullName() {
			return getSchema() + delim + getTableName();
		}

		public String getFullName(boolean quote) {
			return getFullName(quote, false, true);
		}

		public String getFullName(boolean quote, boolean withPrefix, boolean defaultSchema) {
			String prefix = withPrefix ? getPrefix(quote) : "";
			String schema = useSchemaInSource ? getSchema(quote, defaultSchema) : "";
			return (prefix.length() > 0 ? prefix + delim : "") + (schema.length() > 0 ? schema + delim : "") +
					getTableName(quote);
		}

		public String getTableNameByDwhPlatform() {
			return useFullSchemaName ? getFullName(false, useCatalogName, false) : getTableName(false);
		}

		public String getTableNameByDwhPlatformInSource() {
			return getFullName(true, useCatalogName, false);
		}

		public boolean isCustomDefinedCatalog() {
			return customDefinedCatalog;
		}

		@Override
		public String toString() {
			return "TableNameInSource{" +
					"defaultSchema='" + defaultSchema + '\'' +
					", schemaByPlatform='" + schemaByPlatform + '\'' +
					", delim=" + delim +
					", tableName='" + tableName + '\'' +
					", quote='" + quote + '\'' +
					", prefix='" + prefix + '\'' +
					", useFullSchemaName=" + useFullSchemaName +
					", useCatalogName=" + useCatalogName +
					", useSchemaInSource=" + useSchemaInSource +
					'}';
		}


	}
}
