/*
	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"



mtWasp::Project::Project ()
{
	new_file ();
}

int mtWasp::Project::new_file ()
{
	m_file.new_file ();

	m_changes_not_saved = 0;
	m_filename.clear();

	return 0;
}

std::string mtWasp::Project::get_titlebar_text () const
{
	size_t	const	fullname_sep = m_filename.rfind ( MTKIT_DIR_SEP );
	std::string	txt;

	if ( m_filename.size() > 0 )
	{
		if ( fullname_sep == std::string::npos )
		{
			txt += m_filename;
		}
		else
		{
			txt += (m_filename.c_str() + fullname_sep + 1);
		}
	}
	else
	{
		txt += "Untitled";
	}

	if ( has_file_changed () )
	{
		txt += " (Modified)";
	}

	if ( fullname_sep != std::string::npos )
	{
		txt += " - ";
		txt += std::string ( m_filename, 0, fullname_sep );
	}

	txt += " - ";
	txt += VERSION;

	return txt;
}

char const * mtWasp::Project::get_wave_function () const
{
	ShapeFunction const * const func = m_file.get_shape_function ( 0 );

	if ( ! func )
	{
		return nullptr;
	}

	return func->function ();
}

int mtWasp::Project::get_wave_function_value (
	double	const	x,
	double		&result
	)
{
	mtDW::DoubleParser parser;
	auto & vars = parser.variables ();

	vars[ "x" ] = x / 180.0 * M_PI;

	int const err = parser.evaluate ( get_wave_function () );

	if ( err )
	{
		result = parser.error_pos ();
		return 1;
	}

	result = parser.result ().get_num ();

	return 0;
}

void mtWasp::Project::get_wave_volume_points (
	double	&p1x,
	double	&p1y,
	double	&p2x,
	double	&p2y
	) const
{
	ShapeSpline const * const spline = m_file.get_shape_spline ( 0 );

	if ( ! spline )
	{
		p1x = 0;
		p1y = 0;
		p2x = 0;
		p2y = 0;

		return;
	}

	std::vector<mtGin::Vertex> const & p = spline->points();

	p1x = p[0].x;
	p1y = p[0].y;
	p2x = p[1].x;
	p2y = p[1].y;
}



/// ----------------------------------------------------------------------------



int mtWasp::Project::export_wave_file (
	char	const * const	filename,
	double		const	secs,
	int		const	octave
	)
{
	if ( ! filename )
	{
		std::cerr << "mtWasp::Project::export_wave_file ERROR: "
			"filename is NULL\n";
		return 1;
	}

	if ( prepare_wave_audio ( secs, octave ) )
	{
		std::cerr << "mtWasp::Project::export_wave_file ERROR: "
			"Unable to build wave\n";
		return 1;
	}

	mtGin::AudioFileWrite	audio_file;

	if ( audio_file.open ( filename, m_audio_player.audiospec() ) )
	{
		std::cerr << "mtWasp::Project::export_wave_file ERROR: "
			"Unable to open file: '"
			<< filename
			<< "'\n";
		return 1;
	}

	if ( audio_file.write ( m_sample.wave_buf(), m_sample.wave_buf_samples()
		) )
	{
		std::cerr << "mtWasp::Project::export_wave_file ERROR: "
			"Unable to write data to file\n";
		return 1;
	}

	return 0;
}

int mtWasp::Project::set_wave_function ( char const * const function )
{
	if ( ! function )
	{
		return 1;
	}

	ShapeFunction * const func = m_file.get_shape_function ( 0 );
	if ( ! func )
	{
		return 1;
	}

	func->set_function ( function );

	settings_have_changed ();

	return 0;
}

int mtWasp::Project::set_wave_volume_points (
	double	const	p1x,
	double	const	p1y,
	double	const	p2x,
	double	const	p2y
	)
{
	if (	p1x < MTWASP_WAVE_VOLUME_X_MIN || p1x > MTWASP_WAVE_VOLUME_X_MAX
		|| p1y < MTWASP_WAVE_VOLUME_Y_MIN || p1y > MTWASP_WAVE_VOLUME_Y_MAX
		|| p2x < MTWASP_WAVE_VOLUME_X_MIN || p2x > MTWASP_WAVE_VOLUME_X_MAX
		|| p2y < MTWASP_WAVE_VOLUME_Y_MIN || p2y > MTWASP_WAVE_VOLUME_Y_MAX
		|| p1x > p2x
		)
	{
		return 1;
	}

	ShapeSpline * const spline = m_file.get_shape_spline ( 0 );
	if ( ! spline )
	{
		return 1;
	}

	spline->set_point ( 0, p1x, p1y );
	spline->set_point ( 1, p2x, p2y );

	settings_have_changed ();

	return 0;
}

int mtWasp::Project::prepare_wave_audio (
	double	const	secs,
	int	const	octave
	)
{
	if ( secs < MTWASP_WAVE_DURATION_MIN
		|| secs > MTWASP_WAVE_DURATION_MAX
		)
	{
		return 1;
	}

	if ( octave < MTWASP_NOTE_OCTAVE_MIN
		|| octave > MTWASP_NOTE_OCTAVE_MAX
		)
	{
		return 1;
	}

	// 1 + Hz * secs:
	size_t const samples = (size_t)(1 + MTWASP_AUDIOSPEC_HZ * secs );
	short * const buf = m_sample.get_wave_buf ( samples );

	if ( ! buf )
	{
		return 1;
	}

	for ( size_t i = 0; i < samples; i++ )
	{
		double const hz = get_wave_function_hz ( octave );
		double const xp = (double)i / ((double)samples - 1.0);	// X %
		double const secs_elapsed = xp * secs;
		double const cycles_elapsed = secs_elapsed * hz;
		double const x = 360.0 * fmod ( cycles_elapsed, 1.0 );

		double y;
		short res = 0;

		if ( get_wave_function_value ( x, y ) )
		{
			// Function failed, but no error needs to be reported,
			// just clear the buffer to create silence.
			memset ( buf, 0, samples * sizeof(*buf) );
			break;
		}
		else
		{
			double const volume = get_wave_volume_at ( xp );
			double const dres = mtkit_double_bound (
				y * volume * SHRT_MAX,
				SHRT_MIN, SHRT_MAX );

			res = (short)dres;
		}

		buf[i] = res;
	}

	return 0;
}

double mtWasp::Project::get_wave_volume_at ( double const x ) const
{
	ShapeSpline const * const spline = m_file.get_shape_spline ( 0 );

	return spline ? spline->get_y ( x ) : 0.0;
}

