/*
 * 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/Table.h"

namespace apache {
	namespace das {
		namespace rdb {

Table::Table(std::string tableName) {
	tableName = StringWrapper(tableName).toUpper();

	if (tableName.find(' ') != -1 || tableName.find('\t') != -1 || tableName.find('\n') != -1 || tableName.find('\r') != -1) {
		throw DASInvalidTableNameException("Table name must not contain whitespace characters!");
	}

	this->tableName = tableName;
	this->typeName = tableName;
	this->columns = new std::map<std::string, const Column*>();

}

Table::Table(std::string tableName, std::string typeName) {
	tableName = StringWrapper(tableName).toUpper();

	if (tableName.find(' ') != -1 || tableName.find('\t') != -1 || tableName.find('\n') != -1 || tableName.find('\r') != -1) {
		throw DASInvalidTableNameException("Table name must not contain whitespace characters!");
	}

	this->tableName = tableName;
	this->typeName = typeName;
	this->columns = new std::map<std::string, const Column*>();

}

Table::Table(const Table& table) {
	tableName = table.tableName;
	typeName = table.typeName;
	this->columns = new std::map<std::string, const Column*>();

	std::map<std::string, const Column*>::iterator it;
	for (it = table.columns->begin() ; it != table.columns->end() ; it++) {
		Column* newColumn = new Column(*it->second);
		newColumn->setContainerTable(this);
		columns->insert(std::make_pair(it->first, newColumn));
		
	}

}

Table::~Table(void) {
	std::map<std::string, const Column*>::iterator it;

	for (it = columns->begin() ; it != columns->end() ; it++) {
		delete it->second;
	}

	delete columns;

}

const Column* Table::getColumnByProperty(std::string propertyName) const {
	
	for (std::map<std::string, const Column*>::const_iterator it = columns->begin() ;
		it != columns->end() ; it++) {
	
			if (it->second->getPropertyName() == propertyName) {
				return it->second;
			}

	}

	return 0;

}

std::string Table::getTableName(void) const {
	return tableName;
}

Column& Table::addColumn(std::string columnName, SQLSMALLINT sqlType) {
	Column* column = new Column(columnName, sqlType);
	return newColumn(*column);
}

Column& Table::addColumn(const Column& column) {
	return newColumn(*(new Column(column)));
}

Column& Table::newColumn(Column& column) {
	std::string columnName = column.getName();
	Column* tableColumn = getColumn(columnName);

	if (tableColumn == 0) {
		columns->insert(std::make_pair(columnName, &column));
		column.setContainerTable(this);

		return column;

	}

	return *tableColumn;

}

void Table::createGraph(const GraphBuilderMetaData& graphBuilderMetaData, commonj::sdo::DataFactoryPtr dataFactory) const {
	std::map<std::string, const Column*>::const_iterator it;
	std::map<std::string, Relationship*>& relationships = graphBuilderMetaData.getRelationships();
	std::map<std::string, Relationship*>::const_iterator relationshipIterator;
	std::list<Relationship*> tablePKRelationships;
	std::list<Relationship*> tableFKRelationships;
	std::list<std::string> relationshipColumns;
	
	for (relationshipIterator = relationships.begin() ; relationshipIterator != 
		relationships.end() ; relationshipIterator++) {

			if (relationshipIterator->second->getFKTableName() == tableName) {
				const std::map<std::string, const KeyPair*>& keyPairs = relationshipIterator->second->getKeyPairs();
				std::map<std::string, const KeyPair*>::const_iterator keyPairIterator;

				for (keyPairIterator = keyPairs.begin() ; keyPairIterator != 
					keyPairs.end() ; keyPairIterator++) {

						relationshipColumns.push_back(keyPairIterator->second->
							getFKColumnName());

				}

			}

	}
	
	for (it = columns->begin() ; it != columns->end() ; it++) {
		const Column& column = *(it->second);

		if (graphBuilderMetaData.getResultSetMetaData().containsColumn(tableName, column.getName())) {

			std::list<std::string>::const_iterator it2 = std::find(relationshipColumns.begin(),
				relationshipColumns.end(), column.getName());
		
			if (it2 == relationshipColumns.end()) {
				dataFactory->addPropertyToType(graphBuilderMetaData.getConfig().getURI(), typeName.c_str(),
					column.getPropertyName().c_str(), SDO_NAMESPACE, 
					ODBCTypeHelper::getSDOType(column.getSQLType()).c_str() , false, 
					false, true);
			
			}

		}
	
	}

}

const Column* Table::getOCCColumn(void) const {

	for (std::map<std::string, const Column*>::const_iterator it = columns->begin() ; it != columns->end() ; it++) {

		if (it->second->isCollision()) {
			return it->second;
		}

	}

	return 0;

}

void Table::setTypeName(std::string typeName) {
	typeName = StringWrapper(typeName).toUpper();

	if (typeName.find(' ') != -1 || typeName.find('\t') != -1 || typeName.find('\n') != -1 || typeName.find('\r') != -1) {
		throw DASInvalidTypeNameException("Type must not contain whitespace characters!");
	}

	this->typeName = typeName;

}

Column* Table::getColumn(std::string columnName) {
	std::map<std::string, const Column*>::const_iterator it = columns->find(columnName);

	if (it == columns->end()) {
		return 0;
	}

	return (Column*) it->second;

}

const Column* Table::getColumn(std::string columnName) const {
	return ((Table*) this)->getColumn(columnName);
}

std::string Table::getTypeName(void) const {
	return typeName;
}

const std::map<std::string, const Column*>& Table::getColumns(void) const {
	return *columns;
}

unsigned int Table::getPKColumnCount(void) const {
	unsigned int count = 0;
	std::map<std::string, const Column*>::const_iterator it;

	for (it = columns->begin() ; it != columns->end() ; it++) {

		if (it->second->isPK()) {
			count++;
		}

	}

	return count;

}

		};
	};
};
