package hiro.yoshioka.util;

import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

public enum SQLDataType {
	/** TINYINT */
	TINYINT("TINYINT", Types.TINYINT),
	/** SMALLINT */
	SMALLINT("SMALLINT", Types.SMALLINT),
	/** INTEGER */
	INTEGER("INTEGER", Types.INTEGER),
	/** BIGINT */
	BIGINT("BIGINT", Types.BIGINT),
	/** FLOAT */
	FLOAT("FLOAT", Types.FLOAT),
	/** REAL */
	REAL("REAL", Types.REAL),
	/** DOUBLE */
	DOUBLE("DOUBLE", Types.DOUBLE),
	/** NUMERIC */
	NUMERIC("NUMERIC", Types.NUMERIC),
	/** DECIMAL */
	DECIMAL("DECIMAL", Types.DECIMAL),
	// ----------------------------------------------------------------
	// CHARACTORS
	/** CHAR */
	CHAR("CHAR", Types.CHAR),
	/** VARCHAR */
	VARCHAR("VARCHAR", Types.VARCHAR),
	/** LONGVARCHAR */
	LONGVARCHAR("LONGVARCHAR", Types.LONGVARCHAR),
	/** NCHAR */
	NCHAR("NCHAR", Types.NCHAR),
	/** NVARCHAR */
	NVARCHAR("NVARCHAR", Types.NVARCHAR),
	/** LONGNVARCHAR */
	LONGNVARCHAR("LONGNVARCHAR", Types.LONGNVARCHAR),
	/** CLOB */
	CLOB("CLOB", Types.CLOB),
	/** NCLOB */
	NCLOB("NCLOB", Types.NCLOB),
	// ----------------------------------------------------------------
	// DATE
	/** DATE */
	DATE("DATE", Types.DATE),
	/** TIME */
	TIME("TIME", Types.TIME),
	/** TIMESTAMP */
	TIMESTAMP("TIMESTAMP", Types.TIMESTAMP),
	// ----------------------------------------------------------------
	// BINARY
	/** BINARY */
	BINARY("BINARY", Types.BINARY),
	/** VARBINARY */
	VARBINARY("VARBINARY", Types.VARBINARY),
	/** LONGVARBINARY */
	LONGVARBINARY("LONGVARBINARY", Types.LONGVARBINARY),
	/** BLOB */
	BLOB("BLOB", Types.BLOB),
	// ----------------------------------------------------------------
	// BIT / BOOLEAN
	/** BIT */
	BIT("BIT", Types.BIT),
	/** BLOOLEAN */
	BOOLEAN("BOOLEAN", Types.BOOLEAN),
	// ----------------------------------------------------------------
	// OTHER
	/** OTHER */
	OTHER("OTHER", Types.OTHER),
	/** JAVA_OBJECT */
	JAVA_OBJECT("JAVA_OBJECT", Types.JAVA_OBJECT),
	/** DISTINCT */
	DISTINCT("DISTINCT", Types.DISTINCT),
	/** STRUCT */
	STRUCT("STRUCT", Types.STRUCT),
	/** ARRAY */
	ARRAY("ARRAY", Types.ARRAY),
	/** REF */
	REF("REF", Types.REF),
	/** DATALINK */
	DATALINK("DATALINK", Types.DATALINK),
	/** ROWID */
	ROWID("ROWID", Types.ROWID),
	/** SQLXML */
	SQLXML("SQLXML", Types.SQLXML),
	/** NULL */
	NULL("NULL", Types.NULL),
	/** BINARY_FLOAT */
	BINARY_FLOAT("BINARY_FLOAT", 100),
	/** BINARY_DOUBLE */
	BINARY_DOUBLE("BINARY_DOUBLE", 101),
	/** UNKNOWN */
	UNKNOWN("UNKNOWN", Integer.MAX_VALUE);

	public static final List<SQLDataType> DateOrTimeOrTimeStampTypeList = new ArrayList<SQLDataType>();
	public static final List<SQLDataType> DateOrTimeStampTypeList = new ArrayList<SQLDataType>();
	public static final List<SQLDataType> BinaryTypeList = new ArrayList<SQLDataType>();
	public static final List<SQLDataType> NumericsTypeList = new ArrayList<SQLDataType>();
	public static final List<SQLDataType> StringsTypeList = new ArrayList<SQLDataType>();
	static {
		DateOrTimeOrTimeStampTypeList.add(DATE);
		DateOrTimeOrTimeStampTypeList.add(TIME);
		DateOrTimeOrTimeStampTypeList.add(TIMESTAMP);

		DateOrTimeStampTypeList.add(DATE);
		DateOrTimeStampTypeList.add(TIMESTAMP);

		BinaryTypeList.add(BLOB);
		BinaryTypeList.add(BINARY);
		BinaryTypeList.add(VARBINARY);
		BinaryTypeList.add(LONGVARBINARY);

		NumericsTypeList.add(TINYINT);
		NumericsTypeList.add(SMALLINT);
		NumericsTypeList.add(INTEGER);
		NumericsTypeList.add(BIGINT);
		NumericsTypeList.add(FLOAT);
		NumericsTypeList.add(REAL);
		NumericsTypeList.add(DOUBLE);
		NumericsTypeList.add(NUMERIC);
		NumericsTypeList.add(DECIMAL);

		StringsTypeList.add(CHAR);
		StringsTypeList.add(VARCHAR);
		StringsTypeList.add(LONGVARCHAR);
		StringsTypeList.add(NCHAR);
		StringsTypeList.add(NVARCHAR);
		StringsTypeList.add(LONGNVARCHAR);
		StringsTypeList.add(CLOB);
		StringsTypeList.add(NCLOB);
	}

	private String message;
	private int type;

	private SQLDataType(String message, int type) {
		this.message = message;
		this.type = type;
	}

	public static SQLDataType parse(int type) {
		for (SQLDataType dataType : values()) {
			if (dataType.type == type) {
				return dataType;
			}
		}
		System.err.println("SQLDataTypesNum#what is this type[" + type + "]");
		return UNKNOWN;
	}

	public static SQLDataType parse(String message) {
		for (SQLDataType dataType : values()) {
			if (dataType.message.equalsIgnoreCase(message)) {
				return dataType;
			}
		}
		message = message.toUpperCase();
		if ("VARCHAR2".equals(message)) {
			return SQLDataType.VARCHAR;
		}
		if (message.startsWith("NUMBER")) {
			return SQLDataType.NUMERIC;
		}
		if (message.equals("TEXT")) {
			return SQLDataType.LONGVARCHAR;
		}
		if (message.equals("TIMESTAMP WITHOUT TIME ZONE")) {
			return SQLDataType.TIMESTAMP;
		}
		for (SQLDataType type : values()) {
			if (message.startsWith(type.name() + "(")) {
				return type;
			}
		}

		System.err.println("what is this type[" + message + "]");
		return UNKNOWN;
	}

	public String getMessage() {
		return message;
	}

	public int getType() {
		return type;
	}

	public boolean isBlob() {
		return BLOB.equals(this);
	}

	public boolean isBinary() {
		return BINARY.equals(this) || VARBINARY.equals(this)
				|| LONGVARBINARY.equals(this);
	}

	public boolean isDateOrDateTimeOrTime() {
		switch (this) {
		case DATE:
		case TIME:
		case TIMESTAMP:
			return true;
		}
		return false;
	}

	public boolean isBit() {
		return BIT.equals(this);
	}

	public boolean isBoolean() {
		return BOOLEAN.equals(this);
	}

	public boolean isBitOrBoolean() {
		return BOOLEAN.equals(this) || BIT.equals(this);
	}

	public boolean isDate() {
		return DATE.equals(this);
	}

	public boolean isTime() {
		return TIME.equals(this);
	}

	public boolean isTimeStamp() {
		return TIMESTAMP.equals(this);
	}

	public boolean isNumerics() {
		return NumericsTypeList.contains(this);
	}

	public boolean isBigDecimal() {
		return BIGINT.equals(this);
	}

	public boolean isString() {
		switch (this) {
		case CHAR:
		case VARCHAR:
		case LONGVARCHAR:
		case NCHAR:
		case NVARCHAR:
		case LONGNVARCHAR:
		case CLOB:
		case NCLOB:
			return true;
		}
		return false;
	}

	/**
	 * A string with the data type of the values of the column. The type can be
	 * one of the following: 'string' 'number' 'boolean' 'date' 'datetime'
	 * 'timeofday'.
	 **/
	public GoogleDataType getGoogleDataType() {
		if (isTime()) {
			return GoogleDataType.TIME_OF_DAY;
		}
		if (isTimeStamp()) {
			return GoogleDataType.DATE_TIME;
		}
		if (isDate()) {
			return GoogleDataType.DATE;
		}
		if (isBoolean() || isBit()) {
			return GoogleDataType.BOOLEAN;
		}
		if (isNumerics()) {
			return GoogleDataType.NUMBER;
		}
		return GoogleDataType.STRING;
	}
}
