/*
	Copyright (C) 2024-2025 Mark Tyler

	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 3 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 in the file COPYING.
*/

#include <mtdatawell_math.h>

#include "mtwasp.h"



#define MTWASP_FILE_VERSION	2
#define ELNA_MTWASP		"mtWasp"
#define ELNA_SHAPE		"Shape"
#define ELNA_FUNCTION		"Function"
#define	ELNA_ITEM		"Item"
#define ELNA_SPLINE		"Spline"
#define ELNA_POINT		"Point"
#define ELNA_WAVE		"Wave"
#define ATNA_VERSION		"version"
#define ATNA_NAME		"name"
#define ATNA_X			"x"
#define ATNA_Y			"y"
#define ATNA_FUNCTION		"function"
#define ATNA_SPLINE		"spline"
#define ATNA_FILENAME		"filename"



int mtWasp::File::new_file ()
{
	clear_file ();

	add_shape_function ( "Sine wave", "sin(x)" );

	ShapeSpline & spline = add_shape_spline ( "Volume envelope" );
	spline.add_point ( MTWASP_WAVE_VOLUME_SINGLE_STEP,
		MTWASP_WAVE_VOLUME_Y_MAX );
	spline.add_point ( MTWASP_WAVE_VOLUME_X_MAX -
		MTWASP_WAVE_VOLUME_SINGLE_STEP, MTWASP_WAVE_VOLUME_Y_MAX );

	return 0;
}

int mtWasp::File::clear_file ()
{
	m_shape_funcs.clear();
	m_shape_splines.clear();

	return 0;
}

mtWasp::ShapeFunction &mtWasp::File::add_shape_function (
	char	const	* name,
	char	const	* function
	)
{
	m_shape_funcs.emplace_back ();

	ShapeFunction & s = m_shape_funcs[ m_shape_funcs.size() - 1 ];
	s.set_name ( name );
	s.set_function ( function );

	return s;
}

mtWasp::ShapeSpline &mtWasp::File::add_shape_spline( char const * name )
{
	m_shape_splines.emplace_back ();

	ShapeSpline & s = m_shape_splines[ m_shape_splines.size() - 1 ];
	s.set_name ( name );

	return s;
}

int mtWasp::File::load_file ( char const * const filename )
{
	if ( ! filename )
	{
		std::cerr << "mtWasp::File::load_file ERROR: "
			"filename is NULL\n";
		return 1;
	}

	mtKit::Utree file;
	mtUtreeNode * const root = file.load ( filename );

	if ( ! root )
	{
		std::cerr << "mtWasp::File::load_file ERROR: "
			"unable to load file '" << filename << "'\n";
		return 1;
	}

	mtUtreeNode * node;
	int version;

	if (	!( node = mtkit_utree_get_node ( root, ELNA_MTWASP, 0 ))
		|| mtkit_utree_get_attribute_int ( node, ATNA_VERSION, &version)
		)
	{
		std::cerr << "mtWasp::File::load_file ERROR: "
			"unable to load Utree data from file\n";
		return 1;
	}

	clear_file ();

	for ( node = node->child; node; node = node->next )
	{
		if ( node->type != MTKIT_UTREE_NODE_TYPE_ELEMENT )
		{
			continue;
		}

		if ( 0 == strcmp ( node->text, ELNA_SHAPE ) )
		{
			load_shape ( node );
		}
		else if ( 0 == strcmp ( node->text, ELNA_WAVE ) )
		{
			load_wave ( node );
		}
		else
		{
			std::cerr << "mtWasp::File::load_file: unknown node "
				" '" << node->text << "'\n";
		}
	}

	// Verify file contains at least 1 spline and 1 function
	if ( m_shape_funcs.size() < 1 || m_shape_splines.size() < 1 )
	{
		std::cerr << "File doesn't contain enough functions/splines\n";
		new_file();
	}

	return 0;
}

int mtWasp::File::load_shape ( mtUtreeNode * node )
{
	for ( node = node->child; node; node = node->next )
	{
		if ( node->type != MTKIT_UTREE_NODE_TYPE_ELEMENT )
		{
			continue;
		}

		if ( 0 == strcmp ( node->text, ELNA_FUNCTION ) )
		{
			load_function ( node );
		}
		else if ( 0 == strcmp ( node->text, ELNA_SPLINE ) )
		{
			load_spline ( node );
		}
		else
		{
			std::cerr << "mtWasp::File::load_shape: unknown node "
				" '" << node->text << "'\n";
		}
	}

	return 0;
}

int mtWasp::File::load_function ( mtUtreeNode * node )
{
	for ( node = node->child; node; node = node->next )
	{
		if ( node->type != MTKIT_UTREE_NODE_TYPE_ELEMENT )
		{
			continue;
		}

		if ( 0 == strcmp ( node->text, ELNA_ITEM ) )
		{
			load_function_item ( node );
		}
		else
		{
			std::cerr << "mtWasp::File::load_function: unknown node "
				" '" << node->text << "'\n";
		}
	}

	return 0;
}

