package liquibase.database.typeconversion.core;

import liquibase.database.Database;
import liquibase.database.core.OracleDatabase;
import liquibase.database.structure.type.*;

import java.sql.Types;
import java.text.ParseException;

public class OracleTypeConverter extends AbstractTypeConverter {

	public int getPriority() {
		return PRIORITY_DATABASE;
	}

	public boolean supports(Database database) {
		return database instanceof OracleDatabase;
	}

	/**
	 * Extension of super.getDataType(String columnTypeString, Boolean autoIncrement, String dataTypeName, String precision)<br>
	 * Contains definition of Oracle's data-types
	 * */
	@Override
	protected DataType getDataType(String columnTypeString, Boolean autoIncrement, String dataTypeName, String precision) {
		// Try to define data type by searching of common standard types
		DataType returnTypeName = super.getDataType(columnTypeString, autoIncrement, dataTypeName, precision);
		// If we found CustomType (it means - nothing compatible) then search for oracle types
		if (returnTypeName instanceof CustomType) {
			if (columnTypeString.toUpperCase().startsWith("VARCHAR2")) {
				// Varchar2 type pattern: VARCHAR2(50 BYTE) | VARCHAR2(50 CHAR)
				returnTypeName = getVarcharType();
				if (precision != null) {
					String[] typeParams = precision.split(" ");
					returnTypeName.setFirstParameter(typeParams[0].trim());
					if (typeParams.length > 1) {
						returnTypeName.setUnit(typeParams[1]);
					}
				}
			}
		}
		return returnTypeName;
	}

	@Override
	public Object convertDatabaseValueToObject(Object defaultValue, int dataType, int columnSize, int decimalDigits, Database database) throws ParseException {
		if (defaultValue != null) {
			if (defaultValue instanceof String) {
				if (dataType == Types.DATE || dataType == Types.TIME || dataType == Types.TIMESTAMP) {
					if (((String) defaultValue).indexOf("YYYY-MM-DD HH") > 0) {
						defaultValue = ((String) defaultValue).replaceFirst("^to_date\\('", "").replaceFirst("', 'YYYY-MM-DD HH24:MI:SS'\\)$", "");
					} else if (((String) defaultValue).indexOf("YYYY-MM-DD") > 0) {
						defaultValue = ((String) defaultValue).replaceFirst("^to_date\\('", "").replaceFirst("', 'YYYY-MM-DD'\\)$", "");
					} else {
						defaultValue = ((String) defaultValue).replaceFirst("^to_date\\('", "").replaceFirst("', 'HH24:MI:SS'\\)$", "");
					}
				} else if (
						dataType == Types.BIGINT ||
						dataType == Types.NUMERIC ||
						dataType == Types.BIT ||
						dataType == Types.SMALLINT ||
						dataType == Types.DECIMAL ||
						dataType == Types.INTEGER ||
						dataType == Types.TINYINT ||
						dataType == Types.FLOAT ||
						dataType == Types.REAL
						) {
					/*
					* if dataType is numeric-type then cut "(" , ")" symbols
					* Cause: Column's default value option may be set by both ways:
					* DEFAULT 0
					* DEFAULT (0)
					* */
					defaultValue = ((String) defaultValue).replaceFirst("\\(", "").replaceFirst("\\)", "");
				}
				defaultValue = ((String) defaultValue).replaceFirst("'\\s*$", "'"); //sometimes oracle adds an extra space after the trailing ' (see http://sourceforge.net/tracker/index.php?func=detail&aid=1824663&group_id=187970&atid=923443).
			}
		}
		return super.convertDatabaseValueToObject(defaultValue, dataType, columnSize, decimalDigits, database);
	}

	@Override
    protected Object convertToCorrectObjectType(String value, int dataType, int columnSize, int decimalDigits, Database database) throws ParseException {
		Object returnValue = super.convertToCorrectObjectType(value, dataType, columnSize, decimalDigits, database);
		// I'll do it lately.
		// It needs to design and create Database Function Dictionary first.
		/*if (dataType == Types.BLOB || dataType == Types.TIMESTAMP) {
			if (database.containsDatabaseFunction(value)) {
				returnValue = value;
			}
		}*/
		return returnValue;
	}

	@Override
	public BooleanType getBooleanType() {
		return new BooleanType.NumericBooleanType("NUMBER(1)");
	}

	@Override
	public CurrencyType getCurrencyType() {
		return new CurrencyType("NUMBER(15, 2)");
	}

	@Override
	public UUIDType getUUIDType() {
		return new UUIDType("RAW(16)");
	}

	@Override
	public TimeType getTimeType() {
		return new TimeType("DATE");
	}

	@Override
	public DateTimeType getDateTimeType() {
		return new DateTimeType("TIMESTAMP");
	}

	@Override
	public BigIntType getBigIntType() {
		return new BigIntType("NUMBER(38,0)");
	}

	@Override
	public IntType getIntType() {
		return new IntType("INTEGER");
	}

	@Override
	public VarcharType getVarcharType() {
		return new VarcharType("VARCHAR2");
	}

    @Override
    public NVarcharType getNVarcharType() {
        return new NVarcharType("NVARCHAR2");
    }

    @Override
	public DoubleType getDoubleType() {
		return new DoubleType("FLOAT(24)");
	}

	@Override
	public TinyIntType getTinyIntType() {
		return new TinyIntType("NUMBER(3)");
	}
}

@@ -121,32 +121,77 @@
 racleDatabase;%0D%0A
+import liquibase.database.structure.Column;%0D%0A
 import liquibase
@@ -1593,32 +1593,672 @@
 ypeName;%0D%0A%09%7D%0D%0A%0D%0A
+    @Override%0D%0A    public String convertToDatabaseTypeString(Column referenceColumn, Database database) %7B%0D%0A        String translatedTypeName = referenceColumn.getTypeName();%0D%0A        if (%22NVARCHAR2%22.equals(translatedTypeName)) %7B%0D%0A            translatedTypeName = translatedTypeName+ %22(%22 + referenceColumn.getColumnSize() + %22)%22;%0D%0A        %7D else if (%22BINARY_FLOAT%22.equals(translatedTypeName) %7C%7C %22BINARY_DOUBLE%22.equals(translatedTypeName)) %7B%0D%0A            // nothing to do%0D%0A        %7D else %7B%0D%0A            translatedTypeName = super.convertToDatabaseTypeString(referenceColumn, database);%0D%0A        %7D%0D%0A        return translatedTypeName;%0D%0A    %7D%0D%0A%0D%0A
 %09@Override%0D%0A%09pub

change from http://liquibase.org/forum/index.php?topic=715.msg2607#msg2607