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

#ifndef MTLANTER_BE_H_
#define MTLANTER_BE_H_

#include <mtgin.h>
#include <mtgin_gl.h>
#include <mtcelledit.h>



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



#ifdef __cplusplus
extern "C" {
#endif

// C API



#define PREFS_WINDOW_MAXIMIZED		"main.window_max"
#define PREFS_WINDOW_X			"main.window_x"
#define PREFS_WINDOW_Y			"main.window_y"
#define PREFS_WINDOW_W			"main.window_w"
#define PREFS_WINDOW_H			"main.window_h"



enum
{
	LANDSCAPE_VIEW_RANGE		= 1000
};

enum
{
	ANIM_FPS_MIN		= 10,
	ANIM_FPS_MAX		= 120,
	ANIM_FPS_DEFAULT	= 60,

	MAP_SIZE_MIN		= 4,		// rows/cols = 2^4 + 1	= 17
	MAP_SIZE_MAX		= 10,		// rows/cols = 2^10 + 1	= 1025
	DEFAULT_MAP_SIZE	= 6,		// (1 << 6)+1 = 65 rows
	DEFAULT_MAP_SEED	= 0,

	OUTPUT_WH_MIN		= 128,
	OUTPUT_WH_MAX		= 15360,
	OUTPUT_WIDTH_DEFAULT	= 500,
	OUTPUT_HEIGHT_DEFAULT	= 500
};



#ifdef __cplusplus
}

// C++ API

class CommandLine;
class MemPrefs;
class TerraAnimate;
class TerraCamera;
class TerraMap;
class TerraScene;



float constexpr	m_gl_range = LANDSCAPE_VIEW_RANGE * 10;
float constexpr m_landscape_color_red = 0.2f;
float constexpr m_landscape_color_green = 0.5f;
float constexpr m_landscape_color_blue = 0.4f;



class MemPrefs
{
public:
	int	window_x		= 0;
	int	window_y		= 0;
	int	window_w		= 0;
	int	window_h		= 0;
	int	window_maximized	= 0;

	mtKit::UPrefUIEdit		ui_editor;
};



class CommandLine
{
public:
	int parse ( int argc, char const * const * argv );
		// 0 = Continue running (start UI)
		// 1 = Terminate program, returning exit.value()

	char const * get_anim_dir () const { return m_anim_dir; }
	char const * get_anim_tsv () const { return m_anim_tsv; }
	int get_anim_fps () const	{ return m_anim_fps; }
	int get_map_size () const	{ return m_map_size; }
	int get_map_seed () const	{ return m_map_seed; }
	int get_output_width () const	{ return m_output_width; }
	int get_output_height () const	{ return m_output_height; }
	char const * get_save_map () const { return m_save_map; }
	int get_verbose () const	{ return m_verbose; }

	// Accessors

	MemPrefs const & get_memprefs () const	{ return m_memprefs; }
	mtKit::Exit & exit ()			{ return m_exit; }
	mtKit::UserPrefs & userprefs ()		{ return m_userprefs; }

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

private:
	void prefs_init ( char const * filename );

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

	mtKit::Exit		m_exit;

	// uprefs must be destroyed before mprefs, so it appears below it:
	MemPrefs		m_memprefs;
	mtKit::UserPrefs	m_userprefs;

	char	const *	m_anim_dir		= nullptr;
	int		m_anim_fps		= ANIM_FPS_DEFAULT;
	char	const *	m_anim_tsv		= nullptr;
	int		m_map_size		= DEFAULT_MAP_SIZE;
	int		m_map_seed		= DEFAULT_MAP_SEED;
	int		m_output_width		= 0;
	int		m_output_height		= 0;
	char	const *	m_save_map		= nullptr;
	int		m_verbose		= 0;
};



typedef std::function<int (std::string const & filename)> t_Image_Export;



class TerraAnimate
{
public:
	explicit TerraAnimate ( CommandLine & cline );

	int init ();

	int prepare_frame ( int frame );
	int save_frame ( int frame, t_Image_Export func_image_export ) const;

	int get_frame_total () const { return m_frame_total; }
	mtGin::Vertex const & get_position () const { return m_view_position; }
	mtGin::Vertex const & get_focus () const { return m_view_focus; }

private:
	void clear ();

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

	CommandLine		& m_cline;

	std::string		m_output_dir;
	int			m_fps		= ANIM_FPS_DEFAULT;
	int			m_frame_total	= 0;

	mtGin::BezierPath	m_camera_path;
	mtGin::BezierPath	m_focus_path;
	std::vector<double>	m_time_path;

	mtGin::Vertex		m_view_position;
	mtGin::Vertex		m_view_focus;
};



class TerraMap
{
public:
	TerraMap ();
	~TerraMap ();

	void set_seed ( uint64_t const seed )
	{
		m_random.set_seed ( seed );
	}

	void set_value( int const x, int const y, int const value ) const
	{
		m_mem[ x + m_rows * y ] = value;
	}

	int get_rows () const { return m_rows; }

	int get_value ( int x, int y ) const
	{
		return m_mem[ x + m_rows * y ];
	}

	float get_value_real ( int x, int y ) const
	{
		return float( get_value(x,y) ) / float(m_rows) * m_scale / 3;
	}

	float get_xy_real ( int xy ) const
	{
		return m_scale * ( float(xy) / float(m_rows-1) ) - m_scale2;
	}

	int create_landscape ( int size );
	int save_tsv ( char const * filename ) const;

private:
	int set_size ( int size );
	void set_mem ( int * mem, int rows );
	int get_random_value ( int const gap)
	{
		return (m_random.get_int ( gap + 1 ) - gap/2);
	}

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

	mtKit::Random	m_random;

	int		* m_mem		= nullptr;
	int		m_rows		= 0;

	static float constexpr m_scale	= float(LANDSCAPE_VIEW_RANGE);
	static float constexpr m_scale2	= float(LANDSCAPE_VIEW_RANGE) / 2;

	MTKIT_RULE_OF_FIVE( TerraMap )
};



class TerraCamera : public mtGin::GL::Camera
{
public:
	static double constexpr NUDGE_MIN = 0.1;
	static double constexpr NUDGE_MAX = 10.0;
	static double constexpr NUDGE_DEFAULT = 1.0;

	double get_nudge() const { return m_nudge; }
	void set_nudge ( double nudge )
	{
		m_nudge = mtkit_double_bound ( nudge, NUDGE_MIN, NUDGE_MAX );
	}

private:
	double		m_nudge = NUDGE_DEFAULT;
};



class TerraScene
{
public:
	TerraScene ();

	void init_buffers ( TerraMap const & map );

	void render ( TerraCamera const & camera ) const;

	enum
	{
		MODE_MIN,

		MODE_TRIANGLES		= MODE_MIN,
		MODE_WIREFRAME,
		MODE_POINTS,
		MODE_TRIANGLES_WITH_WIREFRAME,

		MODE_MAX
	};

	void next_mode ()
	{
		m_mode ++;

		if ( m_mode < MODE_MAX )
		{
			return;
		}

		m_mode = MODE_MIN;
	}

private:
	void render_light () const;

	static void render_pyramid (
		GLfloat x,
		GLfloat y,
		GLfloat z,
		GLfloat size
		);
	static void render_axis ();

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

	int			m_mode = MODE_TRIANGLES;
	int			m_buffer_ready = 0;

	mtGin::Vertex	const	m_light;
	mtGin::GL::Lines	m_gl_lines;
	mtGin::GL::Lines	m_gl_lines_bez;

	mtGin::GL::Triangles	m_gl_land_triangles;
	mtGin::GL::Lines	m_gl_land_wireframe;
	mtGin::GL::Points	m_gl_land_points;
};



#endif		// C++ API



#endif		// MTLANTER_BE_H_

