/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
#include "apache/das/rdb/ResultSetMetaData.h"

namespace apache {
	namespace das {
		namespace rdb {

ResultSetMetaData::ResultSetMetaData(const ResultSet& aResultSet) {
	resultSet = &aResultSet;
	unsigned int columnCount = getColumnCount();
	
	for (unsigned int i = 0 ; i < columnCount ; i++) {
		std::string columnName = StringWrapper(getColumnName(i)).toUpper();
		std::string tableName = StringWrapper(getTableName(i)).toUpper();
		columnsIndexes.insert(std::make_pair(tableName + "." + columnName, i));

	}

}

ResultSetMetaData::~ResultSetMetaData(void) {}

SQLSMALLINT ResultSetMetaData::getSQLCType(SQLSMALLINT sqlType) {

	switch (sqlType) {

		case SQL_CHAR :
			return SQL_C_CHAR;

		case SQL_BINARY :
			return SQL_C_BINARY;

		case SQL_TYPE_DATE :
			return SQL_C_TYPE_DATE;

		case SQL_DECIMAL :
			return SQL_C_CHAR;

		case SQL_DOUBLE :
			return SQL_C_DOUBLE;

		case SQL_FLOAT :
			return SQL_C_DOUBLE;

		case SQL_INTEGER :
			return SQL_C_LONG;

		case SQL_LONGVARCHAR :
			return SQL_C_CHAR;

		case SQL_LONGVARBINARY :
			return SQL_C_BINARY;

		case SQL_NUMERIC :
			return SQL_C_CHAR;

		case SQL_REAL :
			return SQL_C_FLOAT;

		case SQL_SMALLINT :
			return SQL_C_SHORT;

		case SQL_TYPE_TIME :
			return SQL_C_TYPE_TIME;

		case SQL_TYPE_TIMESTAMP :
			return SQL_C_TYPE_TIMESTAMP;

		case SQL_VARCHAR :
			return SQL_C_CHAR;

		case SQL_VARBINARY :
			return SQL_C_BINARY;

		default :
			throw DASInvalidSQLTypeException();
		
	}

}

SQLSMALLINT ResultSetMetaData::getSQLType(unsigned int columnIndex) const {
	SQLSMALLINT sqlType = 0;
	SQLColAttribute(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_TYPE, NULL, NULL, NULL, &sqlType);
		
	return sqlType;

}

SQLSMALLINT ResultSetMetaData::getSQLType(std::string tableName, std::string columnName) const {
	return getSQLType(getColumnIndex(tableName, columnName));
}

std::string ResultSetMetaData::getSQLTypeName(unsigned int columnIndex) const {
	SQLCHAR* sqlPtr = 0;
	char strAux[1];
	SQLSMALLINT length = 0;
	SQLColAttribute(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_TYPE_NAME, &strAux, 1, (SQLSMALLINT*) &length, NULL);
	length++;
	sqlPtr = (SQLCHAR*) new SQLCHAR[length];
	SQLColAttributeA(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_TYPE_NAME, sqlPtr, length, (SQLSMALLINT*) &length, NULL);

	std::string ret((char*) sqlPtr);
	delete [] sqlPtr;

	return StringWrapper(ret).toUpper();

}

std::string ResultSetMetaData::getSQLTypeName(std::string tableName, std::string columnName) const {
	return getSQLTypeName(getColumnIndex(tableName, columnName));
}


std::string ResultSetMetaData::getColumnName(unsigned int columnIndex) const {
	SQLCHAR* sqlPtr = 0;
	char strAux[1];
	SQLSMALLINT length = 0;
	SQLColAttribute(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_BASE_COLUMN_NAME, &strAux, 1, (SQLSMALLINT*) &length, NULL);
	length++;
	sqlPtr = (SQLCHAR*) new SQLCHAR[length];
	SQLColAttributeA(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_BASE_COLUMN_NAME, sqlPtr, length, (SQLSMALLINT*) &length, NULL);

	std::string ret((char*) sqlPtr);
	delete [] sqlPtr;

	return StringWrapper(ret).toUpper();

}

unsigned int ResultSetMetaData::getColumnIndex(std::string tableName, std::string columnName) const {
	tableName = StringWrapper(tableName).toUpper();
	columnName = StringWrapper(columnName).toUpper();
	std::map<std::string, unsigned int>::const_iterator it = columnsIndexes.find(tableName + "." + columnName);

	if (it == columnsIndexes.end()) {
		throw DASColumnNotFoundException("No such column on table " + tableName + ": " + columnName);
	}

	return it->second;

}


std::string ResultSetMetaData::getTableName(unsigned int columnIndex) const {
	SQLCHAR* sqlPtr = 0;
	char strAux[1];
	SQLSMALLINT length = 0;
	SQLColAttribute(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_TABLE_NAME, &strAux, 1, (SQLSMALLINT*) &length, NULL);
	length++;
	sqlPtr = (SQLCHAR*) new SQLCHAR[length];
	SQLColAttributeA(resultSet->getStatement()->getODBCStatement(), columnIndex + 1, SQL_DESC_TABLE_NAME, sqlPtr, length, (SQLSMALLINT*) &length, NULL);

	std::string ret((char*) sqlPtr);
	delete [] sqlPtr;

	return StringWrapper(ret).toUpper();

}

std::string ResultSetMetaData::getTableName(std::string tableName, std::string columnName) const {
	return getTableName(getColumnIndex(tableName, columnName));
}


SQLSMALLINT ResultSetMetaData::getSQLCType(unsigned int columnIndex) const {
	return getSQLCType(getSQLType(columnIndex));
}

SQLSMALLINT ResultSetMetaData::getSQLCType(std::string tableName, std::string columnName) const {
	return getSQLCType(getSQLType(getColumnIndex(tableName, columnName)));
}

unsigned int ResultSetMetaData::getColumnCount(void) const {
	SQLUINTEGER columnCount = 0;
	SQLRETURN ret = SQLColAttribute(resultSet->getStatement()->getODBCStatement(), NULL, SQL_DESC_COUNT, NULL, NULL, NULL, &columnCount);
	
	return (unsigned int) columnCount;

}

bool ResultSetMetaData::containsColumn(std::string tableName, std::string columnName) const {
	std::map<std::string, unsigned int>::const_iterator it = columnsIndexes.find(tableName + "." + columnName);
	
	if (it == columnsIndexes.end()) {
		return false;
	}

	return true;

}

const ResultSet& ResultSetMetaData::getResultSet(void) const {
	return *resultSet;
}

		};
	};
};
