/*
	Copyright (C) 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 "ginbench_be.h"



int TestSuite::init ( CommandLine const * const cline )
{
	char buf[128];

	if ( mtkit_time_get_string ( "%F %T", buf, sizeof(buf) ) )
	{
		buf[0] = 0;
	}

	m_cline = cline;
	CedSheet * const sheet = ced_sheet_new ();
	m_sheet_results.reset ( sheet );
	if ( ! sheet )
	{
		return 1;
	}

	m_results_row = 1;
	m_results_column = 1;

	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		"Started" );
	ced_sheet_set_cell_text( sheet, m_results_row, m_results_column++, buf);

	m_results_row++;
	m_results_column = 1;

	return 0;
}

int TestSuite::finish ()
{
	char timenow[128];

	if ( mtkit_time_get_string ( "%F %T", timenow, sizeof(timenow) ) )
	{
		timenow[0] = 0;
	}

	m_results_row++;
	m_results_column = 1;

	CedSheet * const sheet = m_sheet_results.get();

	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		"Ended" );
	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		timenow );
	m_results_row++;
	m_results_column = 1;

	double const secs = m_clock_whole.seconds ();
	int const mins = (int)(secs / 60);
	int const hours = mins / 60;
	snprintf ( timenow, sizeof(timenow), "%i:%02i:%09.6f", hours, mins,
		fmod(secs, 100.0) );
	printf("Time taken for whole process = %s\n", timenow );

	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		"Duration" );
	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		timenow );
	m_results_row++;
	m_results_column = 1;

	char const * const filename = m_cline->get_output_sheet();

	if ( filename )
	{
		if (ced_sheet_save( sheet, filename, CED_FILE_TYPE_TSV_VALUE))
		{
			std::cerr << "Error saving TSV file to - " << filename
				<< "\n";
		}
	}

	if ( m_cline->get_verbose() )
	{
		mtFile * file = ced_sheet_save_mem ( sheet,
			CED_FILE_TYPE_TSV_CONTENT );

		if ( file )
		{
			mtkit_file_write ( file, "\0", 1 );

			void * buf;
			mtkit_file_get_mem ( file, &buf, NULL );

			puts ( (char *)buf );

			mtkit_file_close ( file );
		}
	}

	char const * const dir = m_cline->get_output_dir();
	if ( dir )
	{
		std::string dirs ( dir );
		dirs += "/";

		auto save_files = [](
			std::string	const & d,
			char	const * const prefix,
			PixmapVector	const & pv
			)
		{
			char buf[128];

			for ( size_t i=0; i < pv.size(); i++ )
			{
				mtPixmap const * const pm = pv[i].get();
				std::string fn ( d );
				int const w = pixy_pixmap_get_width ( pm );
				int const h = pixy_pixmap_get_height ( pm );

				snprintf( buf, sizeof(buf), "%02i_%ix%i.png",
					(int)i, w, h );
				fn += prefix;
				fn += buf;

				if ( pixy_pixmap_save_png ( pm, fn.c_str(), 5 ))
				{
					std::cerr << "Unable to save file: '"
						<< fn << "'\n";
				}
			}
		};

		save_files ( dirs, "c_idx_", m_canvases_idx );
		save_files ( dirs, "c_rgb_", m_canvases_rgb );
		save_files ( dirs, "s_idx_", m_sprites_idx );
		save_files ( dirs, "s_rgb_", m_sprites_rgb );
	}

	return 0;
}

void TestSuite::list_units () const
{
	for ( auto const & pair : m_test_map )
	{
		printf("%-10s - %s\n",
			pair.first.c_str(),
			pair.second.description.c_str() );
	}
}

int TestSuite::load_patterns ( char const * const filename )
{
	if ( m_brush.load_patterns ( filename ) )
	{
		fprintf ( stderr, "ERROR: Pattern file '%s' invalid.\n",
			filename );
		return 1;
	}

	return 0;
}

int TestSuite::load_shapes ( char const * const filename )
{
	if ( m_brush.load_shapes ( filename ) )
	{
		fprintf ( stderr, "ERROR: Shape file '%s' invalid.\n",
			filename );
		return 1;
	}

	return 0;
}

void TestSuite::prep_tests ()
{
	if ( m_canvases_idx.size() > 0 )
	{
		// Already prepped
		return;
	}

	mtKit::Clock clock;

	m_random.set_seed(0);

	prep_sprites ();

	m_canvases_idx.clear();
	m_canvases_rgb.clear();

	m_canvases_idx.emplace_back ( pixy_pixmap_new_indexed ( 160, 90 ) );
	m_canvases_idx.emplace_back ( pixy_pixmap_new_indexed ( 320, 180 ) );
	m_canvases_idx.emplace_back ( pixy_pixmap_new_indexed ( 640, 360 ) );
	m_canvases_idx.emplace_back ( pixy_pixmap_new_indexed ( 1280, 720 ) );
	m_canvases_idx.emplace_back ( pixy_pixmap_new_indexed ( 1920, 1080 ) );

	for ( size_t i=0; i < m_canvases_idx.size(); i++ )
	{
		mtPixmap * pm = m_canvases_idx[i].get();
		if ( ! pm )
		{
			std::cerr << "Unable to set up IDX canvas: " <<i<< "\n";
			throw 123;
		}

		prep_palette ( pm );

		m_canvases_rgb.emplace_back ( pixy_pixmap_convert_to_rgb( pm ));

		if ( ! m_canvases_rgb[i] )
		{
			std::cerr << "Unable to set up RGB canvas: " <<i<< "\n";
			throw 123;
		}
	}

	CedSheet * const sheet = m_sheet_results.get();

	ced_sheet_set_cell_text ( sheet, m_results_row+1, m_results_column++,
		"Iterations" );
	ced_sheet_set_cell_text ( sheet, m_results_row+1, m_results_column++,
		"Test" );
	ced_sheet_set_cell_text ( sheet, m_results_row+1, m_results_column++,
		"Measure" );

	auto print_titles = [this, sheet]( PixmapVector const & vec,
		char const * const title )
		{
			char buf[128];

			for ( size_t i=0; i < vec.size(); i++ )
			{
				mtPixmap * pm = vec[i].get();
				int const w = pixy_pixmap_get_width ( pm );
				int const h = pixy_pixmap_get_height ( pm );
				snprintf( buf, sizeof(buf), "%i x %i", w, h );

				ced_sheet_set_cell_text ( sheet, m_results_row,
					m_results_column, title );
				ced_sheet_set_cell_text ( sheet, m_results_row+1,
					m_results_column++, buf );
			}
		};

	print_titles ( m_canvases_idx, "Indexed" );
	print_titles ( m_canvases_rgb, "RGB" );

	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column,
		"Canvas type" );
	ced_sheet_set_cell_text ( sheet, m_results_row+1, m_results_column,
		"Canvas size" );

	m_results_row ++;
	m_results_column = 1;

	double const secs = clock.seconds ();
	int const mins = (int)(secs / 60);
	int const hours = mins / 60;
	printf("Time taken for prep_tests = %i:%02i:%09.6f\n", hours, mins,
		fmod(secs, 100.0) );
}

void TestSuite::prep_palette ( mtPixmap * const pixmap )
{
	pixy_palette_set_uniform_balanced ( pixy_pixmap_get_palette ( pixmap ),
		PIXY_PALETTE_UNIFORM_MAX );
}

void TestSuite::prep_sprites ()
{
	int const cw = 1920;
	int const ch = 1080;
	int const coltot = 224;

	m_sprites_idx.clear();
	m_sprites_idx.emplace_back ( pixy_pixmap_new_indexed ( cw, ch ) );

	mtPixmap	* pm = m_sprites_idx[0].get();
	mtPalette	* pal = pixy_pixmap_get_palette ( pm );
	mtColor	const	* cols = pal->color;

	prep_palette ( pm );

	for ( int i=0; i<50000; i++ )
	{
		unsigned char a, b;
		int x1, y1;
		int dx, dy, dw, dh;

		a = (unsigned char)(1 + m_random.get_int ( coltot - 1 ));
		b = 0;

		m_brush.set_shape ( m_random.get_int (64) );
		m_brush.set_pattern ( m_random.get_int (100) );
		m_brush.set_color_ab ( a, b, cols );

		x1 = m_random.get_int ( cw );
		y1 = m_random.get_int ( ch );
		m_brush.paint_brush ( pm, x1, y1, x1, y1, dx, dy, dw, dh );
	}

	{
		// Create the sprites from the main image
		int x = 50;
		int const y = 50;

		for ( int size : { 10, 20, 40, 80, 160 } )
		{
			mtPixmap * s = pixy_pixmap_resize(pm, x, y, size, size);

			m_sprites_idx.emplace_back ( s );

			s = pixy_pixmap_duplicate ( s );
			pixy_pixmap_create_alpha_from_index ( s, 0 );
			m_sprites_idx.emplace_back ( s );

			x += size;
		}
	}

	m_sprites_rgb.clear();

	// Create all RGB sprites from IDX, checking everything as we go through
	for ( size_t i=0; i < m_sprites_idx.size(); i++ )
	{
		pm = m_sprites_idx[i].get();
		if ( ! pm )
		{
			std::cerr << "Unable to set up IDX sprites: " <<i<<"\n";
			throw 123;
		}

		prep_palette ( pm );

		m_sprites_rgb.emplace_back ( pixy_pixmap_convert_to_rgb ( pm ));

		if ( ! m_sprites_rgb[i] )
		{
			std::cerr << "Unable to set up RGB sprites: " <<i<<"\n";
			throw 123;
		}
	}
}

void TestSuite::run_test_unit ( char const * const name )
{
	std::string const test_name ( nullptr == name ? "all" : name );
	auto it = m_test_map.find ( test_name );

	if ( it != m_test_map.end() )
	{
		TestInfo const & info = it->second;

		prep_tests ();

		if ( "all" != test_name )
		{
			print_next_test_name ( test_name.c_str(),
				it->second.measure.c_str() );
		}

		info.func();
	}
	else
	{
		std::cerr << "Error!  No such test unit named '" << test_name
			<< "'\n";
		throw 123;
	}
}

void TestSuite::print_next_test_name (
	char const * const name,
	char const * const unit
	)
{
	CedSheet * const sheet = m_sheet_results.get();

	m_results_row++;
	m_results_column = 1;

	char buf[128];
	snprintf ( buf, sizeof(buf), "%i", m_cline->get_test_total() );
	ced_sheet_set_cell ( sheet, m_results_row, m_results_column++,
		buf );

	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		name );

	ced_sheet_set_cell_text ( sheet, m_results_row, m_results_column++,
		unit );
}

void TestSuite::print_per_sec ( double const secs )
{
	CedSheet * const sheet = m_sheet_results.get();
	char buf[128];
	double const ps = m_cline->get_test_total() / secs;

	snprintf( buf, sizeof(buf), CED_PRINTF_NUM, ps );
	ced_sheet_set_cell ( sheet, m_results_row, m_results_column++, buf );
}

void TestSuite::print_percent (
	double	const	secs_total,
	double	const	secs_used
	)
{
	CedSheet * const sheet = m_sheet_results.get();
	char buf[128];
	double const ps = 100.0 * secs_used / secs_total;

	snprintf( buf, sizeof(buf), CED_PRINTF_NUM, ps );
	ced_sheet_set_cell ( sheet, m_results_row, m_results_column++, buf );
}

