// This program 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 2 of the License, or
// (at your option) any later version.

// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

// Copyright 2003 Liam Girdwood  

#include "config.h"
#include "import.hh"
#include "astro_object.hh"
#include "deep_object.hh"
#include "star_object.hh"
#include "skymap_object.hh"
#include "comet.hh"
#include "galaxy.hh"
#include "nebula.hh"
#include "asteroid.hh"

#include <string>
#include <iostream>
#include <fstream>

namespace Pollux
{

/*! \fn Import::Import()
*
* Constructor. Set up default import paths.
*/
Import::Import()
{
	m_path = NOVA_CATALOG_DIR;
	m_path += "/";
	m_name = "";
}

/*! \fn Import::~Import()
*
* Destructor. Clear the import table.
*/
Import::~Import()
{
	m_import_table.clear();
}

/*! \fn void Import::set_name (std::string& name)
*
* Set the name of the catalog to import.
*/
void Import::set_name (std::string& name)
{
	m_name = name;
}

/*! \fn void Import::set_path (std::string& path)
*
* Set the path to the ASCII catalog
*/
void Import::set_path (std::string& path)
{
	m_path = path;
}

/*! \fn bool Import::parse_descriptor ()
* \return true if the descriptor was parsed successfully.
*
* Parse the XML catalog descriptor and build up an internal
* map of catalog data elements.
*/
bool Import::parse_descriptor ()
{
	Glib::ustring filename = m_path + m_name + ".xml";

	try {
		xmlpp::DomParser parser;
		parser.set_validate();
		parser.set_substitute_entities();
		parser.parse_file(filename);
		if(parser) {
			const xmlpp::Node* pNode = parser.get_document()->get_root_node();
			parse_node(pNode);
		}
	}
	catch(const std::exception& ex) {
		std::cerr << "Exception caught: " << ex.what() << std::endl;
		return false;
	}
	
	if (!build_import_table ()) {
		std::cerr << "could not parse descriptor " << std::endl;
		return false;
	}
	
	return true;
}

void Import::parse_node(const xmlpp::Node* node)
{
	const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
	const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
	const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(node);
	const xmlpp::Attribute* attribute;
	
	if(nodeText && nodeText->is_white_space()) 
		return;
	
	std::string nodename = node->get_name();
	if(const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node)) {
	
		attribute = nodeElement->get_attribute("RA");
		if(attribute) {
			//m_centre_ra = atof(attribute->get_value().c_str());
		}
		attribute = nodeElement->get_attribute("DEC");
		if(attribute) {
			//m_centre_dec = atof(attribute->get_value().c_str());
		}
	}
	
	if(!nodeContent) {
		//Recurse through child nodes:
		xmlpp::Node::NodeList list = node->get_children();
		for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter) {
			parse_node(*iter); //recursive
		}
	}
}

/*! \fn Catalog* Import::import_catalog ()
* \return Returns a pointer to a catalog object on success,
* otherwise returns 0.
* 
* Imports a catalogs ASCII data files into a binary representation.
*/
Catalog* Import::import_catalog ()
{
	std::ifstream* file;
	std::string file_name;
	char c, line[MAX_LINE_SIZE];
	
	m_first_pass = true;
	
	// is the catalog descriptor valid
	if (!parse_descriptor()) {
		std::cerr << "could not parse catalog descriptor" << std::endl;
		return 0;
	}
	
	// create the catalog
	Catalog* catalog = new Catalog(m_name, m_description, m_maintainer, 
									m_type_str, m_created, m_version, m_history,
									m_acknowledge);
	if (!catalog) {
		std::cerr << "could not create catalog" << std::endl;
		return 0;
	}
	
	// iterate through files
	for (std::list<std::string*>::iterator j = m_files.begin(); j != m_files.end(); j++) {
		// try and open catalog file
		file_name = m_path + "/" + **j;
		std::cout << "opening " << file_name << std::endl;
		file = new std::ifstream(file_name.c_str());

		if (file->is_open()) {
			// ok file is open, read it 
			while(!(file->eof ())) {
				// read 1 line of the file
				file->getline(line, 1024, '\n');
				
				// process line and create object
				if (!create_object (catalog, line)) {
					// cant create object, keep trying
					std::cerr << __FILE__ << " cat create object " << std::endl;
				}
			}
		} else {
			// cant open catalog file
			std::cerr << "cant open catalog file " << std::endl;
			file->close();
			delete file;
			delete catalog;
			return 0;
		}
		
		file->close();
		delete file;
	}

	return catalog;
}

