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

        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	"edstring.h"
#include	"env.h"
#include	"Envelope.h"
#include	"gtracker.h"
#include	"inst.h"
#include	"Instrument.h"
#include	"intrface.h"
#include	"log.h"
#include	"Popup.h"
#include	"rsc01.h"



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



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



/*\\\ PROTOTYPES DES FONCTIONS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

Instrument	*INST_instr_ptr [1+INST_NBRINSTR_MAXI];



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



signed int	INST_init (void)
{
	for (int cnt = 0; cnt <= INST_NBRINSTR_MAXI; cnt ++)
	{
		INST_instr_ptr [cnt] = new Instrument (cnt);
		if (INST_instr_ptr [cnt] == NULL)
		{
			LOG_printf ("INST_init: couldn't create instrument # %d.\n", cnt);
			return (-1);
		}
		if (INST_instr_ptr [cnt]->check_ok ())
		{
			LOG_printf ("INST_init: couldn't create instrument # %d.\n", cnt);
			return (-1);
		}
	}

	return (0);
}



void	INST_restore (void)
{
	for (int cnt = 0; cnt <= INST_NBRINSTR_MAXI; cnt ++)
	{
		if (INST_instr_ptr [cnt] != NULL)
		{
			delete INST_instr_ptr [cnt];
			INST_instr_ptr [cnt] = NULL;
		}
	}
}



/****************************************************************************/
/*                                                                          */
/*      ROUTINES DE BASE                                                    */
/*                                                                          */
/****************************************************************************/



void	INST_get_instr_name (int instr, char *name_0)
{
	INST_instr_ptr [instr]->get_instr_name (name_0);
}



void	INST_set_instr_name (int instr, const char *name_0)
{
	INST_instr_ptr [instr]->set_instr_name (name_0);
}



int	INST_get_instr_volume (int instr)
{
	return (INST_instr_ptr [instr]->get_instr_volume ());
}



void	INST_set_instr_volume (int instr, int volume)
{
	INST_instr_ptr [instr]->set_instr_volume (volume);
}



signed int	INST_get_instr_autobal (int instr)
{
	return (INST_instr_ptr [instr]->get_instr_autobal ());
}



void	INST_set_instr_autobal (int instr, signed int balance)
{
	INST_instr_ptr [instr]->set_instr_autobal (balance);
}



int	INST_get_instr_sample (int instr, int note)
{
	return (INST_instr_ptr [instr]->get_instr_sample (note));
}



void	INST_set_instr_sample (int instr, int note, int sample)
{
	INST_instr_ptr [instr]->set_instr_sample (note, sample);
}



signed int	INST_get_instr_transp (int instr, int note)
{
	return (INST_instr_ptr [instr]->get_instr_transp (note));
}



void	INST_set_instr_transp (int instr, int note, signed int transp)
{
	INST_instr_ptr [instr]->set_instr_transp (note, transp);
}



int	INST_get_instr_zone_start (int instr, int note)
{
	return (INST_instr_ptr [instr]->get_instr_zone_start (note));
}



int	INST_get_instr_zone_end (int instr, int note)
{
	return (INST_instr_ptr [instr]->get_instr_zone_end (note));
}



int	INST_get_instr_env (int instr, int type)
{
	return (INST_instr_ptr [instr]->get_instr_env (type));
}



void	INST_set_instr_env (int instr, int type, int env)
{
	INST_instr_ptr [instr]->set_instr_env (type, env);
}



bool	INST_get_filter_flag (int instr)
{
	return (INST_instr_ptr [instr]->get_filter_flag ());
}



void	INST_set_filter_flag (int instr, bool flag)
{
	INST_instr_ptr [instr]->set_filter_flag (flag);
}



bool	INST_get_filter_freq_vol_flag (int instr)
{
	return (INST_instr_ptr [instr]->get_filter_freq_vol_flag ());
}



void	INST_set_filter_freq_vol_flag (int instr, bool flag)
{
	INST_instr_ptr [instr]->set_filter_freq_vol_flag (flag);
}



bool	INST_get_filter_q_vol_flag (int instr)
{
	return (INST_instr_ptr [instr]->get_filter_q_vol_flag ());
}



void	INST_set_filter_q_vol_flag (int instr, bool flag)
{
	INST_instr_ptr [instr]->set_filter_q_vol_flag (flag);
}



double	INST_get_filter_freq (int instr, int type)
{
	return (INST_instr_ptr [instr]->get_filter_freq (type));
}



void	INST_set_filter_freq (int instr, int type, double val)
{
	INST_instr_ptr [instr]->set_filter_freq (type, val);
}



double	INST_get_filter_q (int instr, int type)
{
	return (INST_instr_ptr [instr]->get_filter_q (type));
}



void	INST_set_filter_q (int instr, int type, double val)
{
	INST_instr_ptr [instr]->set_filter_q (type, val);
}



signed int	INST_kill_instr (int instr)
{
	int		env_type;
	int		env_nbr;

	/* Efface d'abord les enveloppes associees */
	for (env_type = 0; env_type < Envelope_NBR_TYPES; env_type ++)
	{
		env_nbr = INST_get_instr_env (instr, env_type);
		if (env_nbr > 0)
		{
			if (ENV_kill_envelope (env_type, env_nbr))
			{
				LOG_printf ("INST_kill_instr: Error: couldn't clear %s envelope.\n",
							   Envelope::CONFIG [env_type].name_0);
				return (-1);
			}
		}
	}

	/* Efface l'instrument lui-meme */
	if (INST_instr_ptr [instr]->kill ())
	{
		LOG_printf ("INST_kill_instr: Error: couldn't clear instrument # %d.\n",
		            instr);
		return (-1);
	}

	return (0);
}


/*==========================================================================*/
/*      Nom: INST_clear_instruments                                         */
/*      Description: Efface tous les instruments (mais pas les samples).    */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	INST_clear_instruments (void)
{
	for (int instr = 1; instr <= INST_NBRINSTR_MAXI; instr ++)
	{
		if (INST_kill_instr (instr))
		{
			LOG_printf ("INST_clear_instruments: Error: couldn't clear instrument # %d.\n",
			            instr);
			return (-1);
		}
	}

	return (0);
}



void	INST_find_samples_for_inst (int instr, std::set <int> &result)
{
	result.clear ();
	for (int note = 0; note < GTK_NBRNOTES_MAXI; ++ note)
	{
		const int      spl = INST_get_instr_sample (instr, note);
		result.insert (spl);
	}
}



/****************************************************************************/
/*                                                                          */
/*      ROUTINES AVEC UTILISATION DE L'INTERFACE                            */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: INST_set_instr_volume_intr                                     */
/*      Description: Change le volume de l'instrument 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	INST_set_instr_volume_intr (int type, signed int value)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		nbr3_0 [3+1];
	Popup		velo_popup;

	new_value = INST_get_instr_volume (GTK_instr_nbr);

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

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr3_0, "%3X", new_value);
		EDIT_edit_string_base (nbr3_0, RSC_OBJ_MP_SUBM_INSTR_MAIN_GRP_VOL_VAL, 3, 16);
		new_value = (int) strtol (nbr3_0, NULL, 16);
		break;

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

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		velo_popup.add_line ("100 (  0 dB)", 0x100);
		velo_popup.add_line (" C0 ( -4 dB)", 0x0C0);
		velo_popup.add_line (" 80 ( -6 dB)", 0x080);
		velo_popup.add_line (" 60 (-10 dB)", 0x060);
		velo_popup.add_line (" 40 (-12 dB)", 0x040);
		velo_popup.add_line (" 30 (-16 dB)", 0x030);
		velo_popup.add_line (" 20 (-18 dB)", 0x020);
		line = velo_popup.select_radio_by_code (new_value);
		code = velo_popup.manage (line);
		if (code >= 0)
		{
			new_value = code;
		}
		break;
	}

	new_value = MIN (new_value, 0x100);
	new_value = MAX (new_value, 0);
	INST_set_instr_volume (GTK_instr_nbr, new_value);
	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();

	return (0);
}



/*==========================================================================*/
/*      Nom: INST_set_instr_env_intr                                        */
/*      Description: Change une enveloppe de l'instrument 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.                                */
/*        - env_type: type de l'enveloppe.                                  */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	INST_set_instr_env_intr (int type, signed int value, int env_type)
{
	int		new_value;

	new_value = INST_get_instr_env (GTK_instr_nbr, env_type);

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

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		break;

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

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		break;
	}

	new_value = MIN (new_value, INST_NBRINSTR_MAXI);
	new_value = MAX (new_value, 0);
	INST_set_instr_env (GTK_instr_nbr, env_type, new_value);
	GTK_modified_flag = true;
	INTR_pattern_editor_menu_ptr->refresh ();

	return (0);
}



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



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