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

#include "pizor_main.h"



int main (
	int			const	argc,
	char	const * const * const	argv
	)
{
	try
	{
		CommandLine	cline;

		if ( cline.parse ( argc, argv ) )
		{
			return cline.exit().value();
		}

		Mainwindow	window ( cline );

		return window.start();
	}
	catch (...)
	{
		std::cerr << "Exception! Program terminates.\n";
		return 1;
	}
}

Mainwindow::Mainwindow ( CommandLine & cline )
	:
	m_cline		( cline ),
	m_zoo		( cline, (size_t)cline.get_thread_total() ),
	m_gin		( SDL_INIT_VIDEO ),
	m_window	( GIN_WINDOW_CANVAS_2D )
{
	char const * const filename = m_cline.get_input_filename();
	if ( filename )
	{
		m_zoo.init_image ( filename );
	}
	else
	{
		m_zoo.init_image ( 256, 256 );
	}

	// At this point all of the pixmap and PRND memory has been allocated

	m_window.init ( APP_NAME " - PIxel Zoo ORnaments",
		m_cline.mprefs.window_x,
		m_cline.mprefs.window_y,
		m_cline.mprefs.window_w,
		m_cline.mprefs.window_h,
		(m_cline.mprefs.window_maximized ? SDL_WINDOW_MAXIMIZED : 0)
			| SDL_WINDOW_RESIZABLE /*| SDL_WINDOW_BORDERLESS*/ );

	m_gin.window_add ( m_window );

	m_window.set_size_min ( 100, 100 );
//	m_window.set_icon ( core.get_svg_filename() );

	m_window.add_signal ( GIN_SIGNAL_SDL_EVENT, [this]
	(mtGin::CB_Data const * const data)
	{
		auto const d = dynamic_cast<mtGin::CB_SDLEvent const *>(data);

		if ( ! d || ! d->sdl_event )
		{
			return 1;	// Not actioned
		}

		SDL_Event const * const event = d->sdl_event;

		switch ( event->type )
		{
		case SDL_MOUSEBUTTONDOWN:
			mouse_click_event ( event );
			break;

		case SDL_MOUSEMOTION:
			mouse_motion_event ( event );
			break;

		case SDL_KEYDOWN:
			key_down_event ( event );
			break;

		case SDL_WINDOWEVENT:
			switch ( event->window.event )
			{
			case SDL_WINDOWEVENT_CLOSE:
				m_gin.stop_main_loop ();
				break;
			}

			break;
		}

		return 0;		// Actioned
	} );

	m_gin.frame_callback.set ( [this]()
	{
		m_zoo.cycle_pixmap();
	} );

	m_window.add_signal( GIN_SIGNAL_DESTROY_WINDOW, [this](void *)
	{
		int x, y, w, h;
		Uint32 flags;
		m_window.get_geometry ( x, y, w, h, flags );

		int const maximized = (flags & SDL_WINDOW_MAXIMIZED) ? 1 : 0;

		m_cline.uprefs.set ( PREFS_WINDOW_MAXIMIZED, maximized );

		if ( 0 == maximized )
		{
			m_cline.uprefs.set ( PREFS_WINDOW_X, x );
			m_cline.uprefs.set ( PREFS_WINDOW_Y, y );
			m_cline.uprefs.set ( PREFS_WINDOW_W, w );
			m_cline.uprefs.set ( PREFS_WINDOW_H, h );
		}

		return 0;
	} );

	m_window.render_scale ( m_zoo.get_pixmap(), 1, 0 );

	// As per note in mtgin_sdl.h, an indexed pixmap can be sped up via this
	if ( cline.get_thread_total() > 1 )
	{
		m_window.render_init_threads ( cline.get_thread_total() );
	}

	m_window.m_render.set ( [this]()
	{
		if ( m_zoo.mode() == MODE_WORKING )
		{
			SDL_SetRenderDrawColor( m_window.renderer(), BORDER_RED,
				BORDER_GREEN, BORDER_BLUE, SDL_ALPHA_OPAQUE );

			m_window.render_pixmap ( m_zoo.get_pixmap() );
		}
	} );

	fps_rate(20);
	m_window.m_render.start ();
	m_gin.frame_callback.start ();
}

int Mainwindow::start ()
{
	m_gin.main_loop ();

	return m_cline.exit().value();
}

void Mainwindow::fps_change ( int const delta )
{
	int const fps = mtkit_int_bound ( m_fps + delta, FPS_MIN, FPS_MAX );

	if ( fps != m_fps )
	{
		fps_rate ( fps );
	}
}

void Mainwindow::fps_rate ( int const fps )
{
	m_gin.set_fps_max ( fps );
	m_zoo.set_fps ( fps );
	m_fps = fps;
}

void Mainwindow::mouse_click_event ( SDL_Event const * const event )
{
	int const button = event->button.button;
	int const state = event->button.state;
	if ( state != SDL_PRESSED ||
		(	button != SDL_BUTTON_LEFT &&
			button != SDL_BUTTON_RIGHT &&
			button != SDL_BUTTON_MIDDLE
			)
		)
	{
		// No mouse button was pressed down, so nothing to do
		return;
	}

	mouse_event ( button, event );
}

void Mainwindow::mouse_motion_event ( SDL_Event const * const event )
{
	int const button =
		(event->button.button & SDL_BUTTON_LMASK) ? SDL_BUTTON_LEFT :
		(event->button.button & SDL_BUTTON_RMASK) ? SDL_BUTTON_RIGHT :
		(event->button.button & SDL_BUTTON_MMASK) ? SDL_BUTTON_MIDDLE :
		0;

	if ( ! button )
	{
		// No mouse button was pressed down, so nothing to do
		return;
	}

	mouse_event ( button, event );
}

void Mainwindow::mouse_event (
	int		const	button,
	SDL_Event const * const	event
	)
{
	// A mouse button click/hold was detected by the caller

	int const x = event->button.x;
	int const y = event->button.y;
	SDL_Renderer * const renderer = m_window.renderer();

	int w, h;
	SDL_RenderGetLogicalSize ( renderer, &w, &h );

	if ( x < 0 || y < 0 || x >= w || y >= h )
	{
		// Mouse button was pressed, but is outside the pixmap area
		return;
	}

	switch ( button )
	{
	case SDL_BUTTON_LEFT:
	case SDL_BUTTON_RIGHT:
	case SDL_BUTTON_MIDDLE:
		m_zoo.plot_pixel ( x, y );
		break;
	}
}

void Mainwindow::key_down_event ( SDL_Event const * const event )
{
	switch ( event->key.keysym.sym )
	{
	case SDLK_PLUS:
	case SDLK_KP_PLUS:
		fps_change ( 1 );
		break;

	case SDLK_MINUS:
	case SDLK_KP_MINUS:
		fps_change ( -1 );
		break;

	case SDLK_1:
	case SDLK_KP_1:
		fps_rate ( 10 );
		break;

	case SDLK_2:
	case SDLK_KP_2:
		fps_rate ( 20 );
		break;

	case SDLK_3:
	case SDLK_KP_3:
		fps_rate ( 30 );
		break;

	case SDLK_4:
	case SDLK_KP_4:
		fps_rate ( 40 );
		break;

	case SDLK_5:
	case SDLK_KP_5:
		fps_rate ( 50 );
		break;

	case SDLK_6:
	case SDLK_KP_6:
		fps_rate ( 60 );
		break;

	case SDLK_s:
		m_zoo.save_screenshot ( m_cline.get_output_filename () );
		break;

	case SDLK_f:
		toggle_fullscreen ();
		break;

	case SDLK_SPACE:
		m_zoo.toggle_mode ();
		break;

	case SDLK_ESCAPE:
	case SDLK_q:
		m_gin.stop_main_loop ();
		break;
	}
}

void Mainwindow::toggle_fullscreen ()
{
	Uint32 const flags = SDL_GetWindowFlags(m_window.sdl());
	if ( flags & SDL_WINDOW_FULLSCREEN_DESKTOP )
	{
		SDL_SetWindowFullscreen ( m_window.sdl(), 0 );
	}
	else
	{
		SDL_SetWindowFullscreen ( m_window.sdl(),
			SDL_WINDOW_FULLSCREEN_DESKTOP );
	}
}