int mtWasp::File::load_function_item ( mtUtreeNode * node )
{
	char const * name = nullptr;
	char const * function = nullptr;

	mtkit_utree_get_attribute_string ( node, ATNA_NAME, &name );
	mtkit_utree_get_attribute_string ( node, ATNA_FUNCTION, &function );

	if ( function && name )
	{
		m_shape_funcs.emplace_back ();

		ShapeFunction & s = m_shape_funcs[ m_shape_funcs.size() - 1 ];
		s.set_name ( name );
		s.set_function ( function );

		return 0;
	}

	std::cerr << "mtWasp::File::load_function_item failed: missing "
		"attribute\n";

	return 1;
}

int mtWasp::File::load_spline ( mtUtreeNode * node )
{
	for ( node = node->child; node; node = node->next )
	{
		if ( node->type != MTKIT_UTREE_NODE_TYPE_ELEMENT )
		{
			continue;
		}

		if ( 0 == strcmp ( node->text, ELNA_ITEM ) )
		{
			load_spline_item ( node );
		}
		else
		{
			std::cerr << "mtWasp::File::load_spline: unknown node "
				" '" << node->text << "'\n";
		}
	}

	return 0;
}

int mtWasp::File::load_spline_item ( mtUtreeNode * node )
{
	char const * name = nullptr;

	mtkit_utree_get_attribute_string ( node, ATNA_NAME, &name );

	if ( name )
	{
		m_shape_splines.emplace_back ();

		ShapeSpline & s = m_shape_splines[ m_shape_splines.size() - 1 ];
		s.set_name ( name );

		for ( node = node->child; node; node = node->next )
		{
			if ( node->type != MTKIT_UTREE_NODE_TYPE_ELEMENT
				|| strcmp ( node->text, ELNA_POINT )
				)
			{
				continue;
			}

			load_spline_point ( node, s );
		}

		return 0;
	}

	return 1;
}

int mtWasp::File::load_spline_point (
	mtUtreeNode	* const	node,
	ShapeSpline		& spline
	)
{
	double x = INFINITY;
	double y = INFINITY;

	mtkit_utree_get_attribute_double ( node, ATNA_X, &x );
	mtkit_utree_get_attribute_double ( node, ATNA_Y, &y );

	if ( isinf(x) || isinf(y) )
	{
		return 1;
	}

	spline.add_point ( x, y );

	return 0;
}

int mtWasp::File::load_wave ( mtUtreeNode * ARG_UNUSED(node) )
{
	return 0;
}

int mtWasp::File::save_file ( char const * const filename )
{
	if ( ! filename )
	{
		std::cerr << "mtWasp::File::save_file ERROR: "
			"filename is NULL\n";
		return 1;
	}

	mtKit::Utree file;
	mtUtreeNode * root, * node1, * node2, * node3, * node4, * node5;

	if (	!( root = file.init() )
		|| !( node1 = mtkit_utree_new_element ( root, ELNA_MTWASP ) )
		|| mtkit_utree_set_attribute_int ( node1, ATNA_VERSION,
			MTWASP_FILE_VERSION )
		|| !( node2 = mtkit_utree_new_element ( node1, ELNA_SHAPE ) )
		|| !( node3 = mtkit_utree_new_element ( node2, ELNA_FUNCTION ) )
		)
	{
		std::cerr << "mtWasp::File::save_file ERROR: "
			"unable to populate Utree 1\n";
		return 1;
	}

	for ( size_t i = 0; i < m_shape_funcs.size(); i++ )
	{
		ShapeFunction const & sf = m_shape_funcs[ i ];

		if ( !( node4 = mtkit_utree_new_element ( node3, ELNA_ITEM ) )
			|| mtkit_utree_set_attribute_string ( node4, ATNA_NAME,
				sf.name() )
			|| mtkit_utree_set_attribute_string ( node4, ATNA_FUNCTION,
				sf.function() )
			)
		{
			std::cerr << "mtWasp::File::save_file ERROR: "
				"unable to populate Utree Function item\n";
			return 1;
		}
	}

	if ( !( node3 = mtkit_utree_new_element ( node2, ELNA_SPLINE ) ) )
	{
		std::cerr << "mtWasp::File::save_file ERROR: "
			"unable to populate Utree Spline\n";
		return 1;
	}

	for ( size_t i = 0; i < m_shape_splines.size(); i++ )
	{
		ShapeSpline const & sf = m_shape_splines[ i ];

		if ( !( node4 = mtkit_utree_new_element ( node3, ELNA_ITEM ) )
			|| mtkit_utree_set_attribute_string ( node4, ATNA_NAME,
				sf.name() )
			)
		{
			std::cerr << "mtWasp::File::save_file ERROR: "
				"unable to populate Utree Spline item\n";
			return 1;
		}

		std::vector<mtGin::Vertex> const & pts = sf.points();

		for ( size_t pi = 0; pi < pts.size(); pi++ )
		{
			mtGin::Vertex const & p = pts[ pi ];

			if ( !( node5 = mtkit_utree_new_element ( node4,
					ELNA_POINT ) )
				|| mtkit_utree_set_attribute_double ( node5,
					ATNA_X, p.x )
				|| mtkit_utree_set_attribute_double ( node5,
					ATNA_Y, p.y )
				)
			{
				std::cerr << "mtWasp::File::save_file ERROR: "
					"unable to populate Utree Spline point\n";
				return 1;
			}
		}
	}

	if ( file.save ( filename, MTKIT_UTREE_OUTPUT_ALL ) )
	{
		std::cerr << "mtWasp::File::save_file ERROR: "
			"unable to save file '" << filename << "'\n";
		return 1;
	}

	return 0;
}

