/*
Calf Box, an open source musical instrument.
Copyright (C) 2010-2011 Krzysztof Foltman

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "auxbus.h"
#include "config-api.h"
#include "engine.h"
#include "errors.h"
#include "instr.h"
#include "io.h"
#include "layer.h"
#include "master.h"
#include "midi.h"
#include "module.h"
#include "pattern.h"
#include "rt.h"
#include "scene.h"
#include "seq.h"
#include <assert.h>
#include <glib.h>

CBOX_CLASS_DEFINITION_ROOT(cbox_scene)

static gboolean cbox_scene_addlayercmd(struct cbox_scene *s, struct cbox_command_target *fb, struct cbox_osc_command *cmd, int cmd_type, GError **error)
{
    int pos = CBOX_ARG_I(cmd, 0);
    if (pos < 0 || pos > (int)(1 + s->layer_count))
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for append)", pos, 1 + s->layer_count);
        return FALSE;
    }
    if (pos == 0)
        pos = s->layer_count;
    else
        pos--;
    struct cbox_layer *layer = NULL;
    
    switch(cmd_type)
    {
    case 1:
        layer = cbox_layer_new_from_config(s, CBOX_ARG_S(cmd, 1), error);
        break;
    case 2:
        layer = cbox_layer_new_with_instrument(s, CBOX_ARG_S(cmd, 1), error);
        break;
    case 3:
        {
            struct cbox_instrument *instr = cbox_scene_create_instrument(s, CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2), error);
            if (!instr)
                return FALSE;
            layer = cbox_layer_new_with_instrument(s, CBOX_ARG_S(cmd, 1), error);
            break;
        }
    case 4:
        layer = cbox_layer_new(s);
        if (cbox_uuid_fromstring(&layer->external_output, CBOX_ARG_S(cmd, 1), error))
        {
            layer->external_output_set = TRUE;
        }
        else
        {
            CBOX_DELETE(layer);
            return FALSE;
        }
        break;
    default:
        assert(0);
        break;
    }
    if (!layer)
        return FALSE;
    if (!cbox_scene_insert_layer(s, layer, pos, error))
    {
        CBOX_DELETE(layer);
        return FALSE;
    }
    if (fb)
    {
        if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, layer))
            return FALSE;
    }
    return TRUE;
}

static gboolean cbox_scene_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
{
    struct cbox_scene *s = ct->user_data;
    const char *subcommand = NULL;
    char *subobj = NULL;
    int index = 0;
    
    if (!strcmp(cmd->command, "/transpose") && !strcmp(cmd->arg_types, "i"))
    {
        s->transpose = CBOX_ARG_I(cmd, 0);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/load") && !strcmp(cmd->arg_types, "s"))
    {
        if (!cbox_scene_load(s, CBOX_ARG_S(cmd, 0), error))
            return FALSE;
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/clear") && !strcmp(cmd->arg_types, ""))
    {
        cbox_scene_clear(s);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/add_layer") && !strcmp(cmd->arg_types, "is"))
    {
        return cbox_scene_addlayercmd(s, fb, cmd, 1, error);
    }
    else if (!strcmp(cmd->command, "/add_instrument_layer") && !strcmp(cmd->arg_types, "is"))
    {
        return cbox_scene_addlayercmd(s, fb, cmd, 2, error);
    }
    else if (!strcmp(cmd->command, "/add_new_instrument_layer") && !strcmp(cmd->arg_types, "iss"))
    {
        return cbox_scene_addlayercmd(s, fb, cmd, 3, error);
    }
    else if (!strcmp(cmd->command, "/add_midi_layer") && !strcmp(cmd->arg_types, "is"))
    {
        return cbox_scene_addlayercmd(s, fb, cmd, 4, error);
    }
    else if (!strcmp(cmd->command, "/delete_layer") && !strcmp(cmd->arg_types, "i"))
    {
        int pos = CBOX_ARG_I(cmd, 0);
        if (pos < 0 || pos > (int)s->layer_count)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for last)", pos, s->layer_count);
            return FALSE;
        }
        if (pos == 0)
            pos = s->layer_count - 1;
        else
            pos--;
        struct cbox_layer *layer = cbox_scene_remove_layer(s, pos);
        CBOX_DELETE(layer);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/move_layer") && !strcmp(cmd->arg_types, "ii"))
    {
        int oldpos = CBOX_ARG_I(cmd, 0);
        if (oldpos < 1 || oldpos > (int)s->layer_count)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", oldpos, s->layer_count);
            return FALSE;
        }
        int newpos = CBOX_ARG_I(cmd, 1);
        if (newpos < 1 || newpos > (int)s->layer_count)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", newpos, s->layer_count);
            return FALSE;
        }
        cbox_scene_move_layer(s, oldpos - 1, newpos - 1);
        return TRUE;
    }
    else if (cbox_parse_path_part_int(cmd, "/layer/", &subcommand, &index, 1, s->layer_count, error))
    {
        if (!subcommand)
            return FALSE;
        return cbox_execute_sub(&s->layers[index - 1]->cmd_target, fb, cmd, subcommand, error);
    }
    else if (cbox_parse_path_part_str(cmd, "/aux/", &subcommand, &subobj, error))
    {
        if (!subcommand)
            return FALSE;
        struct cbox_aux_bus *aux = cbox_scene_get_aux_bus(s, subobj, FALSE, error);
        g_free(subobj);
        if (!aux)
            return FALSE;
        return cbox_execute_sub(&aux->cmd_target, fb, cmd, subcommand, error);
    }
    else if (!strncmp(cmd->command, "/instr/", 7))
    {
        const char *obj = &cmd->command[1];
        const char *pos = strchr(obj, '/');
        obj = &pos[1];
        pos = strchr(obj, '/');
        if (!pos)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid instrument path '%s'", cmd->command);
            return FALSE;
        }
        int len = pos - obj;
        
        gchar *name = g_strndup(obj, len);
        struct cbox_instrument *instr = cbox_scene_get_instrument_by_name(s, name, FALSE, error);
        if (instr)
        {
            g_free(name);
            
            return cbox_execute_sub(&instr->cmd_target, fb, cmd, pos, error);
        }
        else
        {
            cbox_force_error(error);
            g_prefix_error(error, "Cannot access instrument '%s': ", name);
            g_free(name);
            return FALSE;
        }
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/load_aux") && !strcmp(cmd->arg_types, "s"))
    {
        struct cbox_aux_bus *bus = cbox_scene_get_aux_bus(s, CBOX_ARG_S(cmd, 0), TRUE, error);
        if (!bus)
            return FALSE;
        if (fb)
        {
            if (!cbox_execute_on(fb, NULL, "/uuid", "o", error, bus))
                return FALSE;
        }
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/delete_aux") && !strcmp(cmd->arg_types, "s"))
    {
        const char *name = CBOX_ARG_S(cmd, 0);
        struct cbox_aux_bus *aux = cbox_scene_get_aux_bus(s, name, FALSE, error);
        if (!aux)
            return FALSE;
        CBOX_DELETE(aux);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
    {
        if (!cbox_check_fb_channel(fb, cmd->command, error))
            return FALSE;

        if (!cbox_execute_on(fb, NULL, "/name", "s", error, s->name) || 
            !cbox_execute_on(fb, NULL, "/title", "s", error, s->title) ||
            !cbox_execute_on(fb, NULL, "/transpose", "i", error, s->transpose) ||
            !cbox_execute_on(fb, NULL, "/enable_default_song_input", "i", error, s->enable_default_song_input) ||
            !cbox_execute_on(fb, NULL, "/enable_default_external_input", "i", error, s->enable_default_external_input) ||
            !CBOX_OBJECT_DEFAULT_STATUS(s, fb, error))
            return FALSE;
        
        for (uint32_t i = 0; i < s->layer_count; i++)
        {
            if (!cbox_execute_on(fb, NULL, "/layer", "o", error, s->layers[i]))
                return FALSE;
        }
        for (uint32_t i = 0; i < s->instrument_count; i++)
        {
            if (!cbox_execute_on(fb, NULL, "/instrument", "sso", error, s->instruments[i]->module->instance_name, s->instruments[i]->module->engine_name, s->instruments[i]))
                return FALSE;
        }
        for (uint32_t i = 0; i < s->aux_bus_count; i++)
        {
            if (!cbox_execute_on(fb, NULL, "/aux", "so", error, s->aux_buses[i]->name, s->aux_buses[i]))
                return FALSE;
        }
        return TRUE;
    }
    else
    if (!strcmp(cmd->command, "/send_event") && (!strcmp(cmd->arg_types, "iii") || !strcmp(cmd->arg_types, "ii") || !strcmp(cmd->arg_types, "i")))
    {
        int mcmd = CBOX_ARG_I(cmd, 0);
        int arg1 = 0, arg2 = 0;
        if (cmd->arg_types[1] == 'i')
        {
            arg1 = CBOX_ARG_I(cmd, 1);
            if (cmd->arg_types[2] == 'i')
                arg2 = CBOX_ARG_I(cmd, 2);
        }
        struct cbox_midi_buffer buf;
        cbox_midi_buffer_init(&buf);
        cbox_midi_buffer_write_inline(&buf, 0, mcmd, arg1, arg2);
        cbox_midi_merger_push(&s->scene_input_merger, &buf, s->rt);
        return TRUE;
    }
    else
    if (!strcmp(cmd->command, "/play_note") && !strcmp(cmd->arg_types, "iii"))
    {
        int channel = CBOX_ARG_I(cmd, 0);
        int note = CBOX_ARG_I(cmd, 1);
        int velocity = CBOX_ARG_I(cmd, 2);
        struct cbox_midi_buffer buf;
        cbox_midi_buffer_init(&buf);
        cbox_midi_buffer_write_inline(&buf, 0, 0x90 + ((channel - 1) & 15), note & 127, velocity & 127);
        cbox_midi_buffer_write_inline(&buf, 1, 0x80 + ((channel - 1) & 15), note & 127, velocity & 127);
        cbox_midi_merger_push(&s->scene_input_merger, &buf, s->rt);
        return TRUE;
    }
    else
    if (!strcmp(cmd->command, "/play_pattern") && !strcmp(cmd->arg_types, "sfi"))
    {
        struct cbox_midi_pattern *pattern = (struct cbox_midi_pattern *)CBOX_ARG_O(cmd, 0, s, cbox_midi_pattern, error);
        if (!pattern)
            return FALSE;
        
        struct cbox_adhoc_pattern *ap = cbox_adhoc_pattern_new(s->engine, CBOX_ARG_I(cmd, 2), pattern);
        ap->master->tempo = ap->master->new_tempo = CBOX_ARG_F(cmd, 1);
        cbox_scene_play_adhoc_pattern(s, ap);
        return TRUE;
    }
    else
    if (!strcmp(cmd->command, "/enable_default_song_input") && !strcmp(cmd->arg_types, "i"))
    {
        s->enable_default_song_input = CBOX_ARG_I(cmd, 0);
        cbox_scene_update_connected_inputs(s);
        return TRUE;
    }
    else
    if (!strcmp(cmd->command, "/enable_default_external_input") && !strcmp(cmd->arg_types, "i"))
    {
        s->enable_default_external_input = CBOX_ARG_I(cmd, 0);
        cbox_scene_update_connected_inputs(s);
        return TRUE;
    }
    else
        return cbox_object_default_process_cmd(ct, fb, cmd, error);
}

gboolean cbox_scene_load(struct cbox_scene *s, const char *name, GError **error)
{
    const char *cv = NULL;
    int i;
    gchar *section = g_strdup_printf("scene:%s", name);
    
    if (!cbox_config_has_section(section))
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No config section for scene '%s'", name);
        goto error;
    }
    
    cbox_scene_clear(s);
    
    assert(s->layers == NULL);
    assert(s->instruments == NULL);
    assert(s->aux_buses == NULL);
    assert(s->layer_count == 0);
    assert(s->instrument_count == 0);
    assert(s->aux_bus_count == 0);
    
    for (i = 1; ; i++)
    {
        struct cbox_layer *l = NULL;
        
        gchar *sn = g_strdup_printf("layer%d", i);
        cv = cbox_config_get_string(section, sn);
        g_free(sn);
        
        if (!cv)
            break;
        
        l = cbox_layer_new_from_config(s, cv, error);
        if (!l)
            goto error;
        
        if (!cbox_scene_add_layer(s, l, error))
            goto error;
    }
    
    s->transpose = cbox_config_get_int(section, "transpose", 0);
    s->title = g_strdup(cbox_config_get_string_with_default(section, "title", ""));
    g_free(section);
    cbox_command_target_init(&s->cmd_target, cbox_scene_process_cmd, s);
    s->name = g_strdup(name);
    return TRUE;

error:
    g_free(section);
    return FALSE;
}

gboolean cbox_scene_insert_layer(struct cbox_scene *scene, struct cbox_layer *layer, int pos, GError **error)
{
    uint32_t i;
    
    struct cbox_instrument *instrument = layer->instrument;
    
    if (instrument) {
        for (i = 0; i < instrument->aux_output_count; i++)
        {
            assert(!instrument->aux_outputs[i]);
            if (instrument->aux_output_names[i])
            {
                instrument->aux_outputs[i] = cbox_scene_get_aux_bus(scene, instrument->aux_output_names[i], TRUE, error);
                if (!instrument->aux_outputs[i])
                    return FALSE;
                cbox_aux_bus_ref(instrument->aux_outputs[i]);
            }
        }
        for (i = 0; i < scene->layer_count; i++)
        {
            if (scene->layers[i]->instrument == layer->instrument)
                break;
        }
        if (i == scene->layer_count)
        {
            layer->instrument->scene = scene;
            cbox_rt_array_insert(scene->rt, (void ***)&scene->instruments, &scene->instrument_count, -1, layer->instrument);
        }
    }
    cbox_rt_array_insert(scene->rt, (void ***)&scene->layers, &scene->layer_count, pos, layer);
    if (layer->external_output_set && scene->rt)
        cbox_scene_update_connected_outputs(scene);
    
    return TRUE;
}

gboolean cbox_scene_add_layer(struct cbox_scene *scene, struct cbox_layer *layer, GError **error)
{
    return cbox_scene_insert_layer(scene, layer, scene->layer_count, error);
}

struct cbox_layer *cbox_scene_remove_layer(struct cbox_scene *scene, int pos)
{
    struct cbox_layer *removed = scene->layers[pos];
    cbox_rt_array_remove(scene->rt, (void ***)&scene->layers, &scene->layer_count, pos);
    if (removed->instrument)
        cbox_instrument_unref_aux_buses(removed->instrument);
    if (removed->external_output_set)
        cbox_scene_update_connected_outputs(scene);
    
    return removed;
}

void cbox_scene_move_layer(struct cbox_scene *scene, unsigned int oldpos, unsigned int newpos)
{
    if (oldpos == newpos)
        return;
    struct cbox_layer **layers = malloc(sizeof(struct cbox_layer *) * scene->layer_count);
    for (uint32_t i = 0; i < scene->layer_count; i++)
    {
        int s;
        if (i == newpos)
            s = oldpos;
        else
        {
            if (oldpos < newpos)
                s = (i < oldpos || i > newpos) ? i : i + 1;
            else
                s = (i < newpos || i > oldpos) ? i : i - 1;
        }
        layers[i] = scene->layers[s];
    }
    free(cbox_rt_swap_pointers(scene->rt, (void **)&scene->layers, layers));
}

gboolean cbox_scene_remove_instrument(struct cbox_scene *scene, struct cbox_instrument *instrument)
{
    assert(instrument->scene == scene);
    for (uint32_t pos = 0; pos < scene->instrument_count; pos++)
    {
        if (scene->instruments[pos] == instrument)
        {
            cbox_rt_array_remove(scene->rt, (void ***)&scene->instruments, &scene->instrument_count, pos);            
            g_hash_table_remove(scene->instrument_hash, instrument->module->instance_name);
            instrument->scene = NULL;
            return TRUE;
        }
    }
    return FALSE;
}

gboolean cbox_scene_insert_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *aux_bus)
{
    struct cbox_aux_bus **aux_buses = malloc(sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count + 1));
    memcpy(aux_buses, scene->aux_buses, sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count));
    aux_buses[scene->aux_bus_count] = aux_bus;
    free(cbox_rt_swap_pointers_and_update_count(scene->rt, (void **)&scene->aux_buses, aux_buses, &scene->aux_bus_count, scene->aux_bus_count + 1));
    return TRUE;
}

void cbox_scene_remove_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *removed)
{
    int pos = -1;
    for (uint32_t i = 0; i < scene->aux_bus_count; i++)
    {
        if (scene->aux_buses[i] == removed)
        {
            pos = i;
            break;
        }
    }
    assert(pos != -1);
    for (uint32_t i = 0; i < scene->instrument_count; i++)
        cbox_instrument_disconnect_aux_bus(scene->instruments[i], removed);
    
    struct cbox_aux_bus **aux_buses = malloc(sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count - 1));
    memcpy(aux_buses, scene->aux_buses, sizeof(struct cbox_aux_bus *) * pos);
    memcpy(aux_buses + pos, scene->aux_buses + pos + 1, sizeof(struct cbox_aux_bus *) * (scene->aux_bus_count - pos - 1));
    free(cbox_rt_swap_pointers_and_update_count(scene->rt, (void **)&scene->aux_buses, aux_buses, &scene->aux_bus_count, scene->aux_bus_count - 1));
}

struct cbox_aux_bus *cbox_scene_get_aux_bus(struct cbox_scene *scene, const char *name, int allow_load, GError **error)
{
    for (uint32_t i = 0; i < scene->aux_bus_count; i++)
    {
        if (!strcmp(scene->aux_buses[i]->name, name))
        {
            return scene->aux_buses[i];
        }
    }
    if (!allow_load)
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Aux bus not found: %s", name);
        return FALSE;
    }
    struct cbox_aux_bus *bus = cbox_aux_bus_load(scene, name, scene->rt, error);
    if (!bus)
        return NULL;
    return bus;
}

static int write_events_to_instrument_ports(struct cbox_scene *scene, struct cbox_midi_buffer *source)
{
    uint32_t i;

    for (i = 0; i < scene->instrument_count; i++)
        cbox_midi_buffer_clear(&scene->instruments[i]->module->midi_input);
    for (uint32_t l = 0; l < scene->layer_count; l++)
    {
        struct cbox_layer *lp = scene->layers[l];
        if (lp->external_output_set)
            cbox_midi_buffer_clear(&lp->output_buffer);
    }

    if (!source)
        return 0;

    uint32_t event_count = cbox_midi_buffer_get_count(source);
    for (i = 0; i < event_count; i++)
    {
        const struct cbox_midi_event *event = cbox_midi_buffer_get_event(source, i);
        
        // XXXKF ignore sysex for now
        if (event->size >= 4)
            continue;
        
        for (uint32_t l = 0; l < scene->layer_count; l++)
        {
            struct cbox_layer *lp = scene->layers[l];
            if (!lp->enabled)
                continue;
            if (!lp->external_output_set && !lp->instrument)
                continue;
            uint8_t data[4] = {0, 0, 0, 0};
            memcpy(data, event->data_inline, event->size);
            if (data[0] < 0xF0) // per-channel messages
            {
                int cmd = data[0] >> 4;
                // filter on MIDI channel
                if (lp->in_channel >= 0 && lp->in_channel != (data[0] & 0x0F))
                    continue;
                // force output channel
                if (lp->out_channel >= 0)
                    data[0] = (data[0] & 0xF0) + (lp->out_channel & 0x0F);
                if (cmd >= 8 && cmd <= 10)
                {
                    if (cmd == 10 && lp->disable_aftertouch)
                        continue;
                    // note filter
                    if (data[1] < lp->low_note || data[1] > lp->high_note)
                        continue;
                    // transpose
                    int transpose = lp->transpose + (lp->ignore_scene_transpose ? 0 : scene->transpose);
                    if (transpose)
                    {
                        int note = data[1] + transpose;
                        if (note < 0 || note > 127)
                            continue;
                        data[1] = (uint8_t)note;
                    }
                    // fixed note
                    if (lp->fixed_note != -1)
                    {
                        data[1] = (uint8_t)lp->fixed_note;
                    }
                }
                else if (cmd == 11 && data[1] == 64 && lp->invert_sustain)
                {
                    data[2] = 127 - data[2];
                }
                else if (lp->ignore_program_changes && cmd == 11 && (data[1] == 0 || data[1] == 32))
                    continue;
                else if (cmd == 13 && lp->disable_aftertouch)
                    continue;
                else if (cmd == 12 && lp->ignore_program_changes)
                    continue;
            }
            struct cbox_midi_buffer *output = lp->instrument ? &lp->instrument->module->midi_input : &lp->output_buffer;
            if (!cbox_midi_buffer_write_event(output, event->time, data, event->size))
                return -i;
            if (lp->consume)
                break;
        }
    }
    
    return event_count;
}

void cbox_scene_update_connected_outputs(struct cbox_scene *scene)
{
    for (uint32_t l = 0; l < scene->layer_count; l++)
    {
        struct cbox_layer *lp = scene->layers[l];
        struct cbox_midi_merger *merger = NULL;
        if (lp->external_output_set)
            merger = cbox_rt_get_midi_output(scene->engine->rt, &lp->external_output);
        if (merger != lp->external_merger)
        {
            if (lp->external_merger)
                cbox_midi_merger_disconnect(lp->external_merger, &lp->output_buffer, scene->rt);
            if (merger)
                cbox_midi_merger_connect(merger, &lp->output_buffer, scene->rt, &lp->external_merger);
        }
    }
}

void cbox_scene_render(struct cbox_scene *scene, uint32_t nframes, float *output_buffers[], uint32_t output_channels)
{
    uint32_t i, n;

    if (scene->rt && scene->rt->io)
    {
        struct cbox_io *io = scene->rt->io;
        for (i = 0; i < io->io_env.input_count; i++)
        {
            if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_mono_inputs[i]))
                cbox_recording_source_push(&scene->rec_mono_inputs[i], (const float **)&io->input_buffers[i], 0, nframes);
        }
        for (i = 0; i < io->io_env.input_count / 2; i++)
        {
            if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_stereo_inputs[i]))
            {
                const float *buf[2] = { io->input_buffers[i * 2], io->input_buffers[i * 2 + 1] };
                cbox_recording_source_push(&scene->rec_stereo_inputs[i], buf, 0, nframes);
            }
        }
    }
    
    for(struct cbox_adhoc_pattern **ppat = &scene->adhoc_patterns; *ppat; )
    {
        cbox_midi_buffer_clear(&(*ppat)->output_buffer);
        if ((*ppat)->completed)
        {
            struct cbox_adhoc_pattern *retired = *ppat;
            *ppat = retired->next;
            retired->next = scene->retired_adhoc_patterns;
            scene->retired_adhoc_patterns = retired;
        }
        else
        {
            cbox_adhoc_pattern_render((*ppat), 0, nframes);
            ppat = &((*ppat)->next);
        }
    }
    
    cbox_midi_buffer_clear(&scene->midibuf_total);
    cbox_midi_merger_render(&scene->scene_input_merger);

    write_events_to_instrument_ports(scene, &scene->midibuf_total);

    for (n = 0; n < scene->aux_bus_count; n++)
    {
        for (i = 0; i < nframes; i ++)
        {
            scene->aux_buses[n]->input_bufs[0][i] = 0.f;
            scene->aux_buses[n]->input_bufs[1][i] = 0.f;
        }
    }
    
    for (n = 0; n < scene->instrument_count; n++)
    {
        struct cbox_instrument *instr = scene->instruments[n];
        struct cbox_module *module = instr->module;
        int event_count = instr->module->midi_input.count;
        int cur_event = 0;
        uint32_t highwatermark = 0;
        cbox_sample_t channels[CBOX_MAX_AUDIO_PORTS][CBOX_BLOCK_SIZE];
        cbox_sample_t *outputs[CBOX_MAX_AUDIO_PORTS];
        for (i= 0; i < module->outputs; i++)
            outputs[i] = channels[i];
        
        for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE)
        {
            scene->engine->song_pos_offset = i;
            if (i >= highwatermark)
            {
                while(cur_event < event_count)
                {
                    const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&module->midi_input, cur_event);
                    if (event)
                    {
                        if (event->time <= i)
                            (*module->process_event)(module, cbox_midi_event_get_data(event), event->size);
                        else
                        {
                            highwatermark = event->time;
                            break;
                        }
                    }
                    else
                        break;
                    
                    cur_event++;
                }
            }
            (*module->process_block)(module, NULL, outputs);
            for (uint32_t o = 0; o < module->outputs / 2; o++)
            {
                struct cbox_instrument_output *oobj = &instr->outputs[o];
                struct cbox_module *insert = oobj->insert;
                struct cbox_gain *gain_obj = &oobj->gain_obj;
                if (IS_RECORDING_SOURCE_CONNECTED(oobj->rec_dry))
                    cbox_recording_source_push(&oobj->rec_dry, (const float **)(outputs + 2 * o), i, CBOX_BLOCK_SIZE);
                if (insert && !insert->bypass)
                    (*insert->process_block)(insert, outputs + 2 * o, outputs + 2 * o);
                if (IS_RECORDING_SOURCE_CONNECTED(oobj->rec_wet))
                    cbox_recording_source_push(&oobj->rec_wet, (const float **)(outputs + 2 * o), i, CBOX_BLOCK_SIZE);
                float *leftbuf, *rightbuf;
                if (o < module->aux_offset / 2)
                {
                    if (oobj->output_bus < 0)
                        continue;
                    uint32_t leftch = oobj->output_bus * 2;
                    if (leftch >= output_channels)
                        continue;
                    leftbuf = output_buffers[leftch];
                    uint32_t rightch = leftch + 1;
                    rightbuf = rightch >= output_channels ? NULL : output_buffers[rightch];
                }
                else
                {
                    int bus = o - module->aux_offset / 2;
                    struct cbox_aux_bus *busobj = instr->aux_outputs[bus];
                    if (busobj == NULL)
                        continue;
                    leftbuf = busobj->input_bufs[0];
                    rightbuf = busobj->input_bufs[1];
                }
                if (leftbuf && rightbuf)
                {
                    cbox_gain_add_stereo(gain_obj, &leftbuf[i], channels[2 * o], &rightbuf[i], channels[2 * o + 1], CBOX_BLOCK_SIZE);
                }
                else
                {
                    if (leftbuf)
                        cbox_gain_add_mono(gain_obj, &leftbuf[i], channels[2 * o], CBOX_BLOCK_SIZE);
                    if (rightbuf)
                        cbox_gain_add_mono(gain_obj, &rightbuf[i], channels[2 * o + 1], CBOX_BLOCK_SIZE);
                }
            }
        }
        while(cur_event < event_count)
        {
            const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&module->midi_input, cur_event);
            if (event)
            {
                (*module->process_event)(module, cbox_midi_event_get_data(event), event->size);
            }
            else
                break;
            
            cur_event++;
        }
    }
    
    for (n = 0; n < scene->aux_bus_count; n++)
    {
        struct cbox_aux_bus *bus = scene->aux_buses[n];
        float left[CBOX_BLOCK_SIZE], right[CBOX_BLOCK_SIZE];
        float *outputs[2] = {left, right};
        for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE)
        {
            float *inputs[2];
            inputs[0] = &bus->input_bufs[0][i];
            inputs[1] = &bus->input_bufs[1][i];
            bus->module->process_block(bus->module, inputs, outputs);
            for (int j = 0; j < CBOX_BLOCK_SIZE; j++)
            {
                output_buffers[0][i + j] += left[j];
                output_buffers[1][i + j] += right[j];
            }
        }
    }

    uint32_t output_count = scene->engine->io_env.output_count;
    // XXXKF this assumes that the buffers are zeroed on start - which isn't true if there are multiple scenes
    for (i = 0; i < output_count; i++)
    {
        if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_mono_outputs[i]))
            cbox_recording_source_push(&scene->rec_mono_outputs[i], (const float **)&output_buffers[i], 0, nframes);
    }
    for (i = 0; i < output_count / 2; i++)
    {
        if (IS_RECORDING_SOURCE_CONNECTED(scene->rec_stereo_outputs[i]))
        {
            const float *buf[2] = { output_buffers[i * 2], output_buffers[i * 2 + 1] };
            cbox_recording_source_push(&scene->rec_stereo_outputs[i], buf, 0, nframes);
        }
    }
}

void cbox_scene_clear(struct cbox_scene *scene)
{
    g_free(scene->name);
    g_free(scene->title);
    scene->name = g_strdup("");
    scene->title = g_strdup("");
    while(scene->layer_count > 0)
    {
        struct cbox_layer *layer = cbox_scene_remove_layer(scene, 0);
        CBOX_DELETE(layer);
    }
            
    while(scene->aux_bus_count > 0)
        CBOX_DELETE(scene->aux_buses[scene->aux_bus_count - 1]);
}

static struct cbox_instrument *create_instrument(struct cbox_scene *scene, struct cbox_module *module)
{
    int auxes = (module->outputs - module->aux_offset) / 2;

    struct cbox_instrument *instr = malloc(sizeof(struct cbox_instrument));
    CBOX_OBJECT_HEADER_INIT(instr, cbox_instrument, CBOX_GET_DOCUMENT(scene));
    instr->scene = scene;
    instr->module = module;
    instr->outputs = calloc(module->outputs / 2, sizeof(struct cbox_instrument_output));
    instr->refcount = 0;
    instr->aux_outputs = calloc(auxes, sizeof(struct cbox_aux_bus *));
    instr->aux_output_names = calloc(auxes, sizeof(char *));
    instr->aux_output_count = auxes;

    for (uint32_t i = 0; i < module->outputs / 2; i ++)
        cbox_instrument_output_init(&instr->outputs[i], scene, module->engine->io_env.buffer_size);
    
    return instr;
}

struct cbox_instrument *cbox_scene_create_instrument(struct cbox_scene *scene, const char *instrument_name, const char *engine_name, GError **error)
{
    gpointer value = g_hash_table_lookup(scene->instrument_hash, instrument_name);
    if (value)
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Instrument already exists: '%s'", instrument_name);
        return NULL;
    }

    struct cbox_document *doc = CBOX_GET_DOCUMENT(scene);
    struct cbox_module_manifest *mptr = NULL;
    struct cbox_instrument *instr = NULL;
    struct cbox_module *module = NULL;
        
    mptr = cbox_module_manifest_get_by_name(engine_name);
    if (!mptr)
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No engine called '%s'", engine_name);
        return NULL;
    }

    module = cbox_module_manifest_create_module(mptr, NULL, doc, scene->rt, scene->engine, instrument_name, error);
    if (!module)
    {
        cbox_force_error(error);
        g_prefix_error(error, "Cannot create engine '%s' for instrument '%s': ", engine_name, instrument_name);
        return NULL;
    }
    
    instr = create_instrument(scene, module);
    
    cbox_command_target_init(&instr->cmd_target, cbox_instrument_process_cmd, instr);
    g_hash_table_insert(scene->instrument_hash, g_strdup(instrument_name), instr);
    CBOX_OBJECT_REGISTER(instr);
    
    return instr;
}

struct cbox_instrument *cbox_scene_get_instrument_by_name(struct cbox_scene *scene, const char *name, gboolean load, GError **error)
{
    struct cbox_module_manifest *mptr = NULL;
    struct cbox_instrument *instr = NULL;
    struct cbox_module *module = NULL;
    gchar *instr_section = NULL;
    gpointer value = g_hash_table_lookup(scene->instrument_hash, name);
    const char *cv, *instr_engine;
    struct cbox_document *doc = CBOX_GET_DOCUMENT(scene);
    assert(scene);
    
    if (value)
        return value;
    if (!load)
        return NULL;
    
    instr_section = g_strdup_printf("instrument:%s", name);
    
    if (!cbox_config_has_section(instr_section))
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No config section for instrument '%s'", name);
        goto error;
    }
    
    instr_engine = cbox_config_get_string(instr_section, "engine");
    if (!instr_engine)
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Engine not specified in instrument '%s'", name);
        goto error;
    }

    mptr = cbox_module_manifest_get_by_name(instr_engine);
    if (!mptr)
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No engine called '%s'", instr_engine);
        goto error;
    }
    
    // cbox_module_manifest_dump(mptr);
    
    module = cbox_module_manifest_create_module(mptr, instr_section, doc, scene->rt, scene->engine, name, error);
    if (!module)
    {
        cbox_force_error(error);
        g_prefix_error(error, "Cannot create engine '%s' for instrument '%s': ", instr_engine, name);
        goto error;
    }
    
    instr = create_instrument(scene, module);

    for (uint32_t i = 0; i < module->outputs / 2; i ++)
    {
        struct cbox_instrument_output *oobj = instr->outputs + i;
        
        gchar *key = i == 0 ? g_strdup("output_bus") : g_strdup_printf("output%d_bus", 1 + i);
        oobj->output_bus = cbox_config_get_int(instr_section, key, 1) - 1;
        g_free(key);
        key = i == 0 ? g_strdup("gain") : g_strdup_printf("gain%d", 1 + i);
        cbox_gain_set_db(&oobj->gain_obj, cbox_config_get_float(instr_section, key, 0));
        g_free(key);
        
        key = i == 0 ? g_strdup("insert") : g_strdup_printf("insert%d", 1 + i);
        cv = cbox_config_get_string(instr_section, key);
        g_free(key);
        
        if (cv)
        {
            oobj->insert = cbox_module_new_from_fx_preset(cv, CBOX_GET_DOCUMENT(scene), module->rt, scene->engine, error);
            if (!oobj->insert)
            {
                cbox_force_error(error);
                g_prefix_error(error, "Cannot instantiate effect preset '%s' for instrument '%s': ", cv, name);
            }
        }
    }

    for (uint32_t i = 0; i < instr->aux_output_count; i++)
    {
        instr->aux_outputs[i] = NULL;
        
        gchar *key = g_strdup_printf("aux%d", 1 + i);
        gchar *value = cbox_config_get_string(instr_section, key);
        instr->aux_output_names[i] = value ? g_strdup(value) : NULL;
        g_free(key);
        
    }
    cbox_command_target_init(&instr->cmd_target, cbox_instrument_process_cmd, instr);
    
    free(instr_section);
    
    g_hash_table_insert(scene->instrument_hash, g_strdup(name), instr);
    CBOX_OBJECT_REGISTER(instr);
    
    // cbox_recording_source_attach(&instr->outputs[0].rec_dry, cbox_recorder_new_stream("output.wav"));
    
    return instr;
    
error:
    free(instr_section);
    return NULL;
}

static struct cbox_recording_source *create_rec_sources(struct cbox_scene *scene, int buffer_size, int count, int channels)
{
    struct cbox_recording_source *s = malloc(sizeof(struct cbox_recording_source) * count);
    for (int i = 0; i < count; i++)
        cbox_recording_source_init(&s[i], scene, buffer_size, channels);
    return s;
}

static void destroy_rec_sources(struct cbox_recording_source *s, int count)
{
    for (int i = 0; i < count; i++)
        cbox_recording_source_uninit(&s[i]);
    free(s);
}

struct cbox_scene *cbox_scene_new(struct cbox_document *document, struct cbox_engine *engine)
{
    if (!engine->io_env.buffer_size)
        return NULL;

    struct cbox_scene *s = malloc(sizeof(struct cbox_scene));
    if (!s)
        return NULL;

    CBOX_OBJECT_HEADER_INIT(s, cbox_scene, document);
    s->engine = engine;
    s->rt = engine ? engine->rt : NULL;
    s->instrument_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    s->name = g_strdup("");
    s->title = g_strdup("");
    s->layers = NULL;
    s->aux_buses = NULL;
    s->instruments = NULL;
    s->layer_count = 0;
    s->instrument_count = 0;
    s->aux_bus_count = 0;
    cbox_command_target_init(&s->cmd_target, cbox_scene_process_cmd, s);
    s->transpose = 0;
    s->connected_inputs = NULL;
    s->connected_input_count = 0;
    s->enable_default_song_input = TRUE;
    s->enable_default_external_input = TRUE;

    cbox_midi_buffer_init(&s->midibuf_total);
    cbox_midi_merger_init(&s->scene_input_merger, &s->midibuf_total);

    int buffer_size = engine->io_env.buffer_size;
    s->rec_mono_inputs = create_rec_sources(s, buffer_size, engine->io_env.input_count, 1);
    s->rec_stereo_inputs = create_rec_sources(s, buffer_size, engine->io_env.input_count / 2, 2);
    s->rec_mono_outputs = create_rec_sources(s, buffer_size, engine->io_env.output_count, 1);
    s->rec_stereo_outputs = create_rec_sources(s, buffer_size, engine->io_env.output_count / 2, 2);
    s->adhoc_patterns = NULL;
    s->retired_adhoc_patterns = NULL;
    
    CBOX_OBJECT_REGISTER(s);
    
    cbox_engine_add_scene(s->engine, s);
    cbox_scene_update_connected_inputs(s);
    return s;
}

void cbox_scene_update_connected_inputs(struct cbox_scene *scene)
{
    if (!scene->rt || !scene->rt->io)
        return;
    
    // This is called when a MIDI Input port has been created, connected/disconnected
    // or is about to be removed (and then the removing flag will be set)
    for (uint32_t i = 0; i < scene->connected_input_count; )
    {
        struct cbox_midi_input *input = scene->connected_inputs[i];
        if (input->removing || !cbox_uuid_equal(&input->output, &scene->_obj_hdr.instance_uuid))
        {
            cbox_midi_merger_disconnect(&scene->scene_input_merger, &input->buffer, scene->rt);
            cbox_rt_array_remove(scene->rt, (void ***)&scene->connected_inputs, &scene->connected_input_count, i);
        }
        else
            i++;
    }
    for (GSList *p = scene->rt->io->midi_inputs; p; p = p->next)
    {
        struct cbox_midi_input *input = p->data;
        if (cbox_uuid_equal(&input->output, &scene->_obj_hdr.instance_uuid))
        {
            gboolean found = FALSE;
            for (uint32_t i = 0; i < scene->connected_input_count; i++)
            {
                if (scene->connected_inputs[i] == input)
                {
                    found = TRUE;
                    break;
                }
            }
            if (!found)
            {
                cbox_midi_merger_connect(&scene->scene_input_merger, &input->buffer, scene->rt, NULL);
                cbox_rt_array_insert(scene->rt, (void ***)&scene->connected_inputs, &scene->connected_input_count, -1, input);
            }
        }
    }
    if (scene->enable_default_song_input)
    {
        cbox_midi_merger_connect(&scene->scene_input_merger, &scene->engine->midibuf_aux, scene->rt, NULL);
        cbox_midi_merger_connect(&scene->scene_input_merger, &scene->engine->midibuf_song, scene->rt, NULL);
    }
    else
    {
        cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_aux, scene->rt);
        cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_song, scene->rt);
    }
    
    if (scene->enable_default_external_input)
        cbox_midi_merger_connect(&scene->scene_input_merger, &scene->engine->midibuf_jack, scene->rt, NULL);
    else
        cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_jack, scene->rt);
    
}

static void free_adhoc_pattern_list(struct cbox_scene *scene, struct cbox_adhoc_pattern *ap)
{
    while(ap)
    {
        struct cbox_adhoc_pattern *next = ap->next;
        ap->next = NULL;
        cbox_midi_merger_disconnect(&scene->scene_input_merger, &ap->output_buffer, scene->rt);
        cbox_adhoc_pattern_destroy(ap);
        ap = next;
    }
}

struct play_adhoc_pattern_arg
{
    struct cbox_scene *scene;
    struct cbox_adhoc_pattern *ap;
    struct cbox_adhoc_pattern *retired;
};

static int play_adhoc_pattern_execute(void *arg_)
{
    struct play_adhoc_pattern_arg *arg = arg_;
    
    if (arg->ap)
    {
        struct cbox_adhoc_pattern *ap = arg->scene->adhoc_patterns;
        
        // If there is already an adhoc pattern with a given non-zero id, stop it
        // and release all the pending notes. Retry until all the notes are
        // released.
        if (arg->ap->id)
        {
            while(ap && ap->id != arg->ap->id)
                ap = ap->next;
            if (ap)
            {
                ap->completed = TRUE;
                if (ap->active_notes.channels_active)
                    return 0;
            }
        }
        
        arg->ap->next = arg->scene->adhoc_patterns;
        arg->scene->adhoc_patterns = arg->ap;
    }
    arg->retired = arg->scene->retired_adhoc_patterns;
    arg->scene->retired_adhoc_patterns = NULL;
    // XXXKF should convert pattern length into sample position instead of assuming 0x7FFFFFFF (though it likely doesn't matter)
    cbox_midi_clip_playback_set_pattern(&arg->ap->playback, arg->ap->pattern_playback, 0, 0x7FFFFFFF, 0, 0);
    return 1;
}

void cbox_scene_play_adhoc_pattern(struct cbox_scene *scene, struct cbox_adhoc_pattern *ap)
{
    static struct cbox_rt_cmd_definition cmd = { NULL, play_adhoc_pattern_execute, NULL };
    struct play_adhoc_pattern_arg arg = { scene, ap, NULL };
    cbox_midi_merger_connect(&scene->scene_input_merger, &ap->output_buffer, scene->rt, NULL);
    cbox_rt_execute_cmd_sync(scene->rt, &cmd, &arg);
    if (arg.retired)
        free_adhoc_pattern_list(scene, arg.retired);
}

gboolean cbox_scene_move_instrument_to(struct cbox_scene *scene, struct cbox_instrument *instrument, struct cbox_scene *new_scene, int dstpos, GError **error)
{
    int lcount = 0;
    if (dstpos == -1)
        dstpos = new_scene->layer_count;
    for (uint32_t i = 0; i < scene->layer_count; i++)
    {
        if (scene->layers[i]->instrument == instrument)
            lcount++;
    }
    if (!lcount)
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Instrument '%s' not found in source scene", instrument->module->instance_name);
        return FALSE;
    }
    if (cbox_scene_get_instrument_by_name(new_scene, instrument->module->instance_name, FALSE, NULL))
    {
        g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Instrument '%s' already exists in target scene", instrument->module->instance_name);
        return FALSE;
    }
    
    struct cbox_layer **new_src_layers = malloc(sizeof(struct cbox_layer *) * (scene->layer_count - lcount));
    struct cbox_layer **new_dst_layers = malloc(sizeof(struct cbox_layer *) * (new_scene->layer_count + lcount));
    int srcidx = 0, dstidx = 0;
    memcpy(&new_dst_layers[dstidx], new_scene->layers, dstpos * sizeof(struct cbox_layer **));
    dstidx = dstpos;
    for (uint32_t i = 0; i < scene->layer_count; i++)
    {
        if (scene->layers[i]->instrument != instrument)
            new_src_layers[srcidx++] = scene->layers[i];
        else
            new_dst_layers[dstidx++] = scene->layers[i];
    }
    memcpy(&new_dst_layers[dstidx], new_scene->layers, (new_scene->layer_count - dstpos) * sizeof(struct cbox_layer **));
    dstidx += new_scene->layer_count;

    free(cbox_rt_swap_pointers_and_update_count(scene->rt, (void **)&scene->layers, new_src_layers, &scene->layer_count, srcidx));
    cbox_rt_array_remove_by_value(scene->rt, (void ***)&scene->instruments, &scene->instrument_count, instrument);

    cbox_rt_array_insert(scene->rt, (void ***)&new_scene->instruments, &new_scene->instrument_count, -1, instrument);
    free(cbox_rt_swap_pointers_and_update_count(new_scene->rt, (void **)&new_scene->layers, new_dst_layers, &new_scene->layer_count, dstidx));

    return TRUE;
}

static void cbox_scene_destroyfunc(struct cbox_objhdr *objhdr)
{
    struct cbox_scene *scene = CBOX_H2O(objhdr);
    cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_aux,  scene->rt);
    cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_jack, scene->rt);
    cbox_midi_merger_disconnect(&scene->scene_input_merger, &scene->engine->midibuf_song, scene->rt);
    cbox_engine_remove_scene(scene->engine, scene);
    cbox_scene_clear(scene);
    g_free(scene->name);
    g_free(scene->title);
    assert(scene->instrument_count == 0);
    free(scene->layers);
    free(scene->aux_buses);
    free(scene->instruments);
    g_hash_table_destroy(scene->instrument_hash);
    free(scene->connected_inputs);

    destroy_rec_sources(scene->rec_mono_inputs, scene->engine->io_env.input_count);
    destroy_rec_sources(scene->rec_stereo_inputs, scene->engine->io_env.input_count / 2);
    destroy_rec_sources(scene->rec_mono_outputs, scene->engine->io_env.output_count);
    destroy_rec_sources(scene->rec_stereo_outputs, scene->engine->io_env.output_count / 2);
    
    free_adhoc_pattern_list(scene, scene->retired_adhoc_patterns);
    free_adhoc_pattern_list(scene, scene->adhoc_patterns);
    cbox_midi_merger_close(&scene->scene_input_merger, scene->engine->rt);
    free(scene);
}
