/*
  This file is a part of unixcw project.
  unixcw project is covered by GNU General Public License, version 2 or later.
*/

#ifndef H_LIBCW_TQ
#define H_LIBCW_TQ




#include "config.h"




#include <pthread.h>    /* pthread_mutex_t */
#include <stdbool.h>    /* bool */
#include <stdint.h>     /* uint32_t */




#include "libcw2.h"




#if defined(__cplusplus)
extern "C"
{
#endif





/* Right now there is no function that would calculate number of tones
   representing given character or string, so there is no easy way to
   present exact relationship between capacity of tone queue and
   number of characters that it can hold.  TODO: perhaps we could
   write utility functions to do that calculation? */

/* TODO: create tests that validate correctness of handling of tone
   queue capacity. See if we really handle the capacity correctly. */

enum {
	/* Default and maximum values of two basic parameters of tone
	   queue: capacity and high water mark. The parameters can be
	   modified using suitable function. */

	/* Tone queue will accept at most "capacity" tones. */
	CW_TONE_QUEUE_CAPACITY_MAX = 3000,        /* ~= 5 minutes at 12 WPM */

	/* Tone queue will refuse to accept new tones (TODO: tones or
	   characters?) if number of tones in queue (queue length) is already
	   equal or larger than queue's high water mark. */
	CW_TONE_QUEUE_HIGH_WATER_MARK_MAX = 2900
};




/*
  Tone queue states (with totally random non-zero values).

  We need CW_TQ_EMPTY and CW_TQ_JUST_EMPTIED to detect a moment when
  generator has completed generating the last tone. When generator ends
  playing some tone, it goes to dequeueing next tone. If queue returns
  CW_TQ_JUST_EMPTIED, there is a chance that in the meantime some tone will
  be enqueued in the background. But if the queue returns CW_TQ_EMPTY, it
  means that the tone just generated by generator was truly the last tone.

  This is important for cw_tq_wait_for_end_of_current_tone_internal() that
  must be able to wait for end of any tone, including the end of last tone.

  With two-value cw_queue_state_t type (empty/non-empty), the function can't
  detect transition of generator from "I'm generating the last tone that was
  on queue" state to "I'm done with generating the last tone that was on
  queue". In both states cases the tq will be in "I'm empty" state, and
  cw_tq_wait_for_end_of_current_tone_internal() will return in the middle of
  generator's "I'm generating the last tone that was on queue".
*/
typedef enum {
	/* Initial state of queue, and state to which empty queue transitions
	   from CW_TQ_JUST_EMPTIED, when asked again to dequeue a tone. */
	CW_TQ_EMPTY        = 45,

	/* Transitional state, when last element has been just removed. Next
	   call to dequeue function will make the queue transition from this
	   state to CW_TQ_EMPTY. */
	CW_TQ_JUST_EMPTIED = 51,

	/* There is at least one element on the queue. */
	CW_TQ_NONEMPTY     = 74
} cw_queue_state_t;




/* If there are any slopes in a tone, there can be only rising slope (without
   falling slope), falling slope (without rising slope), or both slopes
   (i.e. standard slopes).  These values don't tell anything about shape of
   slopes (unless you consider 'no slopes' a shape). */
typedef enum cw_tone_slope_mode_t {
	CW_SLOPE_MODE_STANDARD_SLOPES,
	CW_SLOPE_MODE_NO_SLOPES,
	CW_SLOPE_MODE_RISING_SLOPE,
	CW_SLOPE_MODE_FALLING_SLOPE
} cw_tone_slope_mode_t;




/* TODO: come up with thought-out, consistent type system for samples
   count and tone duration. The type system should take into
   consideration very long duration of tones in QRSS. */
typedef int64_t cw_sample_iter_t;




typedef struct {
	/* Frequency of a tone, in Hz. */
	int frequency;

	/* Duration of a tone, in microseconds. */
	int duration;

	/* Is this "forever" tone? See libcw_tq.c for more info about
	   "forever" tones. */
	bool is_forever;

	/* Is this the first tone of a character?  Used to remove full
	   character (all tones constituting a character) from the queue. */
	bool is_first;

	/* Type/mode of slope(s) in a tone. */
	cw_tone_slope_mode_t slope_mode;

	/* Duration of a tone, in samples.
	   This is a derived value, a function of duration and sample rate. */
	cw_sample_iter_t n_samples;

	/* Counter of samples from the tone that have been calculated and put
	   into generator's buffer.

	   If a tone has more samples than size of generator's buffer, then
	   we will have to pass the same tone to generator's function few
	   times, until whole length of tone will be re-calculated into
	   samples and put into generator's buffer. This iterator will be
	   advanced by generator's buffer size each time we pass it to the
	   function.
	 */
	cw_sample_iter_t sample_iterator;

	/* a tone can start and/or end abruptly (which may result in
	   audible clicks), or its beginning and/or end can have form
	   of slopes (ramps), where amplitude increases/decreases less
	   abruptly than if there were no slopes;

	   using slopes reduces audible clicks at the beginning/end of
	   tone, and can be used to shape spectrum of a tone;

	   AFAIK most desired shape of a slope looks like sine wave;
	   most simple one is just a linear slope;

	   slope area should be integral part of a tone, i.e. it shouldn't
	   make the tone longer than duration/n_samples;

	   a tone with rising and falling slope should have this length
	   (in samples):
	   rising_slope_n_samples   +   (n_samples - 2 * slope_n_samples)   +   falling_slope_n_samples

	   libcw allows following slope area scenarios (modes):
	   1. no slopes: tone shouldn't have any slope areas (i.e. tone
	      with constant amplitude);
	   1.a. a special case of this mode is silent tone - amplitude
	        of a tone is zero for whole duration of the tone;
	   2. tone has nothing more than a single slope area (rising or
	      falling); there is no area with constant amplitude;
	   3. a regular tone, with area of rising slope, then area with
	   constant amplitude, and then falling slope;

	   currently, if a tone has both slopes (rising and falling), both
	   slope areas have to have the same length; */

	int rising_slope_n_samples;     /* Number of samples on rising slope. */
	int falling_slope_n_samples;    /* Number of samples on falling slope. */

	/* Useful for marking individual tones during debugging. */
	char debug_id;
} cw_tone_t;





/* Set values of tone's fields. Some field are set with values given
   as arguments to the macro. Other are initialized with default
   values. The macro should be used like this:
   cw_tone_t my_tone;
   CW_TONE_INIT(&tone, 200, 5000, CW_SLOPE_MODE_STANDARD_SLOPES);
 */
#define CW_TONE_INIT(m_tone, m_frequency, m_duration, m_slope_mode) {	\
		(m_tone)->frequency               = m_frequency;	\
		(m_tone)->duration                = m_duration;		\
		(m_tone)->slope_mode              = m_slope_mode;	\
		(m_tone)->is_forever              = false;		\
		(m_tone)->is_first                = false;		\
		(m_tone)->n_samples               = 0;			\
		(m_tone)->sample_iterator         = 0;			\
		(m_tone)->rising_slope_n_samples  = 0;			\
		(m_tone)->falling_slope_n_samples = 0;			\
		(m_tone)->debug_id                = 0;			\
	}


/* Copy values of all fields from one variable of cw_tone_t type to
   the other. The macro accepts pointers to cw_tone_t variables as
   arguments. */
#define CW_TONE_COPY(m_dest, m_source) {				\
		(m_dest)->frequency               = (m_source)->frequency; \
		(m_dest)->duration                = (m_source)->duration; \
		(m_dest)->slope_mode              = (m_source)->slope_mode; \
		(m_dest)->is_forever              = (m_source)->is_forever; \
		(m_dest)->is_first                = (m_source)->is_first; \
		(m_dest)->n_samples               = (m_source)->n_samples; \
		(m_dest)->sample_iterator         = (m_source)->sample_iterator;	\
		(m_dest)->rising_slope_n_samples  = (m_source)->rising_slope_n_samples; \
		(m_dest)->falling_slope_n_samples = (m_source)->falling_slope_n_samples; \
		(m_dest)->debug_id                = (m_source)->debug_id; \
	};





struct cw_gen_struct;

typedef struct {
	volatile cw_tone_t queue[CW_TONE_QUEUE_CAPACITY_MAX];

	/* Tail index of tone queue. Index of last (newest) inserted
	   tone, index of tone to be dequeued from the list as a last
	   one.

	   The index is incremented *after* adding a tone to queue. */
	volatile size_t tail;

	/* Head index of tone queue. Index of first (oldest) tone
	   inserted to the queue. Index of the tone to be dequeued
	   from the queue as a first one. */
	volatile size_t head;

	cw_queue_state_t state;

	size_t capacity;
	size_t high_water_mark;
	size_t len;

	/* It's useful to have the tone queue dequeue function call
	   a client-supplied callback routine when the amount of data
	   in the queue drops below a defined low water mark.
	   This routine can then refill the buffer, as required. */
	volatile size_t low_water_mark;
	void     (* low_water_callback)(void *);
	void     * low_water_callback_arg;


	/* Inter-thread communication. Used to broadcast queue events to
	   waiting functions. */
	pthread_cond_t wait_var;
	pthread_mutex_t wait_mutex;

	/* Generator associated with a tone queue. */
	struct cw_gen_struct * gen;

	char label[LIBCW_OBJECT_INSTANCE_LABEL_SIZE];
} cw_tone_queue_t;



cw_tone_queue_t * cw_tq_new_internal(void);
void              cw_tq_delete_internal(cw_tone_queue_t ** tq);
void              cw_tq_flush_internal(cw_tone_queue_t * tq);

size_t cw_tq_capacity_internal(const cw_tone_queue_t * tq);
size_t cw_tq_length_internal(cw_tone_queue_t * tq);
cw_ret_t cw_tq_enqueue_internal(cw_tone_queue_t * tq, const cw_tone_t * tone);
cw_queue_state_t cw_tq_dequeue_internal(cw_tone_queue_t * tq, cw_tone_t * tone);

cw_ret_t cw_tq_wait_for_level_internal(cw_tone_queue_t * tq, size_t level);
cw_ret_t cw_tq_register_low_level_callback_internal(cw_tone_queue_t * tq, cw_queue_low_callback_t callback_func, void * callback_arg, size_t level);
bool cw_tq_is_nonempty_internal(const cw_tone_queue_t * tq);
cw_ret_t cw_tq_wait_for_end_of_current_tone_internal(cw_tone_queue_t * tq);
void cw_tq_reset_internal(cw_tone_queue_t * tq);
bool cw_tq_is_full_internal(const cw_tone_queue_t * tq);

cw_ret_t cw_tq_remove_last_character_internal(cw_tone_queue_t * tq);




#if defined(__cplusplus)
}
#endif




#endif /* #ifndef H_LIBCW_TQ */
