/*****************************************************************************

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*****************************************************************************/



#if defined (_MSC_VER)
	#pragma warning (1 : 4130 4223 4705 4706)
	#pragma warning (4 : 4355 4786 4800)
#endif



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<stdio.h>

#include	"base.h"
#include	"base_ct.h"
#include	"file.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"log.h"
#include	"modstruc.h"
#include	"mpannel.h"
#include	"PatEdMenuToolsClear.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"song.h"



/*\\\ CONSTANTES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	PatEdMenuToolsClear::check_ok (void) const
{
	if (this == NULL)
	{
		LOG_printf ("PatEdMenuToolsClear::check_ok: Error: \"this\" pointer is NULL.\n");
		return (-1);
	}

	/*** A faire ***/

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	PatEdMenuToolsClear::self_display (void) const
{

	/*** A faire ***/

}



/*==========================================================================*/
/*      Nom: redraw                                                         */
/*      Description: Affiche la composante d'interface.                     */
/*==========================================================================*/

void	PatEdMenuToolsClear::redraw (void)
{
	RSC_display_object (RSC_OBJ_MP_SUBM);

	refresh ();
}



/*==========================================================================*/
/*      Nom: refresh                                                        */
/*      Description: Rafraichit l'affichage des donnees.                    */
/*==========================================================================*/

void	PatEdMenuToolsClear::refresh (void)
{
	/* Rien de special */

	refresh_dynamic (true);
}



/*==========================================================================*/
/*      Nom: refresh_dynamic                                                */
/*      Description: Rafraichit l'affichage des donnees dynamiques.         */
/*        - force_flag: true indique qu'on doit forcer le rafraichissement. */
/*==========================================================================*/

void	PatEdMenuToolsClear::refresh_dynamic (bool force_flag)
{
	/* Rien en fait */
}



/*==========================================================================*/
/*      Nom: manage                                                         */
/*      Description: Gere la composante d'interface                         */
/*      Parametres en entree:                                               */
/*        - sel_object: objet detecte par le gestionnaire d'interface       */
/*        - sel_elder: aine de sel_object                                   */
/*==========================================================================*/

void	PatEdMenuToolsClear::manage (int sel_object, int sel_elder)
{
	switch (sel_object)
	{

	/* Song */
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_SNG:
		if (INTR_dialog_box ("CLEAR SONG",
		                     "Clear patterns and sequence ?",
									"OK\nCancel", 0, 1) == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			if (GTK_clear_song ())
			{
				INTR_dialog_box ("Error",
									  "Couldn't clear song successfully.",
									  "Cancel", 0, 0);
			}
			INTR_graph_ptr->mouse_arrow ();
			MPAN_display_main_pannel ();
		}
		break;

	/* Instruments */
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_INS:
		if (INTR_dialog_box ("CLEAR INSTRUMENTS",
		                     "Clear instruments, envelopes and samples ?",
									"OK\nCancel", 0, 1) == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			if (GTK_clear_instruments ())
			{
				INTR_dialog_box ("Error",
									  "Couldn't clear instruments successfully.",
									  "Cancel", 0, 0);
			}
			INTR_graph_ptr->mouse_arrow ();
			MPAN_display_divers ();
			refresh ();
			MPAN_display_status (true);
		}
		break;

	/* Samples */
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_SPL:
		if (INTR_dialog_box ("CLEAR SAMPLES",
		                     "Clear samples ?",
									"OK\nCancel", 0, 1) == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			if (SAMP_clear_samples ())
			{
				INTR_dialog_box ("Error",
									  "Couldn't clear samples successfully.",
									  "Cancel", 0, 0);
			}
			INTR_graph_ptr->mouse_arrow ();
			MPAN_display_divers ();
			refresh ();
			MPAN_display_status (true);
		}
		break;

	/* Effets */
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_FX:
		if (INTR_dialog_box ("CLEAR FX",
		                     "Clear effect presets ?",
									"OK\nCancel", 0, 1) == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			if (FXP_clear_presets ())
			{
				INTR_dialog_box ("Error",
									  "Couldn't clear effect presets successfully.",
									  "Cancel", 0, 0);
			}
			INTR_graph_ptr->mouse_arrow ();
			MPAN_display_divers ();
			refresh ();
			MPAN_display_status (true);
		}
		break;

	/* Mix */
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_MIX:
		if (INTR_dialog_box ("CLEAR MIX",
		                     "Clear mix presets ?",
									"OK\nCancel", 0, 1) == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			if (MIXP_clear_presets ())
			{
				INTR_dialog_box ("Error",
									  "Couldn't clear mix presets successfully.",
									  "Cancel", 0, 0);
			}
			INTR_graph_ptr->mouse_arrow ();
			MPAN_display_divers ();
			refresh ();
			MPAN_display_status (true);
		}
		break;

	/* Tout */
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_ALL:
		if (INTR_dialog_box ("CLEAR ALL",
		                     "Clear module ?",
									"OK\nCancel", 0, 1) == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			if (GTK_clear_all ())
			{
				INTR_dialog_box ("Error",
									  "Couldn't clear module successfully.",
									  "Cancel", 0, 0);
			}
			else
			{
				FILE_generate_new_module_name ();
			}
			INTR_graph_ptr->mouse_arrow ();
			MPAN_display_main_pannel ();
		}
		break;

	// Unused patterns
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_PATUN:
		clear_unused_patterns ();
		break;

	// Unused instruments
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_INSUN:
		clear_unused_instruments ();
		break;

	// Unused samples
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_SPLUN:
		clear_unused_samples ();
		break;

	// All unused
	case	RSC_OBJ_MP_SUBM_TOOLS_CLEAR_ALLUN:
		clear_all_unused_things ();
		break;
	}
}



/*==========================================================================*/
/*      Nom: activate                                                       */
/*      Description: Active ou desactive le menu. Celui-ci n'est pas        */
/*                   reaffiche.                                             */
/*      Parametres en entree:                                               */
/*        - activated_flag: indique si le menu doit etre active ou          */
/*                          desactive.                                      */
/*==========================================================================*/

void	PatEdMenuToolsClear::activate (bool activated_flag)
{
	RSC_pos_flag (RSC_OBJ_MP_SUBM_TOOLS_CLEAR, RSC_ATTR_NOTDISP, ! activated_flag);
	RSC_pos_flag (RSC_OBJ_MP_SUBM_TOOLS_PICN_CLEAR, RSC_ATTR_SELECTED, activated_flag);
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



void	PatEdMenuToolsClear::clear_unused_patterns ()
{
	INTR_graph_ptr->mouse_bee ();

	ObjSet			used_pat_set;
	collect_patterns_used_in_song (used_pat_set);

	ObjSet			clr_pat_set;
	find_unused_patterns (clr_pat_set, used_pat_set);

	INTR_graph_ptr->mouse_arrow ();

	std::string	x =
		"Clear unused patterns?\n\n"
		"The following objects will be cleared:\n\n";
	x += print_clear_stat (&clr_pat_set, 0, 0);
	const int		button = INTR_dialog_box (
		"CLEAR UNUSED PATTERNS",
		x.c_str (),
		"OK\nCancel",
		0,
		1
	);

	if (button == 0)
	{
		INTR_graph_ptr->mouse_bee ();
		const int		ret_val = clear_all_sets (&clr_pat_set, 0, 0);
		INTR_graph_ptr->mouse_arrow ();

		if (ret_val != 0)
		{
			INTR_dialog_box (
				"Error",
				"Couldn't clear unused patterns successfully.",
				"Cancel",
				0,
				0
			);
		}
	}

	MPAN_display_divers ();
	refresh ();
	MPAN_display_status (true);
}



void	PatEdMenuToolsClear::clear_unused_instruments ()
{
	INTR_graph_ptr->mouse_bee ();

	ObjSet			used_pat_set;
	collect_non_empty_patterns (used_pat_set);
	ObjSet			used_instr_set;
	collect_instruments_used_in_patterns (used_instr_set, used_pat_set);

	ObjSet			clr_instr_set;
	find_unused_instruments (clr_instr_set, used_instr_set);

	INTR_graph_ptr->mouse_arrow ();

	std::string	x =
		"Clear unused instruments?\n\n"
		"The following objects will be cleared:\n\n";
	x += print_clear_stat (0, &clr_instr_set, 0);
	const int		button = INTR_dialog_box (
		"CLEAR UNUSED INSTRUMENTS",
		x.c_str (),
		"OK\nCancel",
		0,
		1
	);

	if (button == 0)
	{
		INTR_graph_ptr->mouse_bee ();
		const int		ret_val = clear_all_sets (0, &clr_instr_set, 0);
		INTR_graph_ptr->mouse_arrow ();

		if (ret_val != 0)
		{
			INTR_dialog_box (
				"Error",
				"Couldn't clear unused instruments successfully.",
				"Cancel",
				0,
				0
			);
		}
	}

	MPAN_display_divers ();
	refresh ();
	MPAN_display_status (true);
}



// Actually takes into account the samples used by instruments used in any
// pattern (even if not referencedd in the song).
void	PatEdMenuToolsClear::clear_unused_samples ()
{
	INTR_graph_ptr->mouse_bee ();

	ObjSet			used_pat_set;
	collect_non_empty_patterns (used_pat_set);
	ObjSet			used_instr_set;
	collect_instruments_used_in_patterns (used_instr_set, used_pat_set);
	ObjSet			used_spl_set;
	collect_sample_used_in_instruments (used_spl_set, used_instr_set);

	ObjSet			clr_spl_set;
	find_unused_samples (clr_spl_set, used_spl_set);

	INTR_graph_ptr->mouse_arrow ();

	std::string	x =
		"Clear unused samples?\n\n"
		"The following objects will be cleared:\n\n";
	x += print_clear_stat (0, 0, &clr_spl_set);
	const int		button = INTR_dialog_box (
		"CLEAR UNUSED SAMPLES",
		x.c_str (),
		"OK\nCancel",
		0,
		1
	);

	if (button == 0)
	{
		INTR_graph_ptr->mouse_bee ();
		const int		ret_val = clear_all_sets (0, 0, &clr_spl_set);
		INTR_graph_ptr->mouse_arrow ();

		if (ret_val != 0)
		{
			INTR_dialog_box (
				"Error",
				"Couldn't clear unused samples successfully.",
				"Cancel",
				0,
				0
			);
		}
	}

	MPAN_display_divers ();
	refresh ();
	MPAN_display_status (true);
}



void	PatEdMenuToolsClear::clear_all_unused_things ()
{
	INTR_graph_ptr->mouse_bee ();

	ObjSet			used_pat_set;
	collect_patterns_used_in_song (used_pat_set);
	ObjSet			used_instr_set;
	collect_instruments_used_in_patterns (used_instr_set, used_pat_set);
	ObjSet			used_spl_set;
	collect_sample_used_in_instruments (used_spl_set, used_instr_set);

	ObjSet			clr_pat_set;
	find_unused_patterns (clr_pat_set, used_pat_set);
	ObjSet			clr_instr_set;
	find_unused_instruments (clr_instr_set, used_instr_set);
	ObjSet			clr_spl_set;
	find_unused_samples (clr_spl_set, used_spl_set);

	INTR_graph_ptr->mouse_arrow ();

	std::string	x =
		"Clear all unused patterns, instruments and samples?\n\n"
		"The following objects will be cleared:\n\n";
	x += print_clear_stat (&clr_pat_set, &clr_instr_set, &clr_spl_set);
	const int		button = INTR_dialog_box (
		"CLEAR ALL UNUSED THINGS",
		x.c_str (),
		"OK\nCancel",
		0,
		1
	);

	if (button == 0)
	{
		INTR_graph_ptr->mouse_bee ();
		const int		ret_val =
			clear_all_sets (&clr_pat_set, &clr_instr_set, &clr_spl_set);
		INTR_graph_ptr->mouse_arrow ();

		if (ret_val != 0)
		{
			INTR_dialog_box (
				"Error",
				"Couldn't clear all unused things successfully.",
				"Cancel",
				0,
				0
			);
		}
	}

	MPAN_display_divers ();
	refresh ();
	MPAN_display_status (true);
}



void	PatEdMenuToolsClear::collect_non_empty_patterns (ObjSet &pat_set) const
{
	pat_set.clear ();

	for (int pattern = 0; pattern < PAT_NBRPATTERNS_MAXI; ++pattern)
	{
		if (MODS_pattern_filled (Pattern_TYPE_SPL, pattern))
		{
			pat_set.insert (pattern);
		}
	}
}



void	PatEdMenuToolsClear::collect_patterns_used_in_song (ObjSet &pat_set) const
{
	pat_set.clear ();

	const int		song_len = SONG_get_song_length ();
	for (int pos = 0; pos < song_len; ++pos)
	{
		const int		pattern = SONG_get_pattern_number (pos);
		pat_set.insert (pattern);
	}
}



void	PatEdMenuToolsClear::collect_instruments_used_in_patterns (ObjSet &instr_set, const ObjSet &pat_set) const
{
	instr_set.clear ();

	for (ObjSet::const_iterator it_pat = pat_set.begin ()
	;	it_pat != pat_set.end ()
	;	++it_pat)
	{
		const int		pattern = *it_pat;
		const int		nbr_lines = PAT_get_pattern_height (pattern);
		const int		nbr_tracks = PAT_get_pattern_nbr_tracks (Pattern_TYPE_SPL, pattern);
		for (int line = 0; line < nbr_lines; ++line)
		{
			for (int track = 0; track < nbr_tracks; ++track)
			{
				const MODS_GT2_SPL_NOTE	*	note_ptr =
					PAT_get_spl_note_adr_pat (pattern, line, track);
				const int		instr = note_ptr->instr;
				if (instr > 0)
				{
					instr_set.insert (instr);
				}
			}
		}
	}
}



void	PatEdMenuToolsClear::collect_sample_used_in_instruments (ObjSet &spl_set, const ObjSet &instr_set) const
{
	spl_set.clear ();

	for (ObjSet::const_iterator it_instr = instr_set.begin ()
	;	it_instr != instr_set.end ()
	;	++it_instr)
	{
		const int		instr = *it_instr;

		for (int note = 0; note < GTK_NBRNOTES_MAXI; ++note)
		{
			const int		spl = INST_get_instr_sample (instr, note);
			if (spl > 0)
			{
				spl_set.insert (spl);
			}
		}
	}
}



void	PatEdMenuToolsClear::find_unused_patterns (ObjSet &pat_set, const ObjSet &reserved_pat_set) const
{
	pat_set.clear ();

	for (int pattern = 0; pattern < PAT_NBRPATTERNS_MAXI; ++pattern)
	{
		bool			fill_flag = false;
		for (int track_type = 0
		;	track_type < Pattern_NBR_TYPES && ! fill_flag
		;	++track_type)
		{
			fill_flag = MODS_pattern_filled (track_type, pattern);
		}

		if (   fill_flag
		    && reserved_pat_set.find (pattern) == reserved_pat_set.end ())
		{
			pat_set.insert (pattern);
		}
	}
}



void	PatEdMenuToolsClear::find_unused_instruments (ObjSet &instr_set, const ObjSet &reserved_instr_set) const
{
	instr_set.clear ();

	for (int instr = 1; instr <= INST_NBRINSTR_MAXI; ++instr)
	{
		if (   MODS_instr_filled (instr)
		    && reserved_instr_set.find (instr) == reserved_instr_set.end ())
		{
			instr_set.insert (instr);
		}
	}
}



void	PatEdMenuToolsClear::find_unused_samples (ObjSet &spl_set, const ObjSet &reserved_spl_set) const
{
	spl_set.clear ();

	for (int spl = 1; spl < SAMP_NBRSAMPLES_MAXI; ++spl)
	{
		if (   MODS_sample_filled (spl)
		    && reserved_spl_set.find (spl) == reserved_spl_set.end ())
		{
			spl_set.insert (spl);
		}
	}
}



int	PatEdMenuToolsClear::clear_all_sets (const ObjSet *pat_set_ptr, const ObjSet *instr_set_ptr, const ObjSet *spl_set_ptr)
{
	int				ret_val = 0;

	if (ret_val == 0 && pat_set_ptr != 0)
	{
		for (ObjSet::const_iterator it = pat_set_ptr->begin ()
		;	it != pat_set_ptr->end () && ret_val == 0
		;	++it)
		{
			ret_val = PAT_clear_pattern (*it);
		}
	}

	if (ret_val == 0 && instr_set_ptr != 0)
	{
		for (ObjSet::const_iterator it = instr_set_ptr->begin ()
		;	it != instr_set_ptr->end () && ret_val == 0
		;	++it)
		{
			ret_val = INST_kill_instr (*it);
		}
	}

	if (ret_val == 0 && spl_set_ptr != 0)
	{
		for (ObjSet::const_iterator it = spl_set_ptr->begin ()
		;	it != spl_set_ptr->end () && ret_val == 0
		;	++it)
		{
			ret_val = SAMP_kill_sample (*it);
		}
	}

	return (ret_val);
}



std::string	PatEdMenuToolsClear::print_clear_stat (const ObjSet *pat_set_ptr, const ObjSet *instr_set_ptr, const ObjSet *spl_set_ptr) const
{
	char				txt_0 [1023+1];
	long				total_mem = 0;
	std::string		result;
	bool				del_obj_flag = false;

	if (pat_set_ptr != 0 && ! pat_set_ptr->empty ())
	{
		result += "Patterns:\n";
		for (ObjSet::const_iterator it = pat_set_ptr->begin ()
		;	it != pat_set_ptr->end ()
		;	++ it)
		{
			const int		pattern = *it;
			sprintf (txt_0, INTR_base_song_3, pattern);
			result += txt_0;
			result += ": ";

			PAT_get_pattern_name (pattern, txt_0);
			BASE_complete_string (txt_0, 32);
			result += txt_0;
			result += " - ";

			const long		mem = PAT_estimate_memory_usage (pattern);
			const long		mem_kb = (mem + 1023) >> 10;
			sprintf (txt_0, "%6ld KB\n", mem_kb);
			result += txt_0;

			total_mem += mem;
		}
		result += "\n";
		del_obj_flag = true;
	}

	if (instr_set_ptr != 0 && ! instr_set_ptr->empty ())
	{
		result += "Instruments:\n";
		for (ObjSet::const_iterator it = instr_set_ptr->begin ()
		;	it != instr_set_ptr->end ()
		;	++ it)
		{
			const int		instr = *it;
			sprintf (txt_0, INTR_base_song_3, instr);
			result += txt_0;
			result += ": ";

			INST_get_instr_name (instr, txt_0);
			result += txt_0;
			result += "\n";
		}
		result += "\n";
		del_obj_flag = true;
	}

	if (spl_set_ptr != 0 && ! spl_set_ptr->empty ())
	{
		result += "Samples:\n";
		for (ObjSet::const_iterator it = spl_set_ptr->begin ()
		;	it != spl_set_ptr->end ()
		;	++ it)
		{
			const int		sample = *it;
			sprintf (txt_0, INTR_base_song_3, sample);
			result += txt_0;
			result += ": ";

			SAMP_get_sample_name (sample, txt_0);
			BASE_complete_string (txt_0, 32);
			result += txt_0;
			result += " - ";

			const long		mem = SAMP_estimate_memory_usage (sample);
			const long		mem_kb = (mem + 1023) >> 10;
			sprintf (txt_0, "%6ld KB\n", mem_kb);
			result += txt_0;

			total_mem += mem;
		}
		result += "\n";
		del_obj_flag = true;
	}

	if (del_obj_flag)
	{
		const long		total_mem_kb = (total_mem + 1023) >> 10;
		sprintf (txt_0, "Total to be freed: %6ld KB", total_mem_kb);
		result += txt_0;
	}
	else
	{
		result += "(no unused object to delete)";
	}

	return (result);
}



/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
