/*
 * 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/ConfigImpl.h"

namespace apache {
	namespace das {
		namespace rdb {

ConfigImpl::ConfigImpl(void) {
	relationships = new std::map<std::string, const Relationship*>();
	tables = new std::map<std::string, const Table*>();
	commands = new std::map<std::string, std::string>();

	convOverConfig = false;
		
}

ConfigImpl::ConfigImpl(const Config& config) : Config(config) {
	ConfigImpl& thisConfig = (ConfigImpl&) config;
	tables = new std::map<std::string, const Table*>();
	relationships = new std::map<std::string, const Relationship*>();
	commands = new std::map<std::string, std::string>(*thisConfig.commands);
	
	std::map<std::string, const Table*>::iterator tableIterator;
	std::map<std::string, const Relationship*>::iterator relationshipIterator;
	std::map<std::string, std::string>::iterator commandIterator;

	this->convOverConfig = thisConfig.convOverConfig;

	for (tableIterator = thisConfig.tables->begin() ; tableIterator != thisConfig.tables->end() ; 
		tableIterator++) {

		tables->insert(std::make_pair(tableIterator->first, new Table(*tableIterator->second)));

	}

	for (relationshipIterator = thisConfig.relationships->begin() ; 
		relationshipIterator != thisConfig.relationships->end() ; relationshipIterator++) {

		relationships->insert(std::make_pair(relationshipIterator->first, 
			new Relationship(*relationshipIterator->second)));

	}

}

ConfigImpl::ConfigImpl(std::string xmlFile) {
	relationships = new std::map<std::string, const Relationship*>();
	tables = new std::map<std::string, const Table*>();
	commands = new std::map<std::string, std::string>();

	convOverConfig = false;

	commonj::sdo::DataFactoryPtr dataFactory = commonj::sdo::DataFactory::getDataFactory();
	dataFactory->addType(DAS_NAMESPACE, "RootType"); 
	dataFactory->addType(DAS_NAMESPACE, "Table"); 
	dataFactory->addType(DAS_NAMESPACE, "Relationship"); 
	dataFactory->addType(DAS_NAMESPACE, "KeyPair"); 
	dataFactory->addType(DAS_NAMESPACE, "Column");
	dataFactory->addType(DAS_NAMESPACE, "Config");
	dataFactory->addType(DAS_NAMESPACE, "Command");
	
	const commonj::sdo::Type& rootType = dataFactory->getType(DAS_NAMESPACE, "RootType");
	const commonj::sdo::Type& table = dataFactory->getType(DAS_NAMESPACE, "Table");
	const commonj::sdo::Type& relationship = dataFactory->getType(DAS_NAMESPACE, "Relationship");
	const commonj::sdo::Type& keyPair = dataFactory->getType(DAS_NAMESPACE, "KeyPair");
	const commonj::sdo::Type& column = dataFactory->getType(DAS_NAMESPACE, "Column");
	const commonj::sdo::Type& config = dataFactory->getType(DAS_NAMESPACE, "Config");
	const commonj::sdo::Type& command = dataFactory->getType(DAS_NAMESPACE, "Command");

	dataFactory->addPropertyToType(rootType, "Config", config);
	
	dataFactory->addPropertyToType(table, "Column", column, true, false, true);
	dataFactory->addPropertyToType(table, "tableName", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(table, "typeName", SDO_NAMESPACE, "String", false, false, true);

	dataFactory->addPropertyToType(config, "Table", table, true, false, true);
	dataFactory->addPropertyToType(config, "Relationship", relationship, true, false, true);
	dataFactory->addPropertyToType(config, "Command", command, true, false, true);
	dataFactory->addPropertyToType(config, "uri", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->setDefault(SDO_NAMESPACE, "String", "uri", "");

	dataFactory->addPropertyToType(command, "name", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(command, "SQL", SDO_NAMESPACE, "String", false, false, true);
	
	dataFactory->addPropertyToType(relationship, "KeyPair", keyPair, true, false, true);
	dataFactory->addPropertyToType(relationship, "name", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(relationship, "primaryKeyTable", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(relationship, "foreignKeyTable", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(relationship, "many", SDO_NAMESPACE, "Boolean", false, false, true);
	dataFactory->setDefault(relationship, "many", true);

	dataFactory->addPropertyToType(keyPair, "primaryKeyColumn", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(keyPair, "foreignKeyColumn", SDO_NAMESPACE, "String", false, false, true);
	
	dataFactory->addPropertyToType(column, "columnName", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(column, "sqlType", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(column, "propertyName", SDO_NAMESPACE, "String", false, false, true);
	dataFactory->addPropertyToType(column, "primaryKey", SDO_NAMESPACE, "Boolean", false, false, true);
	dataFactory->addPropertyToType(column, "primaryKey", SDO_NAMESPACE, "Boolean", false, false, true);
	dataFactory->addPropertyToType(column, "collision", SDO_NAMESPACE, "Boolean", false, false, true);
	dataFactory->addPropertyToType(column, "managed", SDO_NAMESPACE, "Boolean", false, false, true);
	dataFactory->setDefault(column, "primaryKey", false);
	dataFactory->setDefault(column, "collision", false);
	dataFactory->setDefault(column, "managed", true);

	dataFactory->resolve();

	commonj::sdo::XMLHelperPtr xmlh = commonj::sdo::HelperProvider::getXMLHelper(dataFactory);
	commonj::sdo::XMLDocumentPtr doc = xmlh->loadFile(xmlFile.c_str(), DAS_NAMESPACE);
	commonj::sdo::DataObjectPtr root = doc->getRootDataObject();

	commonj::sdo::DataObjectList& tableList = root->getList("Table");
	
	for (unsigned int i = 0 ; i < tableList.size() ; i++) {
		std::string tableName = SDODataObjectWrapper(tableList[i]).getString("tableName");

		if (tableName != "") {
			Table& table = addTable(tableName);
			std::string typeName = SDODataObjectWrapper(tableList[i]).getString("typeName");

			if (typeName != "") {
				table.setTypeName(typeName);
			}
			
			commonj::sdo::DataObjectList& columnList = tableList[i]->getList("Column");

			for (unsigned int j = 0 ; j < columnList.size() ; j++) {
				std::string columnName = SDODataObjectWrapper(columnList[j]).getString("columnName");
				std::string sqlType = SDODataObjectWrapper(columnList[j]).getString("sqlType");

				if (columnName != "" && sqlType != "") {
					Column& column = table.addColumn(columnName, ODBCTypeHelper::getSQLType(sqlType));
					std::string propertyName = SDODataObjectWrapper(columnList[j]).getString("propertyName");

					if (propertyName != "") {
						column.setPropertyName(propertyName);
					}

					column.setPK(columnList[j]->getBoolean("primaryKey"));
					column.setManaged(columnList[j]->getBoolean("managed"));
					column.setCollision(columnList[j]->getBoolean("collision"));

				}		
				

			}

		}

	}

	commonj::sdo::DataObjectList& relationshipList = root->getList("Relationship");

	for (unsigned int i = 0 ; i < relationshipList.size() ; i++) {
		std::string primaryKeyTable = SDODataObjectWrapper(relationshipList[i]).getString("primaryKeyTable");
		std::string foreignKeyTable = SDODataObjectWrapper(relationshipList[i]).getString("foreignKeyTable");
		std::string name = SDODataObjectWrapper(relationshipList[i]).getString("name");;
		
		Relationship& relationship = addRelationship(primaryKeyTable, foreignKeyTable);
		relationship.setMany(relationshipList[i]->getBoolean("many"));

		commonj::sdo::DataObjectList& keyPairList = relationshipList[i]->getList("KeyPair");

		for (unsigned int i = 0 ; i < keyPairList.size() ; i++) {
			std::string primaryKeyColumn = SDODataObjectWrapper(keyPairList[i]).getString("primaryKeyColumn");
			std::string foreignKeyColumn = SDODataObjectWrapper(keyPairList[i]).getString("foreignKeyColumn");
			relationship.addKeyPair(primaryKeyColumn, foreignKeyColumn);

		}

	}

	commonj::sdo::DataObjectList& commandList = root->getList("Command");

	for (unsigned int i = 0 ; i < commandList.size() ; i++) {
		std::string name = SDODataObjectWrapper(commandList[i]).getString("name");
		std::string sql = SDODataObjectWrapper(commandList[i]).getString("SQL");
		
		commands->insert(std::make_pair(name, sql));

	}

	setURI(StringWrapper(root, "uri").getString());

}

ConfigImpl::~ConfigImpl(void) {
	std::map<std::string, const Table*>::const_iterator tableIterator;
	std::map<std::string, const Relationship*>::const_iterator relationshipIterator;

	tableIterator = tables->begin();
	for ( ; tableIterator != tables->end() ; 
		tableIterator++) {
		delete tableIterator->second;
	}
	
	for (relationshipIterator = relationships->begin() ; 
		relationshipIterator != relationships->end() ; relationshipIterator++) {
			delete relationshipIterator->second;
	}

	delete relationships;
	delete tables;
	delete commands;

}

std::string ConfigImpl::getCommand(std::string commandName) const {
	std::map<std::string, std::string>::const_iterator it = commands->find(commandName);

	if (it == commands->end()) {
		throw DASCommandNotFoundException();
	}

	return it->second;

}

const Table* ConfigImpl::getTableByTypeName(std::string typeName) const {
	std::map<std::string, const Table*>::const_iterator it;

	for (it = tables->begin() ; 
		it != tables->end() ; it++) {

			if (it->second->getTypeName() == typeName) {
				return it->second;
			}

	}

	return 0;
	
}

Table& ConfigImpl::addTable(std::string tableName) {
	return newTable(*(new Table(tableName)));
}

Table& ConfigImpl::addTable(const Table& table) {
	return newTable(*(new Table(table)));
}

Table& ConfigImpl::newTable(Table& table) {
	std::string tableName = table.getTableName();
	std::map<std::string, const Table*>::iterator tableIterator = tables->find(tableName);
	
	if (tableIterator == tables->end()) {
		tables->insert(std::make_pair(tableName, &table));

		return table;

	}

	return (Table&) *tableIterator->second;

}

const std::map<std::string, Relationship*>& ConfigImpl::getRelationships(void) const {
	return (const std::map<std::string, Relationship*>&) *relationships;
}

Relationship& ConfigImpl::addRelationship(std::string pkTableName, std::string fkTableName, std::string name) {
	return newRelationship(*(new Relationship(pkTableName, fkTableName, name)));
}

Relationship& ConfigImpl::addRelationship(const Relationship& relationship) {
	return newRelationship(*(new Relationship(relationship)));
}

Relationship& ConfigImpl::newRelationship(Relationship& relationship) {
	std::string pkXfk = relationship.getPKTableName() + "." + relationship.getFKTableName();
	std::map<std::string, const Relationship*>::iterator it = relationships->find(pkXfk);
	
	if (it == relationships->end()) {
		relationships->insert(std::make_pair(pkXfk, &relationship));

		return relationship;

	}

	return (Relationship&) *it->second;

}

const Relationship* ConfigImpl::getRelationship(std::string pkTableName, std::string fkTableName) const {
	std::map<std::string, const Relationship*>::iterator it = relationships->find(pkTableName + "." + fkTableName);

	if (it == relationships->end()) {
		return 0;
	}

	return it->second;

}

const std::map<std::string, Table*>& ConfigImpl::getTables(void) const {
	return (const std::map<std::string, Table*>&) *tables;
}

bool ConfigImpl::isConvOverConfig(void) const {
	return convOverConfig;
}

const Table* ConfigImpl::getTable(std::string tableName) const {
	std::map<std::string, const Table*>::iterator tableIterator = tables->find(tableName);

	if (tableIterator == tables->end()) {
		return 0;
	}

	return tableIterator->second;

}

		};
	};
};
