/*
 * 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/GraphBuilder.h"

namespace apache {
	namespace das {
		namespace rdb {

GraphBuilder::GraphBuilder(const ConfigImpl& config, ResultSetPtr resultSet) {
	graphBuilderMetaData = new GraphBuilderMetaData(config, resultSet->getResultSetMetaData()) ;
	commonj::sdo::DataFactoryPtr dataFactory = graphBuilderMetaData->createGraph();
	
	this->resultSet = resultSet;
	const std::map<std::string, Table*>& tables = graphBuilderMetaData->getTables();
	
	while (resultSet->next()) {
	
		std::map<std::string, Table*>::const_iterator it;
		for (it = tables.begin() ; it != tables.end() ; it++) {
			Table* table = it->second;
			
			std::list<TableData*>* tableList;
			TableData* tableData = new TableData(*table, resultSet);

			if (!tableData->hasPK()) {
				delete tableData;
				continue;

			}

			std::map<std::string, std::list<TableData*>*>::iterator it2 = 
				tablesData.find(table->getTableName());

			if (it2 == tablesData.end()) {
				tableList = new std::list<TableData*>();
				tablesData.insert(std::make_pair(table->getTableName(), tableList));

			} else {
				tableList = it2->second;
			}

			std::list<TableData*>::const_iterator it3;
			bool duplicated = false;

			for (it3 = tableList->begin() ; it3 != tableList->end() ; it3++) {

				if (*tableData == **it3) {
					delete tableData;
					duplicated = true;
					break;

				}

			}

			if (!duplicated) {
				tableList->push_back(tableData);
			}
			
		}

	}

	int a = resultSet->getRowCount();

	root = dataFactory->create(config.getURI(), DAS_ROOT_NAME);
	std::map<std::string, std::list<TableData*>*>::iterator it;

	if (tablesData.size() > 0) {

		for (it = tablesData.begin() ; it != tablesData.end() ; it++) {
			std::list<TableData*>& tableList = *it->second;
			std::list<TableData*>::iterator it2;

			for (it2 = tableList.begin() ; it2 != tableList.end() ; it2++) {
				(*it2)->populateDataGraph(*this);
			}

		}

		std::map<std::string, Table*>::const_iterator it2;
		std::map<std::string, Relationship*>& relationships = graphBuilderMetaData->getRelationships();
		std::map<std::string, std::map<const KeyDataList*, TableData*, KeyDataCmp>*> tablesDataByPK;
		std::map<std::string, std::list<TableData*>*>::iterator it3;
		for (it3 = tablesData.begin() ; it3 != tablesData.end() ; it3++) {
			std::map<const KeyDataList*, TableData*, KeyDataCmp>* tableList = new std::map<const KeyDataList*, TableData*, KeyDataCmp>();
			std::list<TableData*>::iterator it4;

			for (it4 = it3->second->begin() ; it4 != it3->second->end() ; it4++) {
				TableData* tableData = *it4;
				tableList->insert(std::make_pair(&tableData->getPrimaryKeys(), tableData));

			}

			tablesDataByPK.insert(std::make_pair(it3->first, tableList));

		}
			
		for (it2 = tables.begin() ; it2 != tables.end() ; it2++) {
			RelationshipWrapper relationshipWrapper;
			std::list<Relationship*>& fkTableRelationships = relationshipWrapper.getRelationshipsByTableName(relationships, it2->first, false);

			std::list<Relationship*>::iterator relationshipIterator;
			for (relationshipIterator = fkTableRelationships.begin() ; 
				relationshipIterator != fkTableRelationships.end() ; relationshipIterator++) {

					std::list<TableData*>& fkTablesList = 
						*((std::map<std::string, std::list<TableData*>*>::iterator) tablesData.find(
						(*relationshipIterator)->getFKTableName()))->second;

					std::map<const KeyDataList*, TableData*, KeyDataCmp>& pkTablesData = 
							*((std::map<std::string, std::map<const KeyDataList*, TableData*, KeyDataCmp>*>::iterator) 
							tablesDataByPK.find((*relationshipIterator)->getPKTableName()))->second;

					std::list<TableData*>::iterator tableDataIterator;
					for (tableDataIterator = fkTablesList.begin() ; tableDataIterator != 
						fkTablesList.end() ; tableDataIterator++) {

						TableData& fkTableData = **tableDataIterator;
						const std::map<std::string, const KeyPair*>& keyPairs = (*relationshipIterator)->getKeyPairs();
						std::map<std::string, const KeyPair*>::const_iterator keyPairIterator;
						KeyDataList fksColumnList;

						for (keyPairIterator = keyPairs.begin() ; keyPairIterator != keyPairs.end() ;
							keyPairIterator++) {
						
								const KeyPair& keyPair = *keyPairIterator->second;
								ColumnData* columnData = fkTableData.
									getColumnData(keyPair.getFKColumnName());

								fksColumnList.insert(std::make_pair(keyPair.getPKColumnName(),
									columnData));
			
						}

						std::map<const KeyDataList*, TableData*, KeyDataCmp>::iterator pkTablaDataIterator = 
							pkTablesData.find(&fksColumnList);

						if (pkTablaDataIterator != pkTablesData.end()) {
							TableData& pkTableData = *pkTablaDataIterator->second;
						
							if ((*relationshipIterator)->isMany()) {
								pkTableData.getGraphObject()->getList((*relationshipIterator)->getName().c_str()).
									append(fkTableData.getGraphObject());

							} else {
								pkTableData.getGraphObject()->setDataObject((*relationshipIterator)->getName().c_str(), 
									fkTableData.getGraphObject());

							}

						}

					}

			}

		}

		std::map<std::string, std::map<const KeyDataList*, TableData*, KeyDataCmp>*>::iterator tablesDataByPKIterator;
		for (tablesDataByPKIterator = tablesDataByPK.begin() ; tablesDataByPKIterator != 
			tablesDataByPK.end() ; tablesDataByPKIterator++) {

				delete tablesDataByPKIterator->second;

		}

	}

}

GraphBuilder::~GraphBuilder(void) {
	std::map<std::string, std::list<TableData*>*>::iterator it;
	std::list<TableData*>::iterator it2;

	for (it = tablesData.begin() ; it != tablesData.end() ; it++) {
		std::list<TableData*>* tableList = it->second;

		for (it2 = tableList->begin() ; it2 != tableList->end() ; it2++) {
			delete *it2;
		}

		delete tableList;

	}

	delete graphBuilderMetaData;

}

commonj::sdo::DataObjectPtr GraphBuilder::getRoot(void) const {
	return root;
}

		};
	};
};
