#include "config.h"
#include "config-api.h"
#include "dspmath.h"
#include "errors.h"
#include "midi.h"
#include "module.h"
#include "rt.h"
#include "sampler.h"
#include "sampler_impl.h"

void sampler_prevoice_start(struct sampler_prevoice *pv, struct sampler_channel *channel, struct sampler_layer_data *l, int note, int vel)
{
    pv->channel = channel;
    pv->layer_data = l;
    pv->note = note;
    pv->vel = vel;
    pv->age = 0;
    pv->delay_computed = 0.f;
    pv->sync_beats = -1;
    pv->sync_initial_time = -1;
    pv->sync_trigger_time = -1;

    for(struct sampler_noteinitfunc *nif = pv->layer_data->prevoice_nifs; nif; nif = nif->next)
        nif->key.notefunc_prevoice(nif, pv);
    sampler_prevoice_unlink(&channel->module->prevoices_free, pv);
    sampler_prevoice_link(&channel->module->prevoices_running, pv);
}

void sampler_prevoice_link(struct sampler_prevoice **pv, struct sampler_prevoice *v)
{
    v->prev = NULL;
    v->next = *pv;
    if (*pv)
        (*pv)->prev = v;
    *pv = v;
}

void sampler_prevoice_unlink(struct sampler_prevoice **pv, struct sampler_prevoice *v)
{
    if (*pv == v)
        *pv = v->next;
    if (v->prev)
        v->prev->next = v->next;
    if (v->next)
        v->next->prev = v->prev;
    v->prev = NULL;
    v->next = NULL;
}

int sampler_prevoice_process(struct sampler_prevoice *pv, struct sampler_module *m)
{
    struct sampler_layer_data *layer_data = pv->layer_data;
    if (pv->sync_beats != -1)
    {
        double cur_beat = sampler_get_current_beat(m);

        if (cur_beat < pv->sync_initial_time - 0.001 || cur_beat >= pv->sync_trigger_time + 1)
        {
            gboolean backward_jump = cur_beat < pv->sync_initial_time;
            // printf("Recalc: time %f, initial %f, delta %f, trigger %f\n", cur_beat, pv->sync_initial_time, cur_beat - pv->sync_initial_time, pv->sync_trigger_time);
            // Recalculate after seek/looping etc
            pv->sync_initial_time = cur_beat;
            double cur_rel_beat = fmod(cur_beat, pv->sync_beats);
            double bar_start = cur_beat - cur_rel_beat;
            if (pv->layer_data->sync_offset <= cur_rel_beat && !backward_jump) // trigger in next bar
                pv->sync_trigger_time = bar_start + pv->sync_beats + pv->layer_data->sync_offset;
            else // trigger in the same bar
                pv->sync_trigger_time = bar_start + pv->layer_data->sync_offset;
        }
        if (cur_beat < pv->sync_trigger_time)
            return 0;
        // Let the other logic (note delay etc.) take over
        pv->sync_beats = -1;
    }
    pv->age += CBOX_BLOCK_SIZE;
    if (pv->age >= (layer_data->delay + pv->delay_computed) * m->module.srate)
        return 1;
    
    return 0;
}

