/* $Id$ */

/**
 * \file 
 * $Revision$
 * $Date$ 
 * 
 * Copyright (C) 2010 The University of Sydney, Australia
 *
 * This file is part of GPlates.
 *
 * GPlates is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 2, as published by
 * the Free Software Foundation.
 *
 * GPlates 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 Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include <QLocale> 

#include "ExportTotalRotationAnimationStrategy.h"

#include "app-logic/ApplicationState.h"
#include "app-logic/FeatureCollectionFileState.h"

#include "global/GPlatesAssert.h"

#include "gui/ExportAnimationContext.h"
#include "gui/AnimationController.h"
#include "gui/CsvExport.h"

#include "maths/MathsUtils.h"

#include "presentation/ViewState.h"


GPlatesGui::ExportTotalRotationAnimationStrategy::ExportTotalRotationAnimationStrategy(
		GPlatesGui::ExportAnimationContext &export_animation_context,
		const const_configuration_ptr &configuration):
	ExportAnimationStrategy(export_animation_context),
	d_configuration(configuration)
{
	set_template_filename(d_configuration->get_filename_template());
}

bool
GPlatesGui::ExportTotalRotationAnimationStrategy::do_export_iteration(
		std::size_t frame_index)
{	
	GPlatesFileIO::ExportTemplateFilenameSequence::const_iterator &filename_it = 
		*d_filename_iterator_opt;

	GPlatesAppLogic::ApplicationState &application_state =
		d_export_animation_context_ptr->view_state().get_application_state();

	// Export the default rotation layer.
	//
	// Now that layers enables users to have more than one reconstruction tree we need to
	// distinguish which one the user intends to export.
	//
	// FIXME: For now we just use the default reconstruction tree generated by the default
	// reconstruction tree layer.
	// Later we might want to let the user choose.
	GPlatesAppLogic::ReconstructionTree::non_null_ptr_to_const_type reconstruction_tree =
			application_state.get_current_reconstruction()
				.get_default_reconstruction_layer_output()->get_reconstruction_tree();

	GPlatesAppLogic::ReconstructionTree::edge_map_type::const_iterator it;
	GPlatesAppLogic::ReconstructionTree::edge_map_type::const_iterator it_begin =
			reconstruction_tree->get_all_edges().begin();
	GPlatesAppLogic::ReconstructionTree::edge_map_type::const_iterator it_end =
			reconstruction_tree->get_all_edges().end();

	GPlatesGui::CsvExport::LineDataType data_line;
	std::vector<GPlatesGui::CsvExport::LineDataType> data;

	for(it = it_begin; it != it_end ; ++it)
	{
		const bool is_relative_rotation = (
				(d_configuration->rotation_type == Configuration::RELATIVE_COMMA) ||
				(d_configuration->rotation_type == Configuration::RELATIVE_SEMICOLON) ||
				(d_configuration->rotation_type == Configuration::RELATIVE_TAB) );

		GPlatesMaths::FiniteRotation fr =
				is_relative_rotation
				? it->second->get_relative_rotation()
				: it->second->get_composed_absolute_rotation();

		const GPlatesMaths::UnitQuaternion3D &uq = fr.unit_quat();

		QString plate_id_string;
		QString euler_pole_x_string, euler_pole_y_string, euler_pole_z_string;
		QString euler_pole_lat_string, euler_pole_lon_string;
		QString angle_string;

		QLocale locale;

		plate_id_string.setNum(it->first);

		if (GPlatesMaths::represents_identity_rotation(uq)) 
		{
			switch (d_configuration->rotation_options.identity_rotation_format)
			{
			case ExportOptionsUtils::ExportRotationOptions::WRITE_IDENTITY_AS_INDETERMINATE:
				euler_pole_x_string = euler_pole_y_string = euler_pole_z_string = QObject::tr("Indeterminate");
				euler_pole_lat_string = euler_pole_lon_string = QObject::tr("Indeterminate");
				angle_string = QObject::tr("Indeterminate");
				break;

			case ExportOptionsUtils::ExportRotationOptions::WRITE_IDENTITY_AS_NORTH_POLE:
				euler_pole_x_string = locale.toString(0.0);
				euler_pole_y_string = locale.toString(0.0);
				euler_pole_z_string = locale.toString(1.0);
				euler_pole_lat_string = locale.toString(90.0);
				euler_pole_lon_string = locale.toString(0.0);
				angle_string = locale.toString(0.0);
				break;

			default:
				// Shouldn't get here.
				GPlatesGlobal::Abort(GPLATES_ASSERTION_SOURCE);
				break;
			}
		} 
		else 
		{
			using namespace GPlatesMaths;
			UnitQuaternion3D::RotationParams params = uq.get_rotation_params(fr.axis_hint());

			euler_pole_x_string = locale.toString(params.axis.x().dval());
			euler_pole_y_string = locale.toString(params.axis.y().dval());
			euler_pole_z_string = locale.toString(params.axis.z().dval());

			PointOnSphere euler_pole(params.axis);
			LatLonPoint llp = make_lat_lon_point(euler_pole);

			euler_pole_lat_string = locale.toString(llp.latitude());
			euler_pole_lon_string = locale.toString(llp.longitude());

			angle_string = locale.toString(
					GPlatesMaths::convert_rad_to_deg(params.angle).dval());
		}

		data_line.push_back(plate_id_string);

		// Write out the euler pole depending on the pole format requested.
		switch (d_configuration->rotation_options.euler_pole_format)
		{
		case ExportOptionsUtils::ExportRotationOptions::WRITE_EULER_POLE_AS_LATITUDE_LONGITUDE:
			data_line.push_back(euler_pole_lat_string);
			data_line.push_back(euler_pole_lon_string);
			break;

		case ExportOptionsUtils::ExportRotationOptions::WRITE_EULER_POLE_AS_CARTESIAN:
			data_line.push_back(euler_pole_x_string);
			data_line.push_back(euler_pole_y_string);
			data_line.push_back(euler_pole_z_string);
			break;

		default:
			// Shouldn't get here.
			GPlatesGlobal::Abort(GPLATES_ASSERTION_SOURCE);
			break;
		}

		data_line.push_back(angle_string);
		
		if (is_relative_rotation)
		{
			GPlatesModel::integer_plate_id_type fixed_id = it->second->get_fixed_plate();
			QString fixed_plate_id_string;
			fixed_plate_id_string.setNum(fixed_id);

			data_line.push_back(fixed_plate_id_string);
		}
		
		data.push_back(data_line);
		data_line.clear();
	}
	
	CsvExport::ExportOptions option;
	switch(d_configuration->rotation_type)
	{
	case Configuration::RELATIVE_COMMA:
	case Configuration::EQUIVALENT_COMMA:
		option.delimiter = ',';
		break;

	case Configuration::RELATIVE_SEMICOLON:
	case Configuration::EQUIVALENT_SEMICOLON:
		option.delimiter = ';';
		break;

	case Configuration::RELATIVE_TAB:
	case Configuration::EQUIVALENT_TAB:
	default:
		option.delimiter = '\t';
		break;
	}
		
	CsvExport::export_data(
			QDir(d_export_animation_context_ptr->target_dir()).absoluteFilePath(
					*filename_it),
			option,
			data);
	filename_it++;

	// Normal exit, all good, ask the Context process the next iteration please.
	return true;
}
