/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.core.orm.sql.engine;

import nuts.core.orm.type.JdbcTypeRegistry;
import nuts.core.orm.type.TypeHandler;
import nuts.core.orm.type.TypeHandlerFactory;

/**
 * Parameter
 */
public class SqlParameter {
	/**
	 * MODE_IN = "IN";
	 */
	public final static String MODE_IN = "IN";
	
	/**
	 * MODE_OUT = "OUT";
	 */
	public final static String MODE_OUT = "OUT";
	
	/**
	 * MODE_INOUT = "INOUT";
	 */
	public final static String MODE_INOUT = "INOUT";
	
	private String name;
	private String jdbcType;
	private Integer sqlType;
	private Integer scale;
	private Object value;
	private String mode;
	private TypeHandler typeHandler;

	/**
	 * Constructor.
	 * @param name name
	 * @param value value
	 */
	public SqlParameter(String name, Object value) {
		this(name, value, null, null, null);
	}

	/**
	 * Constructor.
	 * @param name name
	 * @param value value
	 * @param jdbcType jdbc type
	 */
	public SqlParameter(String name, Object value, String jdbcType) {
		this(name, value, jdbcType, null, null);
	}

	/**
	 * Constructor.
	 * @param name name
	 * @param value value
	 * @param jdbcType jdbc type
	 * @param mode mode
	 */
	public SqlParameter(String name, Object value, String jdbcType, String mode) {
		this(name, value, jdbcType, null, mode);
	}

	/**
	 * Constructor.
	 * @param name name
	 * @param value value
	 * @param jdbcType jdbc type
	 * @param scale scale
	 * @param mode mode
	 */
	public SqlParameter(String name, Object value, String jdbcType, Integer scale, String mode) {
		this.name = name;
		this.value = value;

		this.jdbcType = jdbcType;
		if (jdbcType != null) {
			this.sqlType = JdbcTypeRegistry.getType(jdbcType);
//			if (this.sqlType == null) {
//				throw new IllegalArgumentException("Illegal parameter '" + name + "': unknown JDBC type [" + jdbcType + "].");
//			}
		}
		this.scale = scale;
		
		if (mode != null) {
			mode = mode.toUpperCase();
			if (MODE_IN.equals(mode) || MODE_OUT.equals(mode) || MODE_INOUT.equals(mode)) {
				this.mode = mode;
			}
			else {
				throw new IllegalArgumentException("Illegal parameter '" + name + "': unknown mode [" + mode + "], it must be IN/OUT/INOUT.");
			}
		}
		else {
			this.mode = MODE_IN;
		}
		
		Class parameterValueClass = value == null ? null : value.getClass(); 
		this.typeHandler = TypeHandlerFactory.getInstance().getTypeHandler(
				parameterValueClass, jdbcType);
		if (this.typeHandler == null) {
			throw new IllegalArgumentException("Illegal parameter '" + name + "': unknown TypeHandler [" + parameterValueClass + ", " + jdbcType + "].");
		}
	}

	/**
	 * isInputAllowed
	 * @return true if the parameter allowed to input
	 */
	public boolean isInputAllowed() {
		return !MODE_OUT.equals(mode);
	}
	
	/**
	 * isOutputAllowed
	 * @return true if the parameter allowed to output
	 */
	public boolean isOutputAllowed() {
		return (MODE_OUT.equals(mode) || MODE_INOUT.equals(mode));
	}
	
	/**
	 * @return the TypeHandler of the parameter
	 */
	public TypeHandler getTypeHandler() {
		return typeHandler;
	}

	/**
	 * @return sqlType
	 */
	public Integer getSqlType() {
		return sqlType;
	}

	/**
	 * @return jdbcType
	 */
	public String getJdbcType() {
		return jdbcType;
	}

	/**
	 * @return name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return value
	 */
	public Object getValue() {
		return value;
	}

	/**
	 * @return mode
	 */
	public String getMode() {
		return mode;
	}

	/**
	 * @return scale
	 */
	public Integer getScale() {
		return scale;
	}

	/**
     * @return  a string representation of the object.
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();

		sb.append("{ ");
		sb.append("name: ").append(name);
		sb.append(", ");
		sb.append("value: ").append(value);
		sb.append(", ");
		sb.append("jdbcType: ").append(jdbcType);
		sb.append(", ");
		sb.append("scale: ").append(scale);
		sb.append(", ");
		sb.append("mode: ").append(mode);
		sb.append(" }");
		
		return sb.toString();
	}

	/**
     * @return  a hash code value for this object.
	 */
	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + ((name == null) ? 0 : name.hashCode());
		result = PRIME * result + ((jdbcType == null) ? 0 : jdbcType.hashCode());
		result = PRIME * result + ((sqlType == null) ? 0 : sqlType.hashCode());
		result = PRIME * result + ((scale == null) ? 0 : scale.hashCode());
		result = PRIME * result + ((mode == null) ? 0 : mode.hashCode());
		result = PRIME * result + ((value == null) ? 0 : value.hashCode());
		result = PRIME * result + ((typeHandler == null) ? 0 : typeHandler.hashCode());
		return result;
	}

	/**
     * @return  <code>true</code> if this object is the same as the obj argument; 
     * 			<code>false</code> otherwise.
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		
		final SqlParameter other = (SqlParameter) obj;
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		}
		else if (!name.equals(other.name)) {
			return false;
		}
		if (jdbcType == null) {
			if (other.jdbcType != null) {
				return false;
			}
		}
		else if (!jdbcType.equals(other.jdbcType)) {
			return false;
		}
		if (sqlType == null) {
			if (other.sqlType != null) {
				return false;
			}
		}
		else if (!sqlType.equals(other.sqlType)) {
			return false;
		}
		if (scale == null) {
			if (other.scale != null) {
				return false;
			}
		}
		else if (!scale.equals(other.scale)) {
			return false;
		}
		if (mode == null) {
			if (other.mode != null) {
				return false;
			}
		}
		else if (!mode.equals(other.mode)) {
			return false;
		}
		if (value == null) {
			if (other.value != null) {
				return false;
			}
		}
		else if (!value.equals(other.value)) {
			return false;
		}
		if (typeHandler == null) {
			if (other.typeHandler != null) {
				return false;
			}
		}
		else if (!typeHandler.equals(other.typeHandler)) {
			return false;
		}
		return true;
	}


}