/*! \fn Catalog* Import::import_catalog ()
* \return true on success.
*
* Builds a table mapping catalog elements to import data
*/
bool Import::build_import_table ()
{
	bool success = false;
#if 0	
	// get cat info from the sax parser
	parser->get_cat_description (m_description);
	parser->get_cat_maintainer (m_maintainer);
	parser->get_cat_type (m_type_str);
	m_size = parser->get_cat_size ();
	parser->get_cat_created (m_created);
	parser->get_cat_history(m_history);
	parser->get_cat_version(m_version);
	parser->get_cat_acknowledge(m_acknowledge);
	
	// set up catalog type
	if (m_type_str == "skymap")
		m_type = SKYMAP;
	else if (m_type_str == "star")
		m_type = STAR;
	else if (m_type_str == "galaxy")
		m_type = GALAXY;
	else if (m_type_str == "nebula")
		m_type = NEBULA;
	else if (m_type_str == "asteroid")
		m_type = ASTEROID;
	else if (m_type_str == "comet")
		m_type = COMET;
	
	// get cat file list
	if (!(parser->get_file_list(m_files)))
		return false;
	
	// get elements from the sax parser
	int lowest = -1;
	Element* element = parser->get_next_element(lowest);
	while (element) {
		success = true;
		
		// create new item
		import_item* item = new import_item;
		
		// item start position
		item->pos = element->position;

		// item length
		item->len = strtol(element->type.c_str()+1, 0, 10);

		// item type
		switch (element->type[0]) {
			case 'I':
				item->type = Castor::AstroObject::INT;
				break;
			case 'F':
				item->type = Castor::AstroObject::DOUBLE;
				break;
			case 'A':
				item->type = Castor::AstroObject::STRING;
				break;
			default:
				item->type = Castor::AstroObject::STRING;
				break;
		}
		
		// build remainder of item
		item->name = element->name;
		item->units = element->units;
		item->desc = element->description;
		item->extra = false;
		m_import_table.push_back (item);
		
		// next element ?
		lowest = item->pos;
		element = parser->get_next_element(lowest);
	}
#endif	
	return success;
}


/*! \fn bool Import::create_object (Catalog* catalog, const char * line)
* \return True on success
*
* Creates the appropraite catalog objects.
*/
bool Import::create_object (Catalog* catalog, const char * line)
{
	Castor::AstroObject* object;

	switch (m_type) {
		case STAR:
			object = new Castor::StarObject();
			break;
		case SKYMAP:
			object = new Castor::SkymapObject();
			break;
		case DEEP_OTHER:
			object = new Castor::DeepObject();
			break;
		case NEBULA:
			object = new Castor::Nebula();
			break;
		case ASTEROID:
			object = new Castor::Asteroid();
			break;
		case COMET:
			object = new Castor::Comet();
			break;
		case GALAXY:
			object = new Castor::Galaxy();
			break;
	}
	
	// add extra element descriptors
	if (m_first_pass) {
		for (std::list<import_item*>::iterator j = m_import_table.begin(); j != m_import_table.end(); j++)  {
			if (!(object->check_element((*j)->name))) {
				catalog->add_description ((*j)->name, (*j)->units, (*j)->desc);
				(*j)->extra = true;
			}
		}
	}
	m_first_pass = false;
		
	// iterate through items
	for (std::list<import_item*>::iterator j = m_import_table.begin(); j != m_import_table.end(); j++)  {
		std::string val;
		// if item is non blank the add, else if blank and extra also add it
		if (get_element (*j, val, line))
			object->add_element ((*j)->name, (*j)->type, val);
		else
			if ((*j)->extra)
				object->add_element ((*j)->name, (*j)->type, val);
	}
	// add object
	catalog->add(object);
	
	return true;
}


/*! \fn int Import::scan(std::list<std::string*>& names, 
				std::list<std::string*>& description)
* \param names A list of catalog names found
* \param description A list of catalog descriptions found
* \return Number of catalogs found
* \todo implement catalog scan
*
* Scan the filesystem catalog dir for Nova catalogs
*/
int Import::scan(std::list<std::string*>& names, 
				std::list<std::string*>& description)
{
}

/*! \fn bool Import::get_element (import_item* item, std::string& value, const char* line)
* \return True on success
*
* Extracts an individual ASCII data element out of the catalog.
*/
bool Import::get_element (import_item* item, std::string& value, const char* line)
{
	int i, pos;
	char buffer[256];
	
	pos = item->pos -1;
	for (i = 0; i < item->len; i++) {
		if (!(isspace(*(line + pos + i)))) {
			strncpy (buffer, line + pos, item->len);
			buffer[item->len] = 0;
			value = buffer;
			return true;
		}
	}
	return false;
}


}


syntax highlighted by Code2HTML, v. 0.9.1