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

        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.

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



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

#include	<stdio.h>
#include	<stdlib.h>

#include	"base.h"
#include	"base_ct.h"
#include	"EditString.h"
#include	"edstring.h"
#include	"fx_p.h"
#include	"FxPreset.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"log.h"
#include	"modstruc.h"
#include	"mpannel.h"
#include	"PatEdMenuFx.h"
#include	"player.h"
#include	"resource.h"
#include	"rsc01.h"



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



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

enum
{
	PatEdMenuFx_SUBMENU_NONE = 0,
	PatEdMenuFx_SUBMENU_DELAY,
	PatEdMenuFx_SUBMENU_RESFILT,
	PatEdMenuFx_SUBMENU_DISTO,

	PatEdMenuFx_NBR_SUBMENUS
};



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



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



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



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



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise                                             */
/*==========================================================================*/

PatEdMenuFx::PatEdMenuFx (void)
{
	_track_nbr = 0;
	_submenu = -1;
	set_submenu (PatEdMenuFx_SUBMENU_NONE);

	/* Pour chaque type d'effet, il faut etablir un lien fils -> pere */
	_delay.set_owner (this);
	_resfilt.set_owner (this);
	_disto.set_owner (this);
}



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

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

	return (0);
}



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

void	PatEdMenuFx::self_display (void) const
{

	/*** A faire ***/

}



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

void	PatEdMenuFx::redraw (void)
{
	/* En mode temps reel, on regarde si l'effet a change sur la piste et on
	   le change dans le menu si necessaire */
	get_real_submenu ();

	RSC_pos_flag (RSC_OBJ_MP_SUBM_FX_GAL_GET, RSC_ATTRP_DISABLE,
	              GTK_fx_real_time_mode_flag);
	RSC_pos_flag (RSC_OBJ_MP_SUBM_FX_GAL_SET, RSC_ATTRP_DISABLE,
	              GTK_fx_real_time_mode_flag);

	RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL);

	redraw_submenu ();

	refresh ();
}



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

void	PatEdMenuFx::refresh (void)
{
	char		name_0 [FxPreset_NAME_LEN+1];
	char		nbr3_0 [3+1];

	refresh_dynamic (true);

	/* Preset */
	sprintf (nbr3_0, INTR_base_song_3, GTK_fx_preset_nbr);
	RSC_set_string (RSC_OBJ_MP_SUBM_FX_GAL_PRE_NBR_VAL, nbr3_0);

	FXP_get_name (GTK_fx_preset_nbr, name_0);
	RSC_set_string (RSC_OBJ_MP_SUBM_FX_GAL_PRE_NAME_VAL, name_0);

	/* Numero de piste */
	sprintf (nbr3_0, INTR_base_track_2, _track_nbr + 1);
	RSC_set_string (RSC_OBJ_MP_SUBM_FX_GAL_TRK_NBR_VAL, nbr3_0);

	/* Flag temps reel */
	RSC_pos_flag (RSC_OBJ_MP_SUBM_FX_GAL_RT_CB,
	              RSC_ATTR_SELECTED, GTK_fx_real_time_mode_flag);

	/* Affichages */
	RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_PRE_NBR_VAL);
	RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_PRE_NAME_VAL);
	RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_TRK_NBR_VAL);
	RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_RT_CB);

	switch (_submenu)
	{
	case	PatEdMenuFx_SUBMENU_DELAY:
		_delay.refresh ();
		break;
	case	PatEdMenuFx_SUBMENU_RESFILT:
		_resfilt.refresh ();
		break;
	case	PatEdMenuFx_SUBMENU_DISTO:
		_disto.refresh ();
		break;
	}
}



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

void	PatEdMenuFx::refresh_dynamic (bool force_flag)
{
	static int	old_submenu = -1;

	_track_nbr = MIN (_track_nbr, GTK_nbr_tracks [Pattern_TYPE_FX] - 1);
	_track_nbr = MAX (_track_nbr, 0);

	/* En mode temps reel, on regarde si l'effet a change sur la piste et on
	   le change dans le menu si necessaire */
	get_real_submenu ();

	/* Si l'effet a change, on met a jour le nom de cet effet et
	   on reaffiche le sous-formulaire correspondant. */
	if (_submenu != old_submenu)
	{
		old_submenu = _submenu;
		redraw_submenu ();

		RSC_set_string (RSC_OBJ_MP_SUBM_FX_GAL_FX_VAL,
		                FxPreset::effect_name_0 [_submenu]);
		RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_FX_VAL);
	}

	switch (_submenu)
	{
	case	PatEdMenuFx_SUBMENU_DELAY:
		_delay.refresh_dynamic (force_flag);
		break;
	case	PatEdMenuFx_SUBMENU_RESFILT:
		_resfilt.refresh_dynamic (force_flag);
		break;
	case	PatEdMenuFx_SUBMENU_DISTO:
		_disto.refresh_dynamic (force_flag);
		break;
	}
}



/*==========================================================================*/
/*      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	PatEdMenuFx::manage (int sel_object, int sel_elder)
{
	int		effect_type;
	int		preset;
	int		button;
	char		name_0 [FxPreset_NAME_LEN+1];
	char		text_0 [128+FxPreset_NAME_LEN+1];

	Player &			player = Player::use_instance ();

	switch (sel_object)
	{

	/* Effet */
	case	RSC_OBJ_MP_SUBM_FX_GAL_FX:
		select_effect ();
		break;

	/* Numero de preset */
	case	RSC_OBJ_MP_SUBM_FX_GAL_PRE_NBR:
		FXP_set_preset_number_intr (INTR_CHGTYPE_KBD, 0);
		break;

	/* Preset + */
	case	RSC_OBJ_MP_SUBM_FX_GAL_PRE_U:
		FXP_set_preset_number_intr (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	/* Preset - */
	case	RSC_OBJ_MP_SUBM_FX_GAL_PRE_D:
		FXP_set_preset_number_intr (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	/* Nom du preset */
	case	RSC_OBJ_MP_SUBM_FX_GAL_PRE_NAME:
		FXP_get_name (GTK_fx_preset_nbr, name_0);
		EDIT_edit_string (name_0, RSC_OBJ_MP_SUBM_FX_GAL_PRE_NAME_VAL,
		                  FxPreset_NAME_LEN, EditString_TYPE_ALPHA);
		FXP_set_name (GTK_fx_preset_nbr, name_0);
		GTK_modified_flag = true;
		refresh ();
		break;

	/* Numero de la piste d'application de l'effet */
	case	RSC_OBJ_MP_SUBM_FX_GAL_TRK_NBR:
		set_track_nbr_intr (INTR_CHGTYPE_KBD, 0);
		break;

	/* Piste + */
	case	RSC_OBJ_MP_SUBM_FX_GAL_TRK_U:
		set_track_nbr_intr (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	/* Piste - */
	case	RSC_OBJ_MP_SUBM_FX_GAL_TRK_D:
		set_track_nbr_intr (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	/* Application du preset a l'effet */
	case	RSC_OBJ_MP_SUBM_FX_GAL_APPLY:
		effect_type = FXP_get_effect_type (GTK_fx_preset_nbr);
		FXP_set_effect_type (0, effect_type);
		FXP_set_parameters (0, FXP_get_parameters (GTK_fx_preset_nbr));
		if (   GTK_fx_real_time_mode_flag
		    && _track_nbr < GTK_nbr_tracks [Pattern_TYPE_FX])
		{
			player.set_fx_preset (_track_nbr, 0, 0xFFFFFFFFL);
			GTK_modified_flag = true;
		}
		else
		{
			get_real_submenu ();
		}
		refresh ();
		break;

	/* Memorisation de l'effet dans le preset */
	case	RSC_OBJ_MP_SUBM_FX_GAL_MEM:
		effect_type = FXP_get_effect_type (0);
		FXP_set_effect_type (GTK_fx_preset_nbr, effect_type);
		FXP_set_parameters (GTK_fx_preset_nbr, FXP_get_parameters (0));
		GTK_modified_flag = true;
		refresh ();
		break;

	/* Chargement d'un preset */
	case	RSC_OBJ_MP_SUBM_FX_GAL_LOAD:
		INTR_dialog_box ("", "Not implemented.", "Cancel", 0, 0);

		/*** A faire ***/

		break;

	/* Sauvegarde du preset */
	case	RSC_OBJ_MP_SUBM_FX_GAL_SAVE:
		FXP_get_name (GTK_fx_preset_nbr, name_0);
		sprintf (text_0,
		         "Do you want to save the actual configuration\n"
		         "or the current selected preset # %d \?\n"
		         "%s",
		         GTK_fx_preset_nbr,
		         String (name_0).trim ().c_str ());
		button = INTR_dialog_box ("SAVE EFFECT PRESET", text_0,
		                          "Actual conf\nCurrent preset\nCancel", 0, 2);
		if (button != 2)
		{
			preset = 0;
			if (button == 1)
			{
				preset = GTK_fx_preset_nbr;
			}
			MODS_save_fx_preset (preset);
		}
		break;

	/* Clean FXs */
	case	RSC_OBJ_MP_SUBM_FX_GAL_CLN:
		player.clear_all_effects ();
		GTK_modified_flag = true;
		refresh ();
		MPAN_display_message ("All effects are stoped.");
		break;

	/* Mode Real-Time On / Off */
	case	RSC_OBJ_MP_SUBM_FX_GAL_RT_CB:
		GTK_fx_real_time_mode_flag = ! GTK_fx_real_time_mode_flag;
		RSC_pos_flag (RSC_OBJ_MP_SUBM_FX_GAL_GET, RSC_ATTRP_DISABLE,
		              GTK_fx_real_time_mode_flag);
		RSC_pos_flag (RSC_OBJ_MP_SUBM_FX_GAL_SET, RSC_ATTRP_DISABLE,
		              GTK_fx_real_time_mode_flag);
		RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_GET);
		RSC_display_object (RSC_OBJ_MP_SUBM_FX_GAL_SET);
		refresh ();
		break;

	/* Mode temps differe: prends les parametres de la piste courante */
	case	RSC_OBJ_MP_SUBM_FX_GAL_GET:
		if (! GTK_fx_real_time_mode_flag)
		{
			if (player.get_fx_preset (_track_nbr, 0) == 0)
			{
				if (FXP_get_effect_type (0) != _submenu)
				{
					set_submenu (0, true);
				}
			}
		}
		refresh ();
		break;

	/* Mode temps differe: applique les parametres a la piste courante */
	case	RSC_OBJ_MP_SUBM_FX_GAL_SET:
		if (! GTK_fx_real_time_mode_flag)
		{
			player.set_fx_preset (_track_nbr, 0, 0xFFFFFFFFL);
			GTK_modified_flag = true;
		}
		break;

	default:
		switch (_submenu)
		{
		case	PatEdMenuFx_SUBMENU_DELAY:
			_delay.manage (sel_object, sel_elder);
			break;
		case	PatEdMenuFx_SUBMENU_RESFILT:
			_resfilt.manage (sel_object, sel_elder);
			break;
		case	PatEdMenuFx_SUBMENU_DISTO:
			_disto.manage (sel_object, sel_elder);
			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	PatEdMenuFx::activate (bool activated_flag)
{
	RSC_pos_flag (RSC_OBJ_MP_SUBM_FX, RSC_ATTR_NOTDISP, ! activated_flag);
	RSC_pos_flag (RSC_OBJ_MP_SMICN_FX, RSC_ATTR_SELECTED, activated_flag);
}



int	PatEdMenuFx::get_submenu (void) const
{
	return (_submenu);
}



void	PatEdMenuFx::set_submenu (int submenu, bool adaptation_flag)
{
	int		menu_cnt;
	bool		activate_flag;
	FxPreset_EFFECT_CONF	parameters;

	Player &			player = Player::use_instance ();

	/* Si on force le changement d'effet, on initialise les
	   parametres aux valeurs par defaut. */
	if (adaptation_flag)
	{
		if (player.get_fx_preset (_track_nbr, 0))
		{
			/* Si l'effet est indisponible, on s'en va. */
			return;
		}
		_submenu = FXP_get_effect_type (0);
	}
	else
	{
		if (submenu != _submenu)
		{
			parameters = FXP_get_parameters (0);
			_submenu = submenu;
			FxPreset::reset_parameters (parameters, _submenu);
			FXP_set_effect_type (0, _submenu);
			FXP_set_parameters (0, parameters);
		}
	}

	/* Activation des menus */
	for (menu_cnt = 0; menu_cnt < PatEdMenuFx_NBR_SUBMENUS; menu_cnt ++)
	{
		activate_flag = (menu_cnt == _submenu);

		switch (menu_cnt)
		{
		case	PatEdMenuFx_SUBMENU_NONE:
			RSC_pos_flag (RSC_OBJ_MP_SUBM_FX_FXPAN_NONE,
			              RSC_ATTR_NOTDISP, ! activate_flag);
			break;
		case	PatEdMenuFx_SUBMENU_DELAY:
			_delay.activate (activate_flag);
			break;
		case	PatEdMenuFx_SUBMENU_RESFILT:
			_resfilt.activate (activate_flag);
			break;
		case	PatEdMenuFx_SUBMENU_DISTO:
			_disto.activate (activate_flag);
			break;
		}
	}

	/* Si on est en mode temps reel, on doit affecter cet effet a la piste */
	if (   ! adaptation_flag
	    && GTK_fx_real_time_mode_flag)
	{
		player.set_fx_preset (_track_nbr,  0, 0xFFFFFFFF);
		GTK_modified_flag = true;
	}
}



int	PatEdMenuFx::get_track_nbr (void)
{
	return (_track_nbr);
}



/*==========================================================================*/
/*      Nom: set_track_nbr_intr                                             */
/*      Description: Change le numero de la piste d'application de l'effet  */
/*                   courant de differentes manieres. L'interface est ici   */
/*                   mise en jeu.                                           */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*                2 = Entree au clavier,                                    */
/*                3 = Entree au pop-up.                                     */
/*        - value: le parametre en question.                                */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	PatEdMenuFx::set_track_nbr_intr (int type, signed int value)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		nbr2_0 [2+1];
	char		line_0 [2 + 2 + Pattern_NAME_LEN+1];
	Popup		popup_menu;

	if (GTK_nbr_tracks [Pattern_TYPE_FX] < 1)
	{
		_track_nbr = 0;
		return (0);
	}

	new_value = _track_nbr;

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr2_0, INTR_base_track_2, new_value + 1);
		EDIT_edit_string_base (nbr2_0, RSC_OBJ_MP_SUBM_FX_GAL_TRK_NBR_VAL, 2, INTR_base_track);
		new_value = (int) strtol (nbr2_0, NULL, INTR_base_track) - 1;
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		for (int cnt = 0; cnt < PAT_NBRPATTERNS_MAXI; cnt ++)
		{
			sprintf (line_0, INTR_base_track_2, cnt);
			line_0 [2] = ':';
			line_0 [3] = ' ';
			PAT_get_pattern_name (cnt, line_0 + 2 + 2);
			popup_menu.add_line (line_0, cnt);
		}
		line = popup_menu.select_radio_by_code (new_value);
		code = popup_menu.manage (line);
		if (code >= 0)
		{
			new_value = (int) code;
		}
		break;
	}

	new_value = MIN (new_value, GTK_nbr_tracks [Pattern_TYPE_FX] - 1);
	new_value = MAX (new_value, 0);
	_track_nbr = new_value;
	refresh ();

	return (0);
}



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



void	PatEdMenuFx::redraw_submenu (void)
{
	switch (_submenu)
	{
	case	PatEdMenuFx_SUBMENU_NONE:
		RSC_display_object (RSC_OBJ_MP_SUBM_FX_FXPAN);
		break;
	case	PatEdMenuFx_SUBMENU_DELAY:
		_delay.redraw ();
		break;
	case	PatEdMenuFx_SUBMENU_RESFILT:
		_resfilt.redraw ();
		break;
	case	PatEdMenuFx_SUBMENU_DISTO:
		_disto.redraw ();
		break;
	}
}



void	PatEdMenuFx::select_effect (void)
{
	Popup	popup_menu;
	int		line;
	long		code;

	for (int effect_cnt = 0; effect_cnt < FxPreset_NBR_TYPES; effect_cnt ++)
	{
		popup_menu.add_line (FxPreset::effect_name_0 [effect_cnt], effect_cnt);
	}

	line = popup_menu.select_radio_by_code (_submenu);
	code = popup_menu.manage (line);
	if (code >= 0)
	{
		set_submenu (code);
		refresh ();
	}
}



void	PatEdMenuFx::get_real_submenu (void)
{
	/* En mode temps reel, on regarde si l'effet a change sur la piste et on
	   le change dans le menu si necessaire */
	if (GTK_fx_real_time_mode_flag)
	{
		const Player &	player = Player::use_instance ();
		if (player.get_fx_preset (_track_nbr, 0) == 0)
		{
			if (FXP_get_effect_type (0) != _submenu)
			{
				set_submenu (0, true);
			}
		}
	}

	/* Mode differe */
	else
	{
		if (FXP_get_effect_type (0) != _submenu)
		{
			set_submenu (0, false);
		}
	}
}



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



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