/*
	Copyright (C) 2024-2026 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.
*/

#ifndef MTWASP_H_
#define MTWASP_H_



#include <mtdatawell_math.h>
#include <mtgin.h>
#include <mtgin_sdl.h>



// Functions return: 0 = success, NULL = fail; unless otherwise stated.



#ifdef __cplusplus
extern "C" {
#endif

// C API

#define MTWASP_WAVE_DURATION_MIN		0.1
#define MTWASP_WAVE_DURATION_DEFAULT		1.0
#define MTWASP_WAVE_DURATION_MAX		100.0

#define MTWASP_WAVE_VOLUME_X_MIN		0.0
#define MTWASP_WAVE_VOLUME_X_MAX		1.0
#define MTWASP_WAVE_VOLUME_Y_MIN		0.0
#define MTWASP_WAVE_VOLUME_Y_MAX		1.0
#define MTWASP_WAVE_VOLUME_SINGLE_STEP		0.1

enum
{
	// Audiospec
	MTWASP_AUDIOSPEC_HZ		= 48000,
	MTWASP_AUDIOSPEC_KHZ		= 48,		// Output KHz
	MTWASP_AUDIOSPEC_CHANNELS	= 1,		// Mono
	MTWASP_AUDIOSPEC_SAMPLES	= 4096,		// For SDL

	// Wave creation model
	MTWASP_NOTE_OCTAVE_MIN		= -4,
	MTWASP_NOTE_OCTAVE_DEFAULT	= 4,		// Middle C
	MTWASP_NOTE_OCTAVE_MAX		= 12,

	// Player
	MTWASP_AUDIO_DEVICE_DEFAULT	= -1,
};



#ifdef __cplusplus
}

// C++ API

namespace mtWasp
{

class File;
class Player;
class Project;
class Sample;
class ShapeFunction;
class ShapeSpline;



class Sample
{
public:
	Sample () = default;
	~Sample ()
	{
		set_wave_buf ( nullptr, 0 );
	}

	void set_wave_buf (
		short	* const	buf,
		size_t	const	samples
		);

	short * get_wave_buf ( size_t samples );
		// Get buffer of this size (if forced to allocate, NULL = fail)

	short const * wave_buf() const { return m_wave_buf; }
	size_t wave_buf_samples() const { return m_wave_buf_samples; }

private:
	short		* m_wave_buf = nullptr;
	size_t		m_wave_buf_samples = 0;

	// Raw pointers used
	MTKIT_RULE_OF_FIVE ( Sample )
};



class Player
{
public:
	Player ();

	void set_audio_device ( int const device )
	{
		m_audio_device = device;
		m_audio_device_ready = 0;
	}

	SDL_AudioSpec const & audiospec () const
	{
		return m_audiospec;
	}

	int play_audio ( Sample const & sample );
	int stop_audio ();

private:
	int init_audio_device ();

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

	SDL_AudioSpec	m_audiospec;
	mtGin::AudioPlay m_audio_player;
	int		m_audio_device_ready;
	int		m_audio_device;
};



class ShapeFunction
{
public:
	char const * name () const		{ return m_name.c_str(); }
	char const * function () const		{ return m_function.c_str(); }

	void set_name ( char const * s )	{ m_name = s; }
	void set_function ( char const * s )	{ m_function = s; }

private:
	std::string	m_name;
	std::string	m_function;
};



class ShapeSpline
{
public:
	ShapeSpline ()
		:
		m_point_start	( MIN_X(), 0.0, 0.0 ),
		m_point_end	( MAX_X(), 0.0, 0.0 )
	{
	}

	enum
	{
		MAX_POINTS		= 10
	};

	static constexpr double MIN_X () { return 0.0; }
	static constexpr double MAX_X () { return 1.0; }
	static constexpr double MIN_Y () { return -1.0; }
	static constexpr double MAX_Y () { return 1.0; }

	char const * name () const { return m_name.c_str(); }
	double get_y ( double x ) const;
	std::vector<mtGin::Vertex> const & points () const { return m_points; }

	void normalize_xy ( double & x, double & y )
	{
		x = mtkit_double_bound( x, MIN_X(), MAX_X() );
		y = mtkit_double_bound( y, MIN_Y(), MAX_Y() );
	}

	void set_name ( char const * s ) { m_name = s; }

	void add_point ( double x, double y );
	void set_point ( int index, double x, double y );

	void insert_point ( int index, double x, double y );
	void delete_point ( int index );

private:
	std::string			m_name;
	std::vector<mtGin::Vertex>	m_points;
	mtGin::Vertex		const	m_point_start;
	mtGin::Vertex		const	m_point_end;
};



class File
{
public:
	int new_file ();		// Sets up default
	int clear_file ();		// Empty everything
	int load_file ( char const * filename );
	int save_file ( char const * filename );

	ShapeFunction & add_shape_function (
		char const * name,
		char const * function
		);
	ShapeSpline & add_shape_spline ( char const * name );

	ShapeFunction * get_shape_function ( size_t const i )
	{
		return (m_shape_funcs.size() > i) ? &m_shape_funcs[i] : nullptr;
	}

	ShapeSpline * get_shape_spline ( size_t const i )
	{
		return (m_shape_splines.size() > i) ? &m_shape_splines[i] : nullptr;
	}

	ShapeFunction const * get_shape_function ( size_t const i ) const
	{
		return (m_shape_funcs.size() > i) ? &m_shape_funcs[i] : nullptr;
	}

	ShapeSpline const * get_shape_spline ( size_t const i ) const
	{
		return (m_shape_splines.size() > i) ? &m_shape_splines[i] : nullptr;
	}

private:
	int load_shape ( mtUtreeNode * node );
	int load_function ( mtUtreeNode * node );
	int load_function_item ( mtUtreeNode * node );
	int load_spline ( mtUtreeNode * node );
	int load_spline_item ( mtUtreeNode * node );
	int load_spline_point ( mtUtreeNode * node, ShapeSpline & spline );

	int load_wave ( mtUtreeNode * node );

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

	std::vector<ShapeFunction> m_shape_funcs;
	std::vector<ShapeSpline> m_shape_splines;
};



class Project
{
public:
	Project ();

/// Actions --------------------------------------------------------------------

	int new_file ();

	int load_file ( char const * const filename )
	{
		if ( m_file.load_file ( filename ) )
		{
			return 1;
		}

		m_changes_not_saved = 0;

		if ( filename != m_filename.c_str() )
		{
			m_filename = filename;
		}

		return 0;
	}

	int save_file ( char const * const filename )
	{
		if ( m_file.save_file ( filename ) )
		{
			return 1;
		}

		m_changes_not_saved = 0;

		if ( filename != m_filename.c_str() )
		{
			m_filename = filename;
		}

		return 0;
	}

	int export_wave_file ( char const * filename, double secs, int octave );

	void set_audio_device ( int const i )
	{
		m_audio_player.set_audio_device ( i );
	}

	int play_audio ()
	{
		return m_audio_player.play_audio ( m_sample );
	}

	int stop_audio ()
	{
		return m_audio_player.stop_audio ();
	}

/// Setters --------------------------------------------------------------------

	int set_wave_function ( char const * function );
		// e.g. "sin(x)" where x = [0.0 - 360.0]

	int set_wave_volume_points (
		double p1x,		// 0.0 -> 1.0
		double p1y,		// 0.0 -> 1.0
		double p2x,		// 0.0 -> 1.0
		double p2y		// 0.0 -> 1.0
		);

/// Getters --------------------------------------------------------------------

	int has_file_changed () const
	{
		return m_changes_not_saved;
	}

	std::string get_titlebar_text () const;

	char const * get_filename () const
	{
		return m_filename.c_str();
	}

	char const * get_wave_function () const;
	int get_wave_function_value (
		double x,		// x = 0.0 -> 360.0
		double &result		// On failure gives text position
		);
	static double get_wave_function_hz ( int const octave )
		// Function cycles per second
	{
		return 440.0 / ( ::pow(2.0, (9.0/12.0)) ) *
			( ::pow(2.0, (octave - 4)) );
	}

	void get_wave_volume_points (
		double &p1x,	// 0.0 -> 1.0
		double &p1y,	// 0.0 -> 1.0
		double &p2x,	// 0.0 -> 1.0
		double &p2y	// 0.0 -> 1.0
		) const;

	double get_wave_volume_at ( double const x ) const;
		// Input: 0.0 to 1.0, Output: 0.0 to 1.0

	int prepare_wave_audio ( double secs, int octave );

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

private:
	void settings_have_changed ()
	{
		m_changes_not_saved = 1;
	}

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

	File		m_file;
	Player		m_audio_player;
	Sample		m_sample;

	int		m_changes_not_saved;
	std::string	m_filename;
};



} // mtWasp



#endif		// C++ API



#endif		// MTWASP_H_

