//---------------------------------------
// Synapse update loop
//---------------------------------------
static inline final_state_t _plasticity_update_synapse(
        uint32_t time,
        const uint32_t last_pre_time, const pre_trace_t last_pre_trace,
        const pre_trace_t new_pre_trace, const uint32_t delay_dendritic,
        const uint32_t delay_axonal, update_state_t current_state,
        const post_event_history_t *post_event_history) {

    use(delay_dendritic);

    // Apply axonal delay to time of last presynaptic spike
    const uint32_t delayed_last_pre_time = last_pre_time + delay_axonal;

    // Get the post-synaptic window of events to be processed
    const uint32_t window_begin_time = delayed_last_pre_time;
    const uint32_t window_end_time = time + delay_axonal;
    post_event_window_t post_window = post_events_get_window_delayed(
            post_event_history, window_begin_time, window_end_time);

    log_debug("\tPerforming deferred synapse update at time:%u", time);
    log_debug("\t\tbegin_time:%u, end_time:%u - prev_time:%u, num_events:%u",
        window_begin_time, window_end_time, post_window.prev_time,
        post_window.num_events);
    
    //if ((time>1000) && (time<1050))
    //    io_printf(IO_BUF,"Spike through Plastic synapse at %dms: number of prior Target spike events: %d\n", time, post_window.num_events);

    // Process events in post-synaptic window
    while (post_window.num_events > 0) {
        uint32_t delayed_post_time = *post_window.next_time;
        post_trace_t target_trace = *post_window.next_trace;

        log_debug("\t\tApplying post-synaptic event at delayed time:%u\n",
              delayed_post_time);

        // Apply spike to state
        current_state = timing_apply_post_spike(
            delayed_post_time, target_trace, delayed_last_pre_time,
            last_pre_trace, post_window.prev_time, post_window.prev_trace,
            current_state);

        // Go onto next event
        post_window = post_events_next_delayed(post_window, delayed_post_time);
    }

    const uint32_t delayed_pre_time = time + delay_axonal;
    log_debug("\t\tApplying pre-synaptic event at time:%u last post time:%u\n",
              delayed_pre_time, post_window.prev_time);

    // Return final synaptic word and weight
    return synapse_structure_get_final_state(current_state);
}
//---------------------------------------
// Synapse update loop
//---------------------------------------
static inline final_state_t _plasticity_update_synapse(
        uint32_t time, uint32_t begin_time,
        uint32_t delay, update_state_t current_state,
        const pre_event_history_t *pre_event_history,
        const post_event_history_t *post_event_history) {

    // Get the pre-synaptic window of events to be processed
    pre_event_window_t pre_window = pre_events_get_window(
        time, pre_event_history, delay, begin_time);

    // Get the post-synaptic window of events to be processed
    post_event_window_t post_window = post_events_get_window(
        post_event_history, begin_time);

    log_debug("\tPerforming deferred synapse update at time:%u"
              " - pre_window.prev_time:%u, pre_window.num_events:%u,"
              " post_window.prev_time:%u, post_window.num_events:%u",
              time, pre_window.prev_time, pre_window.num_events,
              post_window.prev_time, post_window.num_events);

    // Process events that occur within window
    while (true) {
        // Are the next pre and post-synaptic events valid?
        const bool pre_valid = (pre_window.num_events > 0);
        const bool post_valid = (post_window.num_events > 0);

        // If next pre-synaptic event occurs before the next post-synaptic event
        if (pre_valid
                && (!post_valid
                        || (*pre_window.next_time + delay)
                                <= *post_window.next_time)) {
            log_debug("\t\tApplying pre-synaptic event at time:%u",
                      *pre_window.next_time + delay);

            // Apply spike to state
            const uint32_t delayed_pre_time = *pre_window.next_time + delay;
            current_state = timing_apply_pre_spike(delayed_pre_time,
                    *pre_window.next_trace, pre_window.prev_time,
                    pre_window.prev_trace, post_window.prev_time,
                    post_window.prev_trace, current_state);

            // Go onto next event
            pre_window = pre_events_next(pre_window, delayed_pre_time);
        }
        // Otherwise, if the next post-synaptic event occurs before the next pre-synaptic event
        else if (post_valid
                && (!pre_valid
                        || *post_window.next_time
                                <= (*pre_window.next_time + delay))) {
            log_debug("\t\tApplying post-synaptic event at time:%u",
                      *post_window.next_time);

            // Apply spike to state
            current_state = timing_apply_post_spike(*post_window.next_time,
                    *post_window.next_trace, pre_window.prev_time,
                    pre_window.prev_trace, post_window.prev_time,
                    post_window.prev_trace, current_state);

            // Go onto next event
            post_window = post_events_next(post_window);
        }
        // Otherwise, there's no more events so stop
        else {
            break;
        }
    }

    // Return final synaptic word and weight
    return synapse_structure_get_final_state(current_state);
}