/*
* Author: Christian Huitema
* Copyright (c) 2017, Private Octopus, Inc.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "picoquic_internal.h"
#include "picoquic_unified_log.h"
#include "tls_api.h"
#include <stdlib.h>
#include <string.h>

/*
 * Sending logic.
 *
 * Data is sent over streams. This is instantiated by the "Post to stream" command, which
 * chains data to the head of stream structure. Data is unchained when it sent for the
 * first time.
 * 
 * Data is sent in packets, which contain stream frames and possibly other frames.
 * The retransmission logic operates on packets. If a packet is seen as lost, the
 * important frames that it contains will have to be retransmitted.
 *
 * Unacknowledged packets are kept in a chained list. Packets get removed from that
 * list during the processing of acknowledgements. Packets are marked lost when a
 * sufficiently older packet is acknowledged, or after a timer. Lost packets
 * generate new packets, which are queued in the chained list.
 */

static picoquic_stream_head_t* picoquic_find_stream_for_writing(picoquic_cnx_t* cnx,
    uint64_t stream_id, int * ret)
{
    picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id);

    *ret = 0;

    if (stream == NULL) {
        /* Need to check that the ID is authorized */

        /* Check parity */
        if (IS_CLIENT_STREAM_ID(stream_id) != cnx->client_mode) {
            *ret = PICOQUIC_ERROR_INVALID_STREAM_ID;
        }

        if (*ret == 0) {
            stream = picoquic_create_missing_streams(cnx, stream_id, 0);

            if (stream == NULL) {
                *ret = PICOQUIC_ERROR_MEMORY;
            }
        }
    }

    return stream;
}

int picoquic_set_app_stream_ctx(picoquic_cnx_t* cnx,
    uint64_t stream_id, void* app_stream_ctx)
{
    int ret = 0;
    picoquic_stream_head_t* stream = picoquic_find_stream_for_writing(cnx, stream_id, &ret);

    if (ret == 0) {
        stream->app_stream_ctx = app_stream_ctx;
    }

    return ret;
}

int picoquic_mark_active_stream(picoquic_cnx_t* cnx,
    uint64_t stream_id, int is_active, void * app_stream_ctx)
{
    int ret = 0;
    picoquic_stream_head_t* stream = picoquic_find_stream_for_writing(cnx, stream_id, &ret);

    if (ret == 0) {
        if (is_active) {
            /* The call only fails if the stream was closed or reset */
            if (!stream->fin_requested && !stream->reset_requested &&
                cnx->callback_fn != NULL) {
                stream->is_active = 1;
                stream->app_stream_ctx = app_stream_ctx;
                picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic));
            }
            else {
                ret = PICOQUIC_ERROR_CANNOT_SET_ACTIVE_STREAM;
            }
        }
        else {
            stream->is_active = 0;
        }
    }

    return ret;
}

int picoquic_mark_high_priority_stream(picoquic_cnx_t * cnx, uint64_t stream_id, int is_high_priority)
{
    if (is_high_priority) {
        cnx->high_priority_stream_id = stream_id;
    }
    else if (cnx->high_priority_stream_id == stream_id) {
        cnx->high_priority_stream_id = (uint64_t)((int64_t)-1);
    }

    return 0;
}

int picoquic_add_to_stream_with_ctx(picoquic_cnx_t* cnx, uint64_t stream_id,
    const uint8_t* data, size_t length, int set_fin, void * app_stream_ctx)
{
    int ret = 0;
    picoquic_stream_head_t* stream = picoquic_find_stream_for_writing(cnx, stream_id, &ret);

    if (ret == 0 && set_fin) {
        if (stream->fin_requested) {
            /* app error, notified the fin twice*/
            if (length > 0) {
                ret = -1;
            }
        } else {
            stream->fin_requested = 1;
        }
    }

    /* If our side has sent RST_STREAM or received STOP_SENDING, we should not send anymore data. */
    if (ret == 0 && (stream->reset_sent || stream->stop_sending_received)) {
        ret = -1;
    }

    if (ret == 0 && length > 0) {
        picoquic_stream_data_node_t* stream_data = (picoquic_stream_data_node_t*)malloc(sizeof(picoquic_stream_data_node_t));

        if (stream_data == 0) {
            ret = -1;
        } else {
            stream_data->bytes = (uint8_t*)malloc(length);

            if (stream_data->bytes == NULL) {
                free(stream_data);
                stream_data = NULL;
                ret = -1;
            } else {
                picoquic_stream_data_node_t** pprevious = &stream->send_queue;
                picoquic_stream_data_node_t* next = stream->send_queue;

                memcpy(stream_data->bytes, data, length);
                stream_data->length = length;
                stream_data->offset = 0;
                stream_data->next_stream_data = NULL;

                while (next != NULL) {
                    pprevious = &next->next_stream_data;
                    next = next->next_stream_data;
                }

                *pprevious = stream_data;
            }
        }

        picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic));
    }

    if (ret == 0) {
        cnx->nb_bytes_queued += length;
        stream->is_active = 0;
        stream->app_stream_ctx = app_stream_ctx;
    }

    return ret;
}

int picoquic_add_to_stream(picoquic_cnx_t* cnx, uint64_t stream_id,
    const uint8_t* data, size_t length, int set_fin)
{
    return picoquic_add_to_stream_with_ctx(cnx, stream_id, data, length, set_fin, NULL);
}

int picoquic_open_flow_control(picoquic_cnx_t* cnx, uint64_t stream_id, uint64_t expected_data_size)
{
    int ret = 0;
    uint8_t buffer[512];
    size_t length = 0;
    size_t consumed = 0;
    picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id);

    if (cnx->cnx_state == picoquic_state_ready){
        /* Only send the update in ready state, so that the misc frame is not picked by the
         * wrong transport context.
         * TODO: find way to queue the update so it is only sent as 0RTT or 1RTT packet.
         */
        if (stream == NULL) {
            ret = PICOQUIC_ERROR_INVALID_STREAM_ID;
        }
        else {
            uint64_t max_required = stream->consumed_offset + expected_data_size;
            uint8_t* bytes_max = buffer + sizeof(buffer);
            int more_data = 0;
            int is_pure_ack = 1;

            if (max_required > stream->maxdata_local) {
                uint8_t* bytes_next = picoquic_format_max_stream_data_frame(stream, buffer + consumed, bytes_max, &more_data, &is_pure_ack, max_required);
                bytes_next = picoquic_format_max_data_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, expected_data_size);
                if ((length = bytes_next - buffer) > 0) {
                    ret = picoquic_queue_misc_frame(cnx, buffer, length, is_pure_ack);
                }
            }
        }
    }

    return ret;
}

void picoquic_reset_stream_ctx(picoquic_cnx_t* cnx, uint64_t stream_id)
{
    picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id);
    if (stream != NULL) {
        stream->app_stream_ctx = NULL;
    }
}

int picoquic_reset_stream(picoquic_cnx_t* cnx,
    uint64_t stream_id, uint16_t local_stream_error)
{
    int ret = 0;
    picoquic_stream_head_t* stream = NULL;

    stream = picoquic_find_stream(cnx, stream_id);

    if (stream == NULL) {
        ret = PICOQUIC_ERROR_INVALID_STREAM_ID;
    }
    else {
        stream->app_stream_ctx = NULL;
        if (stream->fin_sent) {
            ret = PICOQUIC_ERROR_STREAM_ALREADY_CLOSED;
        }
        else if (!stream->reset_requested) {
            stream->local_error = local_stream_error;
            stream->reset_requested = 1;
        }
    }

    picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic));

    return ret;
}

uint64_t picoquic_get_next_local_stream_id(picoquic_cnx_t* cnx, int is_unidir)
{
    int stream_type_id = (cnx->client_mode ^ 1) | ((is_unidir) ? 2 : 0);

    return cnx->next_stream_id[stream_type_id];     
}

int picoquic_stop_sending(picoquic_cnx_t* cnx,
    uint64_t stream_id, uint16_t local_stream_error)
{
    int ret = 0;
    picoquic_stream_head_t* stream = NULL;

    stream = picoquic_find_stream(cnx, stream_id);

    if (stream == NULL) {
        ret = PICOQUIC_ERROR_INVALID_STREAM_ID;
    }
    else {
        stream->app_stream_ctx = NULL;

        if (stream->reset_received) {
            ret = PICOQUIC_ERROR_STREAM_ALREADY_CLOSED;
        }
        else if (!stream->stop_sending_requested) {
            stream->local_stop_error = local_stream_error;
            stream->stop_sending_requested = 1;
            picoquic_insert_output_stream(cnx, stream);
        }
    }

    picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic));

    return ret;
}

/*
 * Manage content padding
 */

size_t picoquic_pad_to_target_length(uint8_t * bytes, size_t length, size_t target)
{
    if (length < target) {
        memset(bytes + length, 0, target - length);
        length = target;
    }

    return length;
}

size_t picoquic_pad_to_policy(picoquic_cnx_t * cnx, uint8_t * bytes, size_t length, uint32_t max_length)
{
    size_t target = cnx->padding_minsize;

    if (length > target && cnx->padding_multiple != 0) {
        uint32_t delta = (length - target) % cnx->padding_multiple;

        if (delta == 0) {
            target = length;
        }
        else {
            target = length + cnx->padding_multiple - delta;
        }
    }

    if (target > max_length) {
        target = max_length;
    }

    return picoquic_pad_to_target_length(bytes, length, target);
}


/*
 * Packet management
 */

picoquic_packet_t* picoquic_create_packet(picoquic_quic_t * quic)
{
    picoquic_packet_t* packet = quic->p_first_packet;
    
    if (packet == NULL) {
        packet = (picoquic_packet_t*)malloc(sizeof(picoquic_packet_t));
    }
    else {
        quic->p_first_packet = packet->next_packet;
        quic->nb_packets_in_pool--;
    }

    if (packet != NULL) {
        memset(packet, 0, offsetof(struct st_picoquic_packet_t, bytes));
    }

    return packet;
}

void picoquic_recycle_packet(picoquic_quic_t * quic, picoquic_packet_t* packet)
{
    if (packet != NULL) {
        if (quic->nb_packets_in_pool >= PICOQUIC_MAX_PACKETS_IN_POOL) {
            free(packet);
        }
        else {
            packet->next_packet = quic->p_first_packet;
            quic->p_first_packet = packet;
            quic->nb_packets_in_pool++;
        }
    }
}

void picoquic_update_payload_length(
    uint8_t* bytes, size_t pnum_index, size_t header_length, size_t packet_length)
{
    if ((bytes[0] & 0x80) != 0 && header_length > 6 && packet_length > header_length && packet_length < 0x4000)
    {
        picoquic_varint_encode_16(bytes + pnum_index - 2, (uint16_t)(packet_length - header_length));
    }
}

size_t picoquic_create_packet_header(
    picoquic_cnx_t* cnx,
    picoquic_packet_type_enum packet_type,
    uint64_t sequence_number,
    picoquic_connection_id_t * remote_cnxid,
    picoquic_connection_id_t * local_cnxid,
    size_t header_length,
    uint8_t* bytes,
    size_t* pn_offset,
    size_t* pn_length)
{
    size_t length = 0;
    picoquic_connection_id_t dest_cnx_id =
        (cnx->client_mode && (packet_type == picoquic_packet_initial ||
            packet_type == picoquic_packet_0rtt_protected)
            && picoquic_is_connection_id_null(remote_cnxid)) ?
        cnx->initial_cnxid : *remote_cnxid;

    /* Prepare the packet header */
    if (packet_type == picoquic_packet_1rtt_protected) {
        /* Create a short packet -- using 32 bit sequence numbers for now */
        uint8_t K = (cnx->key_phase_enc) ? 0x04 : 0;
        uint8_t C = 0x40; /* set the QUIC bit */
        size_t pn_l = 4;  /* default packet length to 4 bytes */

        if (cnx->do_grease_quic_bit) {
            /* we grease the quic bit if both local and remote agreed to do so */
            C &= (uint8_t)picoquic_public_random_64();
            cnx->quic_bit_greased |= (C == 0);
        }

        length = 0;
        bytes[length++] = (K | C | picoquic_spin_function_table[cnx->spin_policy].spinbit_outgoing(cnx));
        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id);

        *pn_offset = length;
        if (header_length > length && header_length < length + 4) {
            pn_l = header_length - length;
        }
        *pn_length = pn_l;
        bytes[0] |= (pn_l - 1);
        switch (pn_l) {
        case 1:
            bytes[length] = (uint8_t)sequence_number;
            break;
        case 2:
            picoformat_16(&bytes[length], (uint16_t)sequence_number);
            break;
        case 3:
            picoformat_24(&bytes[length], (uint32_t)sequence_number);
            break;
        default:
            picoformat_32(&bytes[length], (uint32_t)sequence_number);
            break;
        }
        length += pn_l;
    }
    else {
        /* Create a long packet -- default encode PP=3 */

        switch (packet_type) {
        case picoquic_packet_initial:
            bytes[0] = 0xC3;
            break;
        case picoquic_packet_0rtt_protected:
            bytes[0] = 0xD3;
            break;
        case picoquic_packet_handshake:
            bytes[0] = 0xE3;
            break;
        case picoquic_packet_retry:
            /* Do not set PP in retry header, the bits are later used for ODCIL */
            bytes[0] = 0xF0;
            break;
        default:
            bytes[0] = 0xFF; /* Will cause an error... */
            break;
        }

        if (cnx->do_grease_quic_bit) {
            bytes[0] &= 0xBF;
        }

        length = 1;
        if ((cnx->cnx_state == picoquic_state_client_init || cnx->cnx_state == picoquic_state_client_init_sent) && packet_type == picoquic_packet_initial) {
            picoformat_32(&bytes[length], cnx->proposed_version);
        }
        else {
            picoformat_32(&bytes[length], picoquic_supported_versions[cnx->version_index].version);
        }
        length += 4;

        bytes[length++] = dest_cnx_id.id_len;
        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id);
        bytes[length++] = local_cnxid->id_len;
        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, *local_cnxid);

        /* Special case of packet initial -- encode token as part of header */
        if (packet_type == picoquic_packet_initial) {
            length += picoquic_varint_encode(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, cnx->retry_token_length);
            if (cnx->retry_token_length > 0) {
                memcpy(&bytes[length], cnx->retry_token, cnx->retry_token_length);
                length += cnx->retry_token_length;
            }
        }

        if (packet_type == picoquic_packet_retry) {
            /* No payload length and no sequence number for Retry */
            *pn_offset = 0;
            *pn_length = 0;
        } else {
            /* Reserve two bytes for payload length */
            bytes[length++] = 0;
            bytes[length++] = 0;
            /* Encode the sequence number */
            *pn_offset = length;
            *pn_length = 4;
            picoformat_32(&bytes[length], (uint32_t) sequence_number);
            length += 4;
        }
    }

    return length;
}

size_t picoquic_predict_packet_header_length(
    picoquic_cnx_t* cnx,
    picoquic_packet_type_enum packet_type)
{
    uint32_t header_length = 0;

    /* The only purpose of the test below is to appease the static analyzer, so it
     * wont complain of possible NULL deref. On windows we could use "__assume(cnx != NULL)
     * but the documentation does not say anything about that for GCC and CLANG */
    if (cnx == NULL) {
        return 0;
    }

    if (packet_type == picoquic_packet_1rtt_protected) {
        /* Predict acceptable length of packet number */
        uint8_t pn_l = 4;
        int64_t delta = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence;
        if (cnx->pkt_ctx[picoquic_packet_context_application].retransmit_oldest != NULL) {
            delta -= cnx->pkt_ctx[picoquic_packet_context_application].retransmit_oldest->sequence_number;
        }
        if (delta < 262144) {
            pn_l = 3;
            if (cnx->pkt_ctx[picoquic_packet_context_application].send_sequence < 1024) {
                pn_l = 2;
                if (cnx->pkt_ctx[picoquic_packet_context_application].send_sequence < 16) {
                    pn_l = 1;
                }
            }
        }

        /* Compute length of a short packet header */
        header_length = 1 + cnx->path[0]->remote_cnxid.id_len + pn_l;
    }
    else {
        /* Compute length of a long packet header */
        header_length = 1 + /* version */ 4 + /* cnx_id length bytes */ 2;

        /* add dest-id length */
        if (cnx->client_mode && (packet_type == picoquic_packet_initial ||
            packet_type == picoquic_packet_0rtt_protected)
            && picoquic_is_connection_id_null(&cnx->path[0]->remote_cnxid)) {
            header_length += cnx->initial_cnxid.id_len;
        }
        else {
            header_length += cnx->path[0]->remote_cnxid.id_len;
        }

        /* add srce-id length */
        header_length += cnx->path[0]->p_local_cnxid->cnx_id.id_len;

        /* add length of payload length and packet number */
        header_length += 2 + 4;

        /* add length of tokens for initial packets */
        if (packet_type == picoquic_packet_initial) {
            uint8_t useless[16];
            header_length += (uint32_t)picoquic_varint_encode(useless, 16, cnx->retry_token_length);
            header_length += (uint32_t)cnx->retry_token_length;
        }
    }

    return header_length;
}

/*
 * Management of packet protection
 */
size_t picoquic_get_checksum_length(picoquic_cnx_t* cnx, picoquic_epoch_enum epoch)
{
    size_t ret = 16;

    if (cnx->crypto_context[epoch].aead_encrypt != NULL) {
        ret = picoquic_aead_get_checksum_length(cnx->crypto_context[epoch].aead_encrypt);
    }
    else {
        DBG_PRINTF("Try getting checksum for empty context, epoch %d", epoch);
    }

    return ret;
}

static size_t picoquic_protect_packet(picoquic_cnx_t* cnx, 
    picoquic_packet_type_enum ptype,
    uint8_t * bytes, 
    uint64_t sequence_number,
    picoquic_connection_id_t * remote_cnxid,
    picoquic_connection_id_t * local_cnxid,
    size_t length, size_t header_length,
    uint8_t* send_buffer, size_t send_buffer_max,
    void * aead_context, void* pn_enc,
    picoquic_path_t* path_x, uint64_t current_time)
{
    size_t send_length;
    size_t h_length;
    size_t pn_offset = 0;
    size_t sample_offset = 0;
    size_t pn_length = 0;
    size_t aead_checksum_length = picoquic_aead_get_checksum_length(aead_context);
    uint8_t first_mask = 0x0F;

    /* Create the packet header just before encrypting the content */
    h_length = picoquic_create_packet_header(cnx, ptype,
        sequence_number, remote_cnxid, local_cnxid, header_length, send_buffer, &pn_offset, &pn_length);
    if (ptype == picoquic_packet_1rtt_protected) {
        if (remote_cnxid != &path_x->remote_cnxid) {
            /* Packet is sent to a different CID: reset the spin bit and loss bit Q to 0 */
            send_buffer[0] &= 0xDF;
            path_x->q_square = 0;
        }

        if (cnx->is_loss_bit_enabled_outgoing) {
            first_mask = 0x07;
            path_x->q_square++;
            if ((path_x->q_square & PICOQUIC_LOSS_BIT_Q_HALF_PERIOD) != 0) {
                send_buffer[0] |= 0x10;
            }
            if (path_x->nb_losses_found > path_x->nb_losses_reported) {
                send_buffer[0] |= 0x08;
                path_x->nb_losses_reported++;
            }
        }
        else {
            first_mask = 0x1F;
        }
    }

    /* Make sure that the payload length is encoded in the header */
    /* Using encryption, the "payload" length also includes the encrypted packet length */
    picoquic_update_payload_length(send_buffer, pn_offset, h_length - pn_length, length + aead_checksum_length);

    /* If fuzzing is required, apply it*/
    if (cnx->quic->fuzz_fn != NULL) {
        if (h_length == header_length) {
            memcpy(bytes, send_buffer, header_length);
        }
        length = cnx->quic->fuzz_fn(cnx->quic->fuzz_ctx, cnx, bytes,
            send_buffer_max - aead_checksum_length, length, header_length);
        if (h_length == header_length) {
            memcpy(send_buffer, bytes, header_length);
        }
    }

    /* Encrypt the packet */
    send_length = picoquic_aead_encrypt_generic(send_buffer + /* header_length */ h_length,
        bytes + header_length, length - header_length,
        sequence_number, send_buffer, /* header_length */ h_length, aead_context);

    send_length += /* header_length */ h_length;

    /* if needed, log the segment before header protection is applied */
    picoquic_log_outgoing_packet(cnx,
        bytes, sequence_number, pn_length, length,
        send_buffer, send_length, current_time);

    /* Next, encrypt the PN -- The sample is located after the pn_offset */
    sample_offset = /* header_length */ pn_offset + 4;

    if (pn_offset < sample_offset)
    {
        /* This is always true, as use pn_length = 4 */
        uint8_t mask_bytes[5] = { 0, 0, 0, 0, 0 };
        uint8_t pn_l;

        picoquic_pn_encrypt(pn_enc, send_buffer + sample_offset, mask_bytes, mask_bytes, 5);
        /* Encode the first byte */
        pn_l = (send_buffer[0] & 3) + 1;
        send_buffer[0] ^= (mask_bytes[0] & first_mask);

        /* Packet encoding is 1 to 4 bytes */
        for (uint8_t i = 0; i < pn_l; i++) {
            send_buffer[pn_offset+i] ^= mask_bytes[i+1];
        }
    }

    return send_length;
}

/* Update the leaky bucket used for pacing.
 */
static void picoquic_update_pacing_bucket(picoquic_path_t * path_x, uint64_t current_time)
{
    if (path_x->pacing_bucket_nanosec < -path_x->pacing_packet_time_nanosec) {
        path_x->pacing_bucket_nanosec = -path_x->pacing_packet_time_nanosec;
    }

    if (current_time > path_x->pacing_evaluation_time) {
        path_x->pacing_bucket_nanosec += (current_time - path_x->pacing_evaluation_time) * 1000;
        path_x->pacing_evaluation_time = current_time;
        if (path_x->pacing_bucket_nanosec > path_x->pacing_bucket_max) {
            path_x->pacing_bucket_nanosec = path_x->pacing_bucket_max;
        }
    }
}

/*
 * Check pacing to see whether the next transmission is authorized.
 * If if is not, update the next wait time to reflect pacing.
 * 
 * In packet train mode, the wait will last until the bucket is completely full, or
 * if at least N packets are received.
 */
int picoquic_is_sending_authorized_by_pacing(picoquic_cnx_t * cnx, picoquic_path_t * path_x, uint64_t current_time, uint64_t * next_time)
{
    int ret = 1;

    picoquic_update_pacing_bucket(path_x, current_time);
    if (path_x->pacing_bucket_nanosec < path_x->pacing_packet_time_nanosec) {
        uint64_t next_pacing_time;
        int64_t bucket_required;
        
        if (cnx->quic->packet_train_mode) {
            bucket_required = path_x->pacing_bucket_max;

            if (bucket_required > 10 * path_x->pacing_packet_time_nanosec) {
                bucket_required = 10 * path_x->pacing_packet_time_nanosec;
            }
            
            bucket_required -= path_x->pacing_bucket_nanosec;
        }
        else {
            bucket_required = path_x->pacing_packet_time_nanosec - path_x->pacing_bucket_nanosec;
        }

        next_pacing_time = current_time + 1 + bucket_required / 1000;
        if (next_pacing_time < *next_time) {
            *next_time = next_pacing_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }
        ret = 0;
    }

    return ret;
}

/* Reset the pacing data after recomputing the pacing rate
 */
void picoquic_update_pacing_rate(picoquic_cnx_t * cnx, picoquic_path_t* path_x, double pacing_rate, uint64_t quantum)
{
    double packet_time = (double)path_x->send_mtu / pacing_rate;
    double quantum_time = (double)quantum / pacing_rate;
    uint64_t rtt_nanosec = path_x->smoothed_rtt * 1000;

    path_x->pacing_rate = (uint64_t)pacing_rate;

    path_x->pacing_packet_time_nanosec = (uint64_t)(packet_time * 1000000000.0);

    if (path_x->pacing_packet_time_nanosec <= 0) {
        path_x->pacing_packet_time_nanosec = 1;
        path_x->pacing_packet_time_microsec = 1;
    }
    else {
        if ((uint64_t)path_x->pacing_packet_time_nanosec > rtt_nanosec) {
            path_x->pacing_packet_time_nanosec = rtt_nanosec;
        }
        path_x->pacing_packet_time_microsec = (path_x->pacing_packet_time_nanosec + 999ull) / 1000;
    }

    path_x->pacing_bucket_max = (uint64_t)(quantum_time * 1000000000.0);
    if (path_x->pacing_bucket_max <= 0) {
        path_x->pacing_bucket_max = 16 * path_x->pacing_packet_time_nanosec;
    }

    if (path_x->pacing_bucket_nanosec > path_x->pacing_bucket_max) {
        path_x->pacing_bucket_nanosec = path_x->pacing_bucket_max;
    }

    if (cnx->is_pacing_update_requested && path_x == cnx->path[0] &&
        cnx->callback_fn != NULL) {
        if ((path_x->pacing_rate > cnx->pacing_rate_signalled &&
            (path_x->pacing_rate - cnx->pacing_rate_signalled >= cnx->pacing_increase_threshold)) ||
            (path_x->pacing_rate < cnx->pacing_rate_signalled &&
            (cnx->pacing_rate_signalled - path_x->pacing_rate > cnx->pacing_decrease_threshold))){
            (void)cnx->callback_fn(cnx, path_x->pacing_rate, NULL, 0, picoquic_callback_pacing_changed, cnx->callback_ctx, NULL);
            cnx->pacing_rate_signalled = path_x->pacing_rate;
        }
    }
}

/*
 * Reset the pacing data after CWIN is updated.
 * The max bucket is set to contain at least 2 packets more than 1/8th of the congestion window.
 */

void picoquic_update_pacing_data(picoquic_cnx_t* cnx, picoquic_path_t * path_x, int slow_start)
{
    uint64_t rtt_nanosec = path_x->smoothed_rtt * 1000;

    if ((path_x->cwin < ((uint64_t)path_x->send_mtu) * 8) || rtt_nanosec <= 1000) {
        /* Small windows, should only relie on ACK clocking */
        path_x->pacing_bucket_max = rtt_nanosec;
        path_x->pacing_packet_time_nanosec = 1;
        path_x->pacing_packet_time_microsec = 1;

        if (path_x->pacing_bucket_nanosec > path_x->pacing_bucket_max) {
            path_x->pacing_bucket_nanosec = path_x->pacing_bucket_max;
        }
    }
    else {
        double pacing_rate = ((double)path_x->cwin / (double)rtt_nanosec) * 1000000000.0;
        uint64_t quantum = path_x->cwin / 4;

        if (quantum < 2ull * path_x->send_mtu) {
            quantum = 2ull * path_x->send_mtu;
        }
        else {
            if (slow_start && path_x->smoothed_rtt > 4*PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) {
                const uint64_t quantum_min = 0x8000;
                if (quantum  < quantum_min){
                    quantum = quantum_min;
                }
                else {
                    uint64_t quantum2 = (uint64_t)((pacing_rate * PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) / 1000000.0);
                    if (quantum2 > quantum_min) {
                        quantum = quantum2;
                    }
                }
            }
            else if (quantum > 16ull * path_x->send_mtu) {
                quantum = 16ull * path_x->send_mtu;
            }

        }

        if (slow_start) {
            pacing_rate *= 1.25;
        }

        picoquic_update_pacing_rate(cnx, path_x, pacing_rate, quantum);
    }
}

/* 
 * Update the pacing data after sending a packet.
 */
void picoquic_update_pacing_after_send(picoquic_path_t * path_x, uint64_t current_time)
{
    picoquic_update_pacing_bucket(path_x, current_time);

    path_x->pacing_bucket_nanosec -= path_x->pacing_packet_time_nanosec;
}

/*
 * Final steps in packet transmission: queue for retransmission, etc
 */

void picoquic_queue_for_retransmit(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet,
    size_t length, uint64_t current_time)
{
    picoquic_packet_context_enum pc = packet->pc;

    /* Manage the double linked packet list for retransmissions */
    packet->previous_packet = NULL;
    if (cnx->pkt_ctx[pc].retransmit_newest == NULL) {
        packet->next_packet = NULL;
        cnx->pkt_ctx[pc].retransmit_oldest = packet;
    } else {
        packet->next_packet = cnx->pkt_ctx[pc].retransmit_newest;
        packet->next_packet->previous_packet = packet;
    }
    cnx->pkt_ctx[pc].retransmit_newest = packet;

    if (!packet->is_ack_trap) {
        /* Account for bytes in transit, for congestion control */
        path_x->bytes_in_transit += length;
        /* Update the pacing data */
        picoquic_update_pacing_after_send(path_x, current_time);
    }
}

picoquic_packet_t* picoquic_dequeue_retransmit_packet(picoquic_cnx_t* cnx, picoquic_packet_t* p, int should_free)
{
    size_t dequeued_length = p->length + p->checksum_overhead;
    picoquic_packet_context_enum pc = p->pc;

    if (p->previous_packet == NULL) {
        cnx->pkt_ctx[pc].retransmit_newest = p->next_packet;
    }
    else {
        p->previous_packet->next_packet = p->next_packet;
    }

    if (p->next_packet == NULL) {
        cnx->pkt_ctx[pc].retransmit_oldest = p->previous_packet;
    }
    else {
#ifdef _DEBUG
        if (p->next_packet->pc != pc) {
            DBG_PRINTF("Inconsistent PC in queue, %d vs %d\n", p->next_packet->pc, pc);
        }

        if (p->next_packet->previous_packet != p) {
            DBG_PRINTF("Inconsistent chain of packets, pc = %d\n", pc);
        }
#endif
        p->next_packet->previous_packet = p->previous_packet;
    }

    /* Account for bytes in transit, for congestion control */

    if (p->send_path != NULL && !p->is_ack_trap) {
        if (p->send_path->bytes_in_transit > dequeued_length) {
            p->send_path->bytes_in_transit -= dequeued_length;
        }
        else {
            p->send_path->bytes_in_transit = 0;
        }
    }

    if (should_free || p->is_ack_trap) {
        picoquic_recycle_packet(cnx->quic, p);
        p = NULL;
    }
    else {
        p->next_packet = NULL;

        /* add this packet to the retransmitted list */
        if (cnx->pkt_ctx[pc].retransmitted_oldest == NULL) {
            cnx->pkt_ctx[pc].retransmitted_newest = p;
            cnx->pkt_ctx[pc].retransmitted_oldest = p;
            p->previous_packet = NULL;
        }
        else {
            cnx->pkt_ctx[pc].retransmitted_newest->next_packet = p;
            p->previous_packet = cnx->pkt_ctx[pc].retransmitted_newest;
            cnx->pkt_ctx[pc].retransmitted_newest = p;
        }
    }

    return p;
}

void picoquic_dequeue_retransmitted_packet(picoquic_cnx_t* cnx, picoquic_packet_t* p)
{
    picoquic_packet_context_enum pc = p->pc;

    if (p->next_packet == NULL) {
        cnx->pkt_ctx[pc].retransmitted_newest = p->previous_packet;
    }
    else {
        p->next_packet->previous_packet = p->previous_packet;
    }

    if (p->previous_packet == NULL) {
        cnx->pkt_ctx[pc].retransmitted_oldest = p->next_packet;
    }
    else {
#ifdef _DEBUG
        if (p->previous_packet->pc != pc) {
            DBG_PRINTF("Inconsistent PC in queue, %d vs %d\n", p->previous_packet->pc, pc);
        }

        if (p->previous_packet->next_packet != p) {
            DBG_PRINTF("Inconsistent chain of packets, pc = %d\n", pc);
        }
#endif
        p->previous_packet->next_packet = p->next_packet;
    }

    picoquic_recycle_packet(cnx->quic, p);
}

/*
 * Inserting holes in the send sequence to trap optimistic ack.
 * return 0 if hole was inserted, !0 if packet should be freed.
 */
void picoquic_insert_hole_in_send_sequence_if_needed(picoquic_cnx_t* cnx, uint64_t current_time, uint64_t * next_wake_time)
{
    if (cnx->quic->sequence_hole_pseudo_period == 0) {
        /* Holing disabled. Set to max value, never worry about it later */
        cnx->pkt_ctx[0].next_sequence_hole = (uint64_t)((int64_t)-1);
    } else if (cnx->cnx_state == picoquic_state_ready &&
        cnx->pkt_ctx[0].retransmit_newest != NULL &&
        cnx->pkt_ctx[0].send_sequence >= cnx->pkt_ctx[0].next_sequence_hole) {
        if (cnx->pkt_ctx[0].next_sequence_hole != 0 &&
            !cnx->pkt_ctx[0].retransmit_newest->is_ack_trap) {
            /* Insert a hole in sequence */
            picoquic_packet_t* packet = picoquic_create_packet(cnx->quic);

            if (packet != NULL) {
                packet->is_ack_trap = 1;
                packet->pc = picoquic_packet_context_application;
                packet->ptype = picoquic_packet_1rtt_protected;
                packet->send_path = cnx->path[0];
                packet->send_time = current_time;
                packet->sequence_number = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence++;
                picoquic_queue_for_retransmit(cnx, cnx->path[0], packet, 0, current_time);
                *next_wake_time = current_time;
                SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                /* Simulate local loss on the Q bit square function. */
                cnx->path[0]->q_square++;
            }
        }
        /* Predict the next hole*/
        cnx->pkt_ctx[0].next_sequence_hole = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence + 3 + picoquic_public_uniform_random(cnx->quic->sequence_hole_pseudo_period);
    }
}

/*
 * Final steps of encoding and protecting the packet before sending
 */

void picoquic_finalize_and_protect_packet(picoquic_cnx_t *cnx, picoquic_packet_t * packet, int ret, 
    size_t length, size_t header_length, size_t checksum_overhead,
    size_t * send_length, uint8_t * send_buffer, size_t send_buffer_max,
    picoquic_connection_id_t * remote_cnxid,
    picoquic_connection_id_t * local_cnxid,
    picoquic_path_t * path_x, uint64_t current_time)
{
    if (length != 0 && length < header_length) {
        length = 0;
    }

    if (ret == 0 && length > 0) {
        packet->length = length;
        cnx->pkt_ctx[packet->pc].send_sequence++;
        path_x->latest_sent_time = current_time;
        path_x->path_cid_rotated = 0;
        packet->delivered_prior = path_x->delivered_last;
        packet->delivered_time_prior = path_x->delivered_time_last;
        packet->delivered_sent_prior = path_x->delivered_sent_last;
        packet->delivered_app_limited = (cnx->cnx_state < picoquic_state_ready || path_x->delivered_limited_index != 0);

        switch (packet->ptype) {
        case picoquic_packet_version_negotiation:
            /* Packet is not encrypted */
            break;
        case picoquic_packet_initial:
            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
                remote_cnxid, local_cnxid,
                length, header_length,
                send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_initial].aead_encrypt, cnx->crypto_context[picoquic_epoch_initial].pn_enc,
                path_x, current_time);
            break;
        case picoquic_packet_handshake:
            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
                remote_cnxid, local_cnxid,
                length, header_length,
                send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt, cnx->crypto_context[picoquic_epoch_handshake].pn_enc,
                path_x, current_time);
            break;
        case picoquic_packet_retry:
            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
                remote_cnxid, local_cnxid,
                length, header_length,
                send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt, cnx->crypto_context[picoquic_epoch_0rtt].pn_enc,
                path_x, current_time);
            break;
        case picoquic_packet_0rtt_protected:
            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number, 
                remote_cnxid, local_cnxid,
                length, header_length,
                send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt, cnx->crypto_context[picoquic_epoch_0rtt].pn_enc,
                path_x, current_time);
            break;
        case picoquic_packet_1rtt_protected:
            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
                remote_cnxid, local_cnxid,
                length, header_length,
                send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_1rtt].aead_encrypt, cnx->crypto_context[picoquic_epoch_1rtt].pn_enc,
                path_x, current_time);
            break;
        default:
            /* Packet type error. Do nothing at all. */
            length = 0;
            break;
        }

        *send_length = length;

        if (length > 0) {
            packet->checksum_overhead = checksum_overhead;
            picoquic_queue_for_retransmit(cnx, path_x, packet, length, current_time);
        } else {
            *send_length = 0;
        }
    }
    else {
        *send_length = 0;
    }
}

/*
 * If a retransmit is needed, fill the packet with the required
 * retransmission. Also, prune the retransmit queue as needed.
 *
 * TODO: consider that the retransmit timer is per path, from the path on
 * which the packet was first sent, but the retransmission may be on 
 * a different path, with different MTU.
 */

static uint64_t picoquic_current_retransmit_timer(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc)
{
    uint64_t rto = cnx->path[0]->retransmit_timer;

    rto <<= cnx->pkt_ctx[pc].nb_retransmit;
    if (cnx->cnx_state < picoquic_state_ready) {
        if (rto > PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER) {
            rto = PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER;
        }
    }
    else if (rto > PICOQUIC_LARGE_RETRANSMIT_TIMER){
        uint64_t alt_rto = PICOQUIC_LARGE_RETRANSMIT_TIMER;
        if (cnx->path[0]->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) {
            alt_rto = (cnx->path[0]->smoothed_rtt * 3) >> 1;
        }
        if (alt_rto < rto) {
            rto = alt_rto;
        }
    }

    return rto;
}

static int picoquic_retransmit_needed_by_packet(picoquic_cnx_t* cnx,
    picoquic_packet_t* p, uint64_t current_time, uint64_t * next_retransmit_time, int* timer_based)
{
    picoquic_packet_context_enum pc = p->pc;
    uint64_t retransmit_time;
    int64_t delta_seq = cnx->pkt_ctx[pc].highest_acknowledged - p->sequence_number;
    int should_retransmit = 0;
    int is_timer_based = 0;

    if (delta_seq > 0) {
        /* By default, we use an RTO  */
        retransmit_time = p->send_time + cnx->path[0]->retransmit_timer;
        /* RACK logic works best when the amount of reordering is not too large */
        if (delta_seq < 3) {
            /* When just a few ulterior packets are acknowledged, we work from the right edge. */
            uint64_t rack_timer_min = cnx->pkt_ctx[pc].highest_acknowledged_time +
                cnx->remote_parameters.max_ack_delay + (cnx->path[0]->smoothed_rtt >> 2);
            if (retransmit_time > rack_timer_min) {
                retransmit_time = rack_timer_min;
            }
        } else {
            /* When enough ulterior packets are acknowledged, we work from the right edge. */
            uint64_t rack_timer_min = p->send_time + (cnx->path[0]->smoothed_rtt >> 2);
            if (rack_timer_min < cnx->pkt_ctx[pc].latest_time_acknowledged) {
                retransmit_time = cnx->pkt_ctx[pc].highest_acknowledged_time;
            }
        }
    }
    else
    {
        /* There has not been any higher packet acknowledged, thus we fall back on timer logic. */
        retransmit_time = p->send_time + picoquic_current_retransmit_timer(cnx, pc);
        is_timer_based = 1;
    }

    if (p->ptype == picoquic_packet_0rtt_protected) {
        /* Special case for 0RTT packets */
        if (cnx->cnx_state != picoquic_state_ready &&
            cnx->cnx_state != picoquic_state_client_ready_start) {
            /* Set the retransmit time ahead of current time since the connection is not ready */
            retransmit_time = current_time + cnx->path[0]->smoothed_rtt + PICOQUIC_RACK_DELAY;
        } else if (!cnx->zero_rtt_data_accepted) {
            /* Zero RTT data was not accepted by the peer, the packets are considered lost */
            retransmit_time = current_time;
        }
    }

    if (current_time >= retransmit_time || (p->is_ack_trap && delta_seq > 0)) {
        should_retransmit = 1;
        *timer_based = is_timer_based;
        if (cnx->quic->sequence_hole_pseudo_period != 0 && pc == picoquic_packet_context_application && !p->is_ack_trap) {
            DBG_PRINTF("Retransmit #%d, delta=%d, timer=%d, time=%d, sent: %d, ack_t: %d, s_rtt: %d, rt: %d",
                (int)p->sequence_number, (int)delta_seq, is_timer_based, (int)current_time, (int)p->send_time, 
                (int)cnx->pkt_ctx[pc].latest_time_acknowledged, (int)cnx->path[0]->smoothed_rtt, (int) retransmit_time);
        }
    }
    
    *next_retransmit_time = retransmit_time;

    return should_retransmit;
}

int picoquic_queue_stream_frame_for_retransmit(picoquic_cnx_t* cnx, uint8_t * bytes, size_t length)
{
    int ret = 0;
    picoquic_misc_frame_header_t* misc = picoquic_create_misc_frame(bytes, length, 0);

    if (misc == NULL) {
        ret = PICOQUIC_ERROR_MEMORY;
    }
    else {
        misc->next_misc_frame = NULL;
        if (cnx->stream_frame_retransmit_queue_last == NULL) {
            cnx->stream_frame_retransmit_queue = misc;
            cnx->stream_frame_retransmit_queue_last = misc;
        }
        else {
            misc->previous_misc_frame = cnx->stream_frame_retransmit_queue_last;
            cnx->stream_frame_retransmit_queue_last->next_misc_frame = misc;
            cnx->stream_frame_retransmit_queue_last = misc;
        }
    }

    return ret;
}

int picoquic_copy_before_retransmit(picoquic_packet_t * old_p,
    picoquic_cnx_t * cnx,
    uint8_t * new_bytes,
    size_t send_buffer_max_minus_checksum,
    int * packet_is_pure_ack,
    int * do_not_detect_spurious,
    size_t * length)
{
    /* check if this is an ACK only packet */
    int ret = 0;
    int frame_is_pure_ack = 0;
    size_t frame_length = 0;
    size_t byte_index = 0; /* Used when parsing the old packet */
    
    if (old_p->is_mtu_probe) {
        if (old_p->send_path != NULL) {
            /* MTU probe was lost, presumably because of packet too big */
            old_p->send_path->mtu_probe_sent = 0;
            old_p->send_path->send_mtu_max_tried = old_p->length + old_p->checksum_overhead;
        }
        /* MTU probes should not be retransmitted */
        *packet_is_pure_ack = 1;
        *do_not_detect_spurious = 0;
    }
    else if (old_p->is_ack_trap) {
        *packet_is_pure_ack = 1;
        *do_not_detect_spurious = 0;
    }
    else {
        /* Copy the relevant bytes from one packet to the next */
        byte_index = old_p->offset;

        while (ret == 0 && byte_index < old_p->length) {
            ret = picoquic_skip_frame(&old_p->bytes[byte_index],
                old_p->length - byte_index, &frame_length, &frame_is_pure_ack);

            /* Check whether the data was already acked, which may happen in
             * case of spurious retransmissions */
            if (ret == 0 && frame_is_pure_ack == 0) {
                ret = picoquic_check_frame_needs_repeat(cnx, &old_p->bytes[byte_index],
                    frame_length, &frame_is_pure_ack);
            }

            /* Prepare retransmission if needed */
            if (ret == 0 && !frame_is_pure_ack) {
                if (PICOQUIC_IN_RANGE(old_p->bytes[byte_index], picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) {
                    ret = picoquic_queue_stream_frame_for_retransmit(cnx, &old_p->bytes[byte_index], frame_length);
                }
                else {
                    if (frame_length > send_buffer_max_minus_checksum - *length &&
                        (old_p->ptype == picoquic_packet_0rtt_protected || old_p->ptype == picoquic_packet_1rtt_protected)) {
                        ret = picoquic_queue_misc_frame(cnx, &old_p->bytes[byte_index], frame_length, 0);
                    }
                    else {
                        memcpy(&new_bytes[*length], &old_p->bytes[byte_index], frame_length);
                        *length += frame_length;
                    }
                }
                *packet_is_pure_ack = 0;
            }
            byte_index += frame_length;
        }
    }

    return ret;
}

int picoquic_retransmit_needed(picoquic_cnx_t* cnx,
    picoquic_packet_context_enum pc,
    picoquic_path_t * path_x, uint64_t current_time, uint64_t * next_wake_time,
    picoquic_packet_t* packet, size_t send_buffer_max, size_t* header_length)
{
    picoquic_packet_t* old_p = cnx->pkt_ctx[pc].retransmit_oldest;
    size_t length = 0;

    /* TODO: while packets are pure ACK, drop them from retransmit queue */
    while (old_p != NULL) {
        picoquic_path_t * old_path = old_p->send_path; /* should be the path on which the packet was transmitted */
        int should_retransmit = 0;
        int timer_based_retransmit = 0;
        uint64_t next_retransmit_time = *next_wake_time;
        uint64_t lost_packet_number = old_p->sequence_number;
        picoquic_packet_t* p_next = old_p->previous_packet;
        uint8_t * new_bytes = packet->bytes;
        int ret = 0;

        length = 0;

        /* Get the packet type */

        should_retransmit = cnx->initial_repeat_needed || 
            picoquic_retransmit_needed_by_packet(cnx, old_p, current_time, &next_retransmit_time, &timer_based_retransmit);

        if (should_retransmit == 0) {
            /*
             * Always retransmit in order. If not this one, then nothing.
             * But make an exception for 0-RTT packets.
             */
            if (old_p->ptype == picoquic_packet_0rtt_protected) {
                old_p = p_next;
                continue;
            }
            else {
                if (next_retransmit_time < *next_wake_time) {
                    *next_wake_time = next_retransmit_time;
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
                break;
            }
        } else if (old_p->is_ack_trap){
            picoquic_dequeue_retransmit_packet(cnx, old_p, 1);
            old_p = p_next;
            continue;
        } else {
            /* check if this is an ACK only packet */
            int packet_is_pure_ack = 1;
            int do_not_detect_spurious = 1;
            int frame_is_pure_ack = 0;
            uint8_t* old_bytes = old_p->bytes;
            size_t frame_length = 0;
            size_t byte_index = 0; /* Used when parsing the old packet */
            size_t checksum_length = 0;

	        /* we'll report it where it got lost */
            if (old_path) {
                old_path->retrans_count++;
            }

            *header_length = 0;

            if (old_p->ptype == picoquic_packet_0rtt_protected) {
                /* Only retransmit as 0-RTT if contains crypto data */
                int contains_crypto = 0;
                byte_index = old_p->offset;

                if (old_p->is_evaluated == 0) {
                    while (ret == 0 && byte_index < old_p->length) {
                        if (old_bytes[byte_index] == picoquic_frame_type_crypto_hs) {
                            contains_crypto = 1;
                            packet_is_pure_ack = 0;
                            break;
                        }
                        ret = picoquic_skip_frame(&old_p->bytes[byte_index],
                            old_p->length - byte_index, &frame_length, &frame_is_pure_ack);
                        byte_index += frame_length;
                    }
                    old_p->contains_crypto = contains_crypto;
                    old_p->is_pure_ack = packet_is_pure_ack;
                    old_p->is_evaluated = 1;
                } else {
                    contains_crypto = old_p->contains_crypto;
                    packet_is_pure_ack = old_p->is_pure_ack;
                }

                if (contains_crypto) {
                    length = picoquic_predict_packet_header_length(cnx, picoquic_packet_0rtt_protected);
                    packet->ptype = picoquic_packet_0rtt_protected;
                    packet->offset = length;
                } else if (cnx->cnx_state < picoquic_state_client_ready_start) {
                    should_retransmit = 0;
                } else {
                    length = picoquic_predict_packet_header_length(cnx, picoquic_packet_1rtt_protected);
                    packet->ptype = picoquic_packet_1rtt_protected;
                    packet->offset = length;
                }
            } else {
                length = picoquic_predict_packet_header_length(cnx, old_p->ptype);
                packet->ptype = old_p->ptype;
                packet->offset = length;
            }

            if (should_retransmit != 0) {
                packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
                packet->send_path = path_x;
                packet->pc = pc;

                *header_length = length;

                switch (packet->ptype) {
                case picoquic_packet_1rtt_protected:
                    checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt);
                    break;
                case picoquic_packet_initial:
                    checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_initial);
                    break;
                case picoquic_packet_handshake:
                    checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_handshake);
                    break;
                case picoquic_packet_0rtt_protected:
                    checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_0rtt);
                    break;
                default:
                    DBG_PRINTF("Trying to retransmit packet type %d", old_p->ptype);
                    checksum_length = 0;
                    break;
                }

                ret = picoquic_copy_before_retransmit(old_p, cnx,
                    new_bytes,
                    send_buffer_max - checksum_length,
                    &packet_is_pure_ack,
                    &do_not_detect_spurious,
                    &length);

                if (ret != 0) {
                    DBG_PRINTF("Copy before retransmit returns %d\n", ret);
                }

                /* Update the number of bytes in transit and remove old packet from queue */
                /* If not pure ack, the packet will be placed in the "retransmitted" queue,
                 * in order to enable detection of spurious restransmissions */

                picoquic_log_packet_lost(cnx, old_p->ptype, old_p->sequence_number,
                        (timer_based_retransmit == 0) ? "repeat" : "timer",
                        (old_p->send_path == NULL) ? NULL : &old_p->send_path->remote_cnxid,
                        old_p->length, current_time);

                old_p = picoquic_dequeue_retransmit_packet(cnx, old_p, packet_is_pure_ack & do_not_detect_spurious);

                /* If we have a good packet, return it */
                if (old_p == NULL || packet_is_pure_ack) {
                    length = 0;
                } else {
                    if (old_p->send_path != NULL &&
                        (old_p->length + old_p->checksum_overhead) == old_p->send_path->send_mtu) {
                        old_p->send_path->nb_mtu_losses++;
                        if (old_p->send_path->nb_mtu_losses > PICOQUIC_MTU_LOSS_THRESHOLD) {
                            picoquic_reset_path_mtu(old_p->send_path);
                            picoquic_log_app_message(cnx,
                                "Reset path MTU after %d retransmissions, %d MTU losses",
                                cnx->pkt_ctx[pc].nb_retransmit,
                                old_p->send_path->nb_mtu_losses);
                        }
                    }

                    if (timer_based_retransmit != 0) {
                        if (cnx->pkt_ctx[pc].nb_retransmit > 7 && cnx->cnx_state >= picoquic_state_ready) {
                            /*
                             * Max retransmission count was exceeded. Disconnect.
                             */
                            DBG_PRINTF("Too many retransmits of packet number %d, disconnect", (int)old_p->sequence_number);
                            cnx->cnx_state = picoquic_state_disconnected;
                            if (cnx->callback_fn) {
                                (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL);
                            }
                            length = 0;
                            break;
                        } else {
                            if (cnx->pkt_ctx[pc].nb_retransmit == 0 ||
                                old_p->sequence_number >= cnx->pkt_ctx[pc].retransmit_sequence) {
                                cnx->pkt_ctx[pc].nb_retransmit++;
                                cnx->pkt_ctx[pc].latest_retransmit_time = current_time;
                                cnx->pkt_ctx[pc].retransmit_sequence = packet->sequence_number;
                            }
                        }
                    }

                    if (old_p->ptype < picoquic_packet_1rtt_protected) {
                        DBG_PRINTF("Retransmit packet type %d, pc=%d, seq = %llx, is_client = %d\n",
                            old_p->ptype, old_p->pc,
                            (unsigned long long)old_p->sequence_number, cnx->client_mode);
                    }

                    /* special case for the client initial */
                    if (old_p->ptype == picoquic_packet_initial && cnx->client_mode) {
                        length = picoquic_pad_to_target_length(new_bytes, length, send_buffer_max - checksum_length);
                    }
                    packet->length = length;
                    cnx->nb_retransmission_total++;

                    if (old_path != NULL) {
                        old_path->nb_losses_found++;
                        old_path->total_bytes_lost += old_p->length;

                        if (cnx->congestion_alg != NULL && cnx->cnx_state >= picoquic_state_ready) {
                            cnx->congestion_alg->alg_notify(cnx, old_path,
                                (timer_based_retransmit == 0) ? picoquic_congestion_notification_repeat : picoquic_congestion_notification_timeout,
                                0, 0, 0, lost_packet_number, current_time);
                        }
                    }
                    
                    if (length <= packet->offset) {
                        length = 0;
                        if (!packet_is_pure_ack) {
                            /* Pace down the next retransmission so as to not pile up error upon error */
                            path_x->pacing_bucket_nanosec -= path_x->pacing_packet_time_nanosec;
                        }
                    }
                    else {
                        break;
                    }
                }
            }
        }
        /*
         * If the loop is continuing, this means that we need to look
         * at the next candidate packet.
         */
        old_p = p_next;
    }

    return (int)length;
}

/*
 * Returns true if there is nothing to repeat in the retransmission queue
 */
int picoquic_is_cnx_backlog_empty(picoquic_cnx_t* cnx)
{
    int backlog_empty = 1;

    for (picoquic_packet_context_enum pc = 0;
        backlog_empty == 1 && pc < picoquic_nb_packet_context; pc++)
    {
        picoquic_packet_t* p = cnx->pkt_ctx[pc].retransmit_oldest;

        if ((cnx->cnx_state == picoquic_state_ready) && (pc != picoquic_packet_context_application)) {
            continue;
        }

        while (p != NULL && backlog_empty == 1) {
            /* check if this is an ACK only packet */
            int ret = 0;
            int frame_is_pure_ack = 0;
            size_t frame_length = 0;
            size_t byte_index = 0; /* Used when parsing the old packet */

            byte_index = p->offset;


            while (ret == 0 && byte_index < p->length) {
                ret = picoquic_skip_frame(&p->bytes[byte_index],
                    p->length - p->offset, &frame_length, &frame_is_pure_ack);

                if (!frame_is_pure_ack) {
                    backlog_empty = 0;
                    break;
                }
                byte_index += frame_length;
            }

            p = p->previous_packet;
        }
    }

    return backlog_empty;
}

/* Decide whether MAX data need to be sent or not */
int picoquic_should_send_max_data(picoquic_cnx_t* cnx)
{
    int ret = 0;

    if (2 * cnx->data_received > cnx->maxdata_local)
        ret = 1;

    return ret;
}

/* Compute the next logical probe length */
static size_t picoquic_next_mtu_probe_length(picoquic_cnx_t* cnx, picoquic_path_t * path_x)
{
    size_t probe_length;

    if (path_x->send_mtu_max_tried == 0) {
        if (cnx->remote_parameters.max_packet_size > 0) {
            probe_length = cnx->remote_parameters.max_packet_size;

            if (cnx->quic->mtu_max > 0 && (int)probe_length > cnx->quic->mtu_max) {
                probe_length = cnx->quic->mtu_max;
            }
            else if (probe_length > PICOQUIC_MAX_PACKET_SIZE) {
                probe_length = PICOQUIC_MAX_PACKET_SIZE;
            }
            if (probe_length < path_x->send_mtu) {
                probe_length = path_x->send_mtu;
            }
        }
        else if (cnx->quic->mtu_max > 0) {
            probe_length = cnx->quic->mtu_max;
        }
        else {
            probe_length = PICOQUIC_PRACTICAL_MAX_MTU;
        }
    }
    else {
        if (path_x->send_mtu_max_tried > 1500) {
            probe_length = 1500;
        }
        else if (path_x->send_mtu_max_tried > 1400) {
            probe_length = 1400;
        }
        else {
            probe_length = (path_x->send_mtu + path_x->send_mtu_max_tried) / 2;
        }
    }

    return probe_length;
}

/* Decide whether to send an MTU probe */
picoquic_pmtu_discovery_status_enum picoquic_is_mtu_probe_needed(picoquic_cnx_t* cnx, picoquic_path_t * path_x)
{
    int ret = picoquic_pmtu_discovery_not_needed;

    if ((cnx->cnx_state == picoquic_state_ready || cnx->cnx_state == picoquic_state_client_ready_start || cnx->cnx_state == picoquic_state_server_false_start)
        && path_x->mtu_probe_sent == 0) {
        if (path_x->send_mtu_max_tried == 0 || path_x->send_mtu_max_tried > 1400) {
            /* MTU discovery is required if the chances of success are large enough
             * and there are enough packets to send to amortize the discovery cost.
             * Of course we don't know at this stage how much data will be sent 
             * on the connection; we take the amount of data queued as a proxy
             * for that. */
            uint64_t next_probe = picoquic_next_mtu_probe_length(cnx, path_x);
            if (next_probe > path_x->send_mtu) {
                if (cnx->is_pmtud_required) {
                    ret = picoquic_pmtu_discovery_required;
                }
                else {
                    uint64_t packets_to_send_before = cnx->nb_bytes_queued / path_x->send_mtu;
                    uint64_t packets_to_send_after = cnx->nb_bytes_queued / next_probe;
                    uint64_t delta = (packets_to_send_before - packets_to_send_after) * 60;
                    if (delta > next_probe) {
                        ret = picoquic_pmtu_discovery_required;
                    }
                    else {
                        ret = picoquic_pmtu_discovery_optional;
                    }
                }
            }
        }
    }

    return ret;
}

/* Prepare an MTU probe packet */
size_t picoquic_prepare_mtu_probe(picoquic_cnx_t* cnx,
    picoquic_path_t * path_x,
    size_t header_length, size_t checksum_length,
    uint8_t* bytes, size_t bytes_max)
{
    size_t probe_length = picoquic_next_mtu_probe_length(cnx, path_x);
    size_t length = header_length;

    if (probe_length > bytes_max) {
        probe_length = bytes_max;
    }

    bytes[length++] = picoquic_frame_type_ping;
    memset(&bytes[length], 0, probe_length - checksum_length - length);

    return probe_length - checksum_length;
}

/* Prepare the next packet to 0-RTT packet to send in the client initial
 * state, when 0-RTT is available
 */
int picoquic_prepare_packet_0rtt(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
    int padding_required, uint64_t * next_wake_time)
{
    int ret = 0;
    picoquic_stream_head_t* stream = NULL;
    picoquic_packet_type_enum packet_type = picoquic_packet_0rtt_protected;
    size_t header_length = 0;
    uint8_t* bytes = packet->bytes;
    size_t length = 0;
    size_t checksum_overhead = picoquic_aead_get_checksum_length(cnx->crypto_context[1].aead_encrypt);
    uint8_t* bytes_max;
    uint8_t* bytes_next;
    int more_data = 0;
    int is_pure_ack = 1;
    int stream_tried_and_failed = 0;

    send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;
    if (path_x->bytes_in_transit + send_buffer_max > PICOQUIC_DEFAULT_0RTT_WINDOW) {
        if (path_x->bytes_in_transit > PICOQUIC_DEFAULT_0RTT_WINDOW) {
            send_buffer_max = 0;
        }
        else {
            send_buffer_max = (size_t)PICOQUIC_DEFAULT_0RTT_WINDOW - (size_t)path_x->bytes_in_transit;
        }
    }
    bytes_max = bytes + send_buffer_max - checksum_overhead;

    stream = picoquic_find_ready_stream(cnx);
    length = picoquic_predict_packet_header_length(cnx, packet_type);
    packet->ptype = picoquic_packet_0rtt_protected;
    packet->offset = length;
    header_length = length;
    packet->pc = picoquic_packet_context_application;
    packet->sequence_number = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence;
    packet->send_time = current_time;
    packet->send_path = path_x;
    packet->checksum_overhead = checksum_overhead;
    bytes_next = bytes + length;


    
    /* Consider sending 0-RTT */
    if ((stream == NULL && cnx->first_misc_frame == NULL && padding_required == 0) || 
        send_buffer_max < PICOQUIC_MIN_SEGMENT_SIZE) {
        length = 0;
    } else {
        /* If present, send misc frame */
        while (cnx->first_misc_frame != NULL) {
            uint8_t* bytes_misc = bytes_next;
            bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
            if (bytes_next == bytes_misc) {
                break;
            }
        }

        /* Encode the stream frame, or frames */
        bytes_next = picoquic_format_available_stream_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, &stream_tried_and_failed, &ret);

        length = bytes_next - bytes;

        if (more_data) {
            *next_wake_time = current_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }

        if (stream_tried_and_failed) {
            path_x->last_sender_limited_time = current_time;
        }

        /* Add padding if required */
        if (padding_required) {
            length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
        }
    }

    picoquic_finalize_and_protect_packet(cnx, packet,
        ret, length, header_length, checksum_overhead,
        send_length, send_buffer, send_buffer_max,
        &path_x->remote_cnxid,
        &path_x->p_local_cnxid->cnx_id,
        path_x, current_time);

    if (length > 0) {
        /* Accounting of zero rtt packets sent */
        cnx->nb_zero_rtt_sent++;
    }

    /* the reinsertion by wake up time will happen in the calling function */

    return ret;
}

/* Get packet type from epoch */
picoquic_packet_type_enum picoquic_packet_type_from_epoch(int epoch)
{
    picoquic_packet_type_enum ptype;

    switch (epoch) {
    case 0:
        ptype = picoquic_packet_initial;
        break;
    case 1:
        ptype = picoquic_packet_0rtt_protected;
        break;
    case 2:
        ptype = picoquic_packet_handshake;
        break;
    case 3:
        ptype = picoquic_packet_1rtt_protected;
        break;
    default:
        ptype = picoquic_packet_error;
        break;
    }

    return ptype;
}

/* Prepare a required repetition or ack in a previous context */
size_t picoquic_prepare_packet_old_context(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc,
    picoquic_path_t* path_x, picoquic_packet_t* packet, size_t send_buffer_max, uint64_t current_time,
    uint64_t* next_wake_time, size_t* header_length)
{
    picoquic_epoch_enum epoch = (pc == picoquic_packet_context_initial) ? picoquic_epoch_initial :
        (pc == picoquic_packet_context_application) ? picoquic_epoch_0rtt : picoquic_epoch_handshake;
    size_t length = 0;
    uint8_t* bytes = packet->bytes;
    int more_data = 0;
    size_t checksum_overhead = picoquic_get_checksum_length(cnx, epoch);
    uint8_t* bytes_max = bytes + send_buffer_max - checksum_overhead;
    uint8_t* bytes_next;

    *header_length = 0;

    send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;

    length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, send_buffer_max, header_length);
    if (length > 0 && (pc == picoquic_packet_context_handshake || cnx->pkt_ctx[picoquic_packet_context_handshake].retransmit_oldest == NULL ||
        cnx->cnx_state == picoquic_state_server_init || cnx->cnx_state == picoquic_state_server_handshake)) {
        cnx->initial_repeat_needed = 0;
    }

    if (length == 0 && cnx->pkt_ctx[pc].ack_needed != 0 &&
        pc != picoquic_packet_context_application) {
        packet->ptype =
            (pc == picoquic_packet_context_initial) ? picoquic_packet_initial :
            (pc == picoquic_packet_context_handshake) ? picoquic_packet_handshake :
                picoquic_packet_0rtt_protected;
        length = picoquic_predict_packet_header_length(cnx, packet->ptype);
        packet->offset = length;
        *header_length = length;
        packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
        packet->send_time = current_time;
        packet->send_path = path_x;
    }

    if (length > 0) {
        if (packet->ptype != picoquic_packet_0rtt_protected) {
            /* Check whether it makes sens to add an ACK at the end of the retransmission */
            bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc);
            length = bytes_next - bytes;
        }
        packet->length = length;
        /* document the send time & overhead */
        packet->send_time = current_time;
        packet->checksum_overhead = checksum_overhead;
        packet->pc = pc;
    }

    return length;
}

/* Empty the handshake repeat queues when transitioning to the completely ready state */
void picoquic_implicit_handshake_ack(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc, uint64_t current_time)
{
    picoquic_packet_t* p = cnx->pkt_ctx[pc].retransmit_oldest;

    /* Remove packets from the retransmit queue */
    while (p != NULL) {
        picoquic_packet_t* p_next = p->next_packet;
        picoquic_path_t * old_path = p->send_path;

        /* Update the congestion control state for the path, but only for the packets sent
         * before the initial timer. */
        if (old_path != NULL && cnx->congestion_alg != NULL && p->send_time < cnx->start_time + PICOQUIC_INITIAL_RTT) {
            cnx->congestion_alg->alg_notify(cnx, old_path,
                picoquic_congestion_notification_acknowledgement,
                0, 0, p->length, 0, current_time);
        }
        /* Update the number of bytes in transit and remove old packet from queue */
        /* The packet will not be placed in the "retransmitted" queue */
        (void)picoquic_dequeue_retransmit_packet(cnx, p, 1);

        p = p_next;
    }
}

/* Program a migration to the server preferred address if present */
int picoquic_prepare_server_address_migration(picoquic_cnx_t* cnx)
{
    int ret = 0;

    if (cnx->remote_parameters.prefered_address.is_defined)
    {
        int ipv4_received = cnx->remote_parameters.prefered_address.ipv4Port != 0;
        int ipv6_received = cnx->remote_parameters.prefered_address.ipv6Port != 0;

        /* Add the connection ID to the local stash */
        ret = picoquic_enqueue_cnxid_stash(cnx, 1,
            cnx->remote_parameters.prefered_address.connection_id.id_len,
            cnx->remote_parameters.prefered_address.connection_id.id,
            cnx->remote_parameters.prefered_address.statelessResetToken,
            NULL);
        if (ret != 0) {
            ret = picoquic_connection_error(cnx, (uint16_t)ret, picoquic_frame_type_new_connection_id);
        }
        else if(ipv4_received || ipv6_received) {
            struct sockaddr_storage dest_addr;

            memset(&dest_addr, 0, sizeof(struct sockaddr_storage));

            /* program a migration. */
            if (ipv4_received && cnx->path[0]->peer_addr.ss_family == AF_INET) {
                /* select IPv4 */
                ipv6_received = 0;
            }

            if (ipv6_received) {
                /* configure an IPv6 sockaddr */
                struct sockaddr_in6 * d6 = (struct sockaddr_in6 *)&dest_addr;
                d6->sin6_family = AF_INET6;
                d6->sin6_port = htons(cnx->remote_parameters.prefered_address.ipv6Port);
                memcpy(&d6->sin6_addr, cnx->remote_parameters.prefered_address.ipv6Address, 16);
            }
            else {
                /* configure an IPv4 sockaddr */
                struct sockaddr_in * d4 = (struct sockaddr_in *)&dest_addr;
                d4->sin_family = AF_INET;
                d4->sin_port = htons(cnx->remote_parameters.prefered_address.ipv4Port);
                memcpy(&d4->sin_addr, cnx->remote_parameters.prefered_address.ipv4Address, 4);
            }

            /* Only send a probe if not already using that address */
            if (picoquic_compare_addr((struct sockaddr *)&dest_addr, (struct sockaddr *)&cnx->path[0]->peer_addr) != 0) {
                struct sockaddr* local_addr = NULL;
                if (cnx->path[0]->local_addr.ss_family != 0) {
                    local_addr = (struct sockaddr*) & cnx->path[0]->local_addr;
                }

                ret = picoquic_probe_new_path_ex(cnx, (struct sockaddr *)&dest_addr, local_addr,
                    picoquic_get_quic_time(cnx->quic), 1);
            }
        }
    }

    return ret;
}

/* Prepare the next packet to send when in one of the client initial states */
int picoquic_prepare_packet_client_init(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t * next_wake_time,
    int * is_initial_sent)
{
    int ret = 0;
    int tls_ready = 0;
    picoquic_packet_type_enum packet_type = 0;
    size_t checksum_overhead = 16;
    int is_cleartext_mode = 1;
    int retransmit_possible = 0;
    size_t header_length = 0;
    uint8_t* bytes = packet->bytes;
    uint8_t* bytes_max;
    uint8_t* bytes_next;
    size_t length = 0;
    int epoch = 0;
    int is_pure_ack = 1;
    int more_data = 0;
    picoquic_packet_context_enum pc = picoquic_packet_context_initial;

    if (*next_wake_time > cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX) {
        *next_wake_time = cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
    }

    cnx->initial_validated = 1; /* always validated on client */

    if (cnx->tls_stream[0].send_queue == NULL) {
        if (cnx->crypto_context[1].aead_encrypt != NULL &&
            cnx->tls_stream[1].send_queue != NULL) {
            epoch = 1;
            pc = picoquic_packet_context_application;
        } else if (cnx->crypto_context[2].aead_encrypt != NULL && 
            cnx->tls_stream[1].send_queue == NULL) {
            epoch = 2;
            pc = picoquic_packet_context_handshake;
        } 
    }

    packet_type = picoquic_packet_type_from_epoch(epoch);

    send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;

    /* Prepare header -- depend on connection state */
    switch (cnx->cnx_state) {
    case picoquic_state_client_init:
        if (cnx->retry_token_length == 0 && cnx->sni != NULL) {
            (void)picoquic_get_token(cnx->quic->p_first_token, current_time, cnx->sni, (uint16_t)strlen(cnx->sni),
                NULL, 0, &cnx->retry_token, &cnx->retry_token_length, 1);
        }
        break;
    case picoquic_state_client_init_sent:
    case picoquic_state_client_init_resent:
        retransmit_possible = 1;
        break;
    case picoquic_state_client_renegotiate:
        packet_type = picoquic_packet_initial;
        break;
    case picoquic_state_client_handshake_start:
        retransmit_possible = 1;
        break;
    case picoquic_state_client_almost_ready:
        break;
    default:
        ret = -1;
        break;
    }

    /* If context is handshake, verify first that there is no need for retransmit or ack
     * on initial context */
    int force_handshake_padding = 0;

    if (ret == 0) {
        if (epoch > picoquic_epoch_initial) {
            if (cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt != NULL) {
                if (cnx->pkt_ctx[picoquic_packet_context_initial].ack_needed) {
                    /* Apply some ack delay, because handshake from server arrive in trains */
                    uint64_t ack_delay = cnx->path[0]->smoothed_rtt / 8;
                    uint64_t ack_time;
                    if (ack_delay > PICOQUIC_ACK_DELAY_MAX) {
                        ack_delay = PICOQUIC_ACK_DELAY_MAX;
                    }
                    ack_time = cnx->pkt_ctx[picoquic_packet_context_initial].time_oldest_unack_packet_received + ack_delay;
                    if (ack_time <= current_time) {
                        force_handshake_padding = 1;
                    }
                    else if (ack_time < *next_wake_time) {
                        *next_wake_time = ack_time;
                        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                    }
                }
                else if (!force_handshake_padding && cnx->pkt_ctx[pc].retransmit_newest != NULL) {
                    /* There is a risk of deadlock if the server is doing DDOS mitigation
                     * and does not receive the Handshake sent by the client. If more than RTT has elapsed since
                     * the last handshake packet was sent, force another one to be sent. */
                    uint64_t rto = picoquic_current_retransmit_timer(cnx, picoquic_packet_context_handshake);
                    uint64_t repeat_time = cnx->pkt_ctx[pc].retransmit_newest->send_time + rto;

                    if (repeat_time <= current_time) {
                        force_handshake_padding = 1;
                        cnx->pkt_ctx[pc].nb_retransmit++;
                    }
                    else if (repeat_time < *next_wake_time) {
                        *next_wake_time = repeat_time;
                        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                    }
                }
            }
            else {
                length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_initial,
                    path_x, packet, send_buffer_max, current_time, next_wake_time, &header_length);
                *is_initial_sent |= (length > 0);
            }
        }
        else {
            /* There is a risk of deadlock if the server is doing DDOS mitigation
             * and does not repeat an initial or handshake packet that was lost. If more than RTT has elapsed since
             * the last initial packet was sent, force another one to be sent. */
            uint64_t rto = picoquic_current_retransmit_timer(cnx, picoquic_packet_context_initial);
            uint64_t repeat_time = cnx->path[0]->latest_sent_time + rto;
            force_handshake_padding = (repeat_time <= current_time);
        }
    }

    if (ret == 0 && epoch > picoquic_epoch_0rtt && length == 0 &&
        cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt != NULL) {
        length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_application,
            path_x, packet, send_buffer_max, current_time, next_wake_time, &header_length);
    }

    /* If there is nothing to send in previous context, check this one too */
    if (length == 0) {
        checksum_overhead = picoquic_get_checksum_length(cnx, epoch);
        packet->checksum_overhead = checksum_overhead;
        bytes_max = bytes + send_buffer_max - checksum_overhead;
        packet->pc = pc;

        tls_ready = picoquic_is_tls_stream_ready(cnx);

        if (ret == 0 && retransmit_possible &&
            (length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, send_buffer_max, &header_length)) > 0) {
            /* Check whether it makes sens to add an ACK at the end of the retransmission */
            if (epoch != picoquic_epoch_0rtt) {
                bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc);
                length = bytes_next - bytes;
            } 
            /* document the send time & overhead */
            packet->length = length;
            packet->send_time = current_time;
            packet->checksum_overhead = checksum_overhead;
        }
        else if (ret == 0 && is_cleartext_mode && tls_ready == 0
            && cnx->first_misc_frame == NULL && !cnx->pkt_ctx[pc].ack_needed && !force_handshake_padding) {
            /* when in a clear text mode, only send packets if there is
            * actually something to send, or resend. */

            packet->length = 0;
        }
        else if (ret == 0) {
            if (cnx->crypto_context[epoch].aead_encrypt == NULL) {
                packet->length = 0;
            }
            else {
                length = picoquic_predict_packet_header_length(cnx, packet_type);
                packet->ptype = packet_type;
                packet->offset = length;
                header_length = length;
                packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
                packet->send_time = current_time;
                packet->send_path = path_x;
                bytes_next = bytes + length;
                bytes_max = bytes + send_buffer_max - checksum_overhead;

                if ((tls_ready == 0 || path_x->cwin <= path_x->bytes_in_transit)
                    && (cnx->cnx_state == picoquic_state_client_almost_ready
                        || picoquic_is_ack_needed(cnx, current_time, next_wake_time, pc) == 0)
                    && cnx->first_misc_frame == NULL && !force_handshake_padding) {
                    length = 0;
                }
                else {
                    if (epoch != 1 && cnx->pkt_ctx[pc].ack_needed) {
                        bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc);
                    }

                    /* If present, send misc frame */
                    while (cnx->first_misc_frame != NULL) {
                        uint8_t* bytes_misc = bytes_next;
                        bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                        if (bytes_next == bytes_misc) {
                            break;
                        }
                    }
                    length = bytes_next - bytes;

                    if (ret == 0 && path_x->cwin > path_x->bytes_in_transit) {
                        /* Encode the crypto handshake frame */
                        if (tls_ready != 0) {
                            /* Encode the crypto frame */
                            bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[epoch],
                                bytes_next, bytes_max, &more_data, &is_pure_ack);
                            length = bytes_next - bytes;
                        }

                        if (packet_type == picoquic_packet_initial) {
                            *is_initial_sent = 1;
                            if (cnx->crypto_context[1].aead_encrypt == NULL ||
                                cnx->cnx_state == picoquic_state_client_renegotiate ||
                                cnx->original_cnxid.id_len != 0) {
                                /* Pad to minimum packet length. But don't do that if the
                                 * initial packet will be coalesced with 0-RTT packet */
                                length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
                            }
                        }
                    }

                    if (length == header_length) {
                        length = picoquic_pad_to_target_length(bytes, length, length + 8);
                    }

                    if (length > header_length && epoch == picoquic_epoch_handshake) {
                        cnx->pkt_ctx[picoquic_packet_context_initial].ack_needed = 0;
                    }

                    /* If TLS packets are sent, progress the state */
                    if (ret == 0 && tls_ready != 0 && 
                        cnx->tls_stream[epoch].send_queue == NULL) {
                        switch (cnx->cnx_state) {
                        case picoquic_state_client_init:
                            cnx->cnx_state = picoquic_state_client_init_sent;
                            break;
                        case picoquic_state_client_renegotiate:
                            cnx->cnx_state = picoquic_state_client_init_resent;
                            break;
                        case picoquic_state_client_almost_ready:
                            if (cnx->tls_stream[0].send_queue == NULL &&
                                cnx->tls_stream[1].send_queue == NULL &&
                                cnx->tls_stream[2].send_queue == NULL) {
                                cnx->cnx_state = picoquic_state_client_ready_start;
                                /* Reset the HS retransmission count, since end of flight counts as acknowledgement */
                                cnx->pkt_ctx[picoquic_packet_context_handshake].nb_retransmit = 0;
                                /* Signal the application, because data can now be sent. */
                                if (cnx->callback_fn != NULL) {
                                    if (cnx->callback_fn(cnx, 0, NULL, 0, picoquic_callback_almost_ready, cnx->callback_ctx, NULL) != 0) {
                                        picoquic_log_app_message(cnx, "Callback almost ready returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR);
                                        picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0);
                                    }
                                }
                            }
                            break;
                        default:
                            break;
                        }
                    }
                }
            }
        }
    }

    if (ret == 0 && length == 0) {
        /* In some circumstances, there is a risk that the handshakes stops because the
         * server is performing anti-dos mitigation and the client has nothing to repeat */
        if ((packet->ptype == picoquic_packet_initial && cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt == NULL &&
            cnx->pkt_ctx[picoquic_packet_context_initial].retransmit_newest == NULL &&
            cnx->pkt_ctx[picoquic_packet_context_initial].first_sack_item.end_of_sack_range != UINT64_MAX) ||
            (packet->ptype == picoquic_packet_handshake &&
                cnx->pkt_ctx[picoquic_packet_context_handshake].retransmit_newest == NULL &&
                cnx->pkt_ctx[picoquic_packet_context_handshake].first_sack_item.end_of_sack_range == UINT64_MAX &&
                cnx->pkt_ctx[picoquic_packet_context_handshake].send_sequence == 0))
        {
            uint64_t try_time_next = cnx->path[0]->latest_sent_time + cnx->path[0]->smoothed_rtt;
            if (current_time < try_time_next) {
                /* schedule a wake time to repeat the probing. */
                if (*next_wake_time > try_time_next) {
                    *next_wake_time = try_time_next;
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
            }
            else {
                length = header_length;
                packet->offset = length;
                if (packet->ptype == picoquic_packet_initial) {
                    /* Repeat an ACK because it helps. */
                    bytes_max = bytes + send_buffer_max - checksum_overhead;
                    bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc);
                    length = bytes_next - bytes;

                    length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
                }
                else {
                    length = picoquic_pad_to_target_length(bytes, length, length + 8);
                }
            }
        }
    }

    if (ret == 0 && length == 0 && cnx->crypto_context[1].aead_encrypt != NULL) {
        ret = picoquic_prepare_packet_0rtt(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length,
                *is_initial_sent, next_wake_time);
    }
    else {
        if (ret == 0 && more_data) {
            *next_wake_time = current_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }

        if (ret == 0 && *is_initial_sent) {
            if (packet->ptype == picoquic_packet_initial) {
                if (length > 0 && cnx->crypto_context[1].aead_encrypt == NULL && 
                    (cnx->crypto_context[2].aead_encrypt == NULL || length + checksum_overhead + PICOQUIC_MIN_SEGMENT_SIZE > send_buffer_max ||
                        !picoquic_is_tls_stream_ready(cnx))) {
                    length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
                }
            }
            else if (packet->ptype == picoquic_packet_handshake && length + checksum_overhead < send_buffer_max &&
                (cnx->crypto_context[3].aead_encrypt == NULL || length + checksum_overhead + PICOQUIC_MIN_SEGMENT_SIZE > send_buffer_max)) {
                length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
            }
            else if (packet->ptype == picoquic_packet_1rtt_protected) {
                length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
            }
        }

        if (length > 0 && packet->ptype == picoquic_packet_handshake && !is_pure_ack) {
            /* Sending an ack eliciting handshake packet terminates the use of the initial context */
            picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_initial, current_time);
            picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_initial]);
        }

        picoquic_finalize_and_protect_packet(cnx, packet,
            ret, length, header_length, checksum_overhead,
            send_length, send_buffer, send_buffer_max,
            &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time);
    }

    return ret;
}

/* Compute the time at which to send the next challenge
 */

uint64_t picoquic_next_challenge_time(picoquic_cnx_t* cnx, picoquic_path_t* path_x)
{
    uint64_t next_challenge_time = path_x->challenge_time;

    if (path_x->challenge_repeat_count >= 2) {
        next_challenge_time += path_x->retransmit_timer << path_x->challenge_repeat_count;
    }
    else {
        next_challenge_time += PICOQUIC_INITIAL_RETRANSMIT_TIMER;
    }

    return next_challenge_time;
}

/* Prepare the next packet to send when in one the server initial states */
int picoquic_prepare_packet_server_init(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t * next_wake_time)
{
    int ret = 0;
    int tls_ready = 0;
    picoquic_epoch_enum epoch = picoquic_epoch_initial;
    picoquic_packet_type_enum packet_type = picoquic_packet_initial;
    picoquic_packet_context_enum pc = picoquic_packet_context_initial;
    size_t checksum_overhead = 8;
    size_t header_length = 0;
    uint8_t* bytes = packet->bytes;
    uint8_t* bytes_max;
    uint8_t* bytes_next;
    size_t length = 0;
    int more_data = 0;
    int is_pure_ack = 1;

    if (*next_wake_time > cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX) {
        *next_wake_time = cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
    }

    /* The only purpose of the test below is to appease the static analyzer, so it
     * wont complain of possible NULL deref. On windows we could use "__assume(path_x != NULL)"
     * but the documentation does not say anything about that for GCC and CLANG */
    if (path_x == NULL) {
        return PICOQUIC_ERROR_UNEXPECTED_ERROR;
    }

    if (cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt != NULL &&
        cnx->tls_stream[0].send_queue == NULL) {
        epoch = picoquic_epoch_handshake;
        pc = picoquic_packet_context_handshake;
        packet_type = picoquic_packet_handshake;
    }

    send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;

    if (!cnx->initial_validated &&
        (cnx->initial_data_sent + send_buffer_max) > 3 * cnx->initial_data_received){
        /* Sending more data now would break the amplication limit */
        *send_length = 0;
        return 0;
    }

    /* If context is handshake, verify first that there is no need for retransmit or ack
     * on initial context */
    if (pc == picoquic_packet_context_handshake) {
        length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_initial,
            path_x, packet, send_buffer_max, current_time, next_wake_time, &header_length);
    }

    if (length == 0) {
        checksum_overhead = picoquic_get_checksum_length(cnx, epoch);
        bytes_max = bytes + send_buffer_max - checksum_overhead;
        tls_ready = (cnx->tls_stream[epoch].send_queue != NULL &&
            cnx->tls_stream[epoch].send_queue->length > cnx->tls_stream[epoch].send_queue->offset);
        length = picoquic_predict_packet_header_length(cnx, packet_type);
        packet->ptype = packet_type;
        packet->offset = length;
        header_length = length;
        packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
        packet->send_time = current_time;
        packet->send_path = path_x;
        packet->pc = pc;
        bytes_next = bytes + length;

        if ((tls_ready != 0 && path_x->cwin > path_x->bytes_in_transit) 
            || cnx->pkt_ctx[pc].ack_needed) {
            bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc);
            /* Encode the crypto frame */
            bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[epoch],
                bytes_next, bytes_max, &more_data, &is_pure_ack);
            length = bytes_next - bytes;

            /* progress the state if the epoch data is all sent */
            if (ret == 0 && tls_ready != 0 && cnx->tls_stream[epoch].send_queue == NULL) {
                if (epoch == picoquic_epoch_handshake && picoquic_tls_client_authentication_activated(cnx->quic) == 0) {
                    cnx->cnx_state = picoquic_state_server_false_start;
                    /* On a server that does address validation, send a NEW TOKEN frame */
                    if (!cnx->client_mode && (cnx->quic->check_token || cnx->quic->provide_token)) {
                        uint8_t token_buffer[256];
                        size_t token_size;
                        picoquic_connection_id_t n_cid = picoquic_null_connection_id;

                        if (picoquic_prepare_retry_token(cnx->quic, (struct sockaddr *)&cnx->path[0]->peer_addr,
                            current_time + PICOQUIC_TOKEN_DELAY_LONG, &n_cid, &n_cid, 0,
                            token_buffer, sizeof(token_buffer), &token_size) == 0) {
                            if (picoquic_queue_new_token_frame(cnx, token_buffer, token_size) != 0) {
                                picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, picoquic_frame_type_new_token);
                            }
                        }
                    }
                    if (cnx->callback_fn != NULL) {
                        if (cnx->callback_fn(cnx, 0, NULL, 0, picoquic_callback_almost_ready, cnx->callback_ctx, NULL) != 0) {
                            picoquic_log_app_message(cnx, "Callback almost ready returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR);
                            picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0);
                        }
                    }
                }
                else {
                    cnx->cnx_state = picoquic_state_server_handshake;
                }
            }
            packet->length = length;
        }
        else  if ((length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, send_buffer_max, &header_length)) > 0) {
            /* Set the new checksum length */
            checksum_overhead = picoquic_get_checksum_length(cnx, epoch);
            cnx->initial_repeat_needed = 0;
            /* Check whether it makes sens to add an ACK at the end of the retransmission */
            bytes_max = bytes + send_buffer_max - checksum_overhead;
            bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc);
            length = bytes_next - bytes;
            packet->length = length;
            /* document the send time & overhead */
            packet->send_time = current_time;
            packet->checksum_overhead = checksum_overhead;
        }
        else if (cnx->pkt_ctx[pc].ack_needed) {
            /* when in a handshake mode, send acks asap. */
            length = picoquic_predict_packet_header_length(cnx, packet_type);
            bytes_next = bytes + length;
            bytes_max = bytes + send_buffer_max - checksum_overhead;
            bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc);
            length = bytes_next - bytes;
        } else {
            length = 0;
            packet->length = 0;
        }
    }

    if (ret == 0 && length == 0 && more_data) {
        *next_wake_time = current_time;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
    }

    picoquic_finalize_and_protect_packet(cnx, packet,
        ret, length, header_length, checksum_overhead,
        send_length, send_buffer, send_buffer_max,
        &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time);

    /* Account for data sent during handshake */
    if (!cnx->initial_validated) {
        cnx->initial_data_sent += *send_length;
    }

    return ret;
}

/* Prepare the next packet to send when in one the closing states */
int picoquic_prepare_packet_closing(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t * next_wake_time)
{
    int ret = 0;
    /* TODO: manage multiple streams. */
    picoquic_packet_type_enum packet_type = 0;
    size_t checksum_overhead = 8;
    size_t header_length = 0;
    uint8_t* bytes = packet->bytes;
    uint8_t* bytes_max;
    uint8_t* bytes_next;
    int more_data = 0;
    size_t length = 0;
    int is_pure_ack = 1;
    picoquic_packet_context_enum pc = picoquic_packet_context_application;
    picoquic_epoch_enum epoch = picoquic_epoch_1rtt;

    /* The only purpose of the test below is to appease the static analyzer, so it
     * wont complain of possible NULL deref. On windows we could use "__assume(path_x != NULL)"
     * but the documentation does not say anything about that for GCC and CLANG */
    if (path_x == NULL) {
        return PICOQUIC_ERROR_UNEXPECTED_ERROR;
    }

    send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;

    /* Prepare header -- depend on connection state */
    /* TODO: 0-RTT work. */
    switch (cnx->cnx_state) {
    case picoquic_state_handshake_failure:
        /* TODO: check whether closing can be requested in "initial" mode */
        if (cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt != NULL &&
            cnx->pkt_ctx[picoquic_packet_context_handshake].first_sack_item.start_of_sack_range != (uint64_t)((int64_t)-1)) {
            pc = picoquic_packet_context_handshake;
            packet_type = picoquic_packet_handshake;
            epoch = picoquic_epoch_handshake;
        }
        else {
            pc = picoquic_packet_context_initial;
            packet_type = picoquic_packet_initial;
        }
        break;
    case picoquic_state_handshake_failure_resend:
        pc = picoquic_packet_context_handshake;
        packet_type = picoquic_packet_handshake;
        epoch = picoquic_epoch_handshake;
        break;
    case picoquic_state_disconnecting:
        packet_type = picoquic_packet_1rtt_protected;
        break;
    case picoquic_state_closing_received:
        packet_type = picoquic_packet_1rtt_protected;
        break;
    case picoquic_state_closing:
        packet_type = picoquic_packet_1rtt_protected;
        break;
    case picoquic_state_draining:
        packet_type = picoquic_packet_1rtt_protected;
        break;
    case picoquic_state_disconnected:
        ret = PICOQUIC_ERROR_DISCONNECTED;
        break;
    default:
        ret = -1;
        break;
    }

    /* At this stage, we don't try to retransmit any old packet, whether in
     * the current context or in previous contexts. */

    checksum_overhead = picoquic_get_checksum_length(cnx, epoch);
    packet->pc = pc;
    bytes_max = bytes + send_buffer_max - checksum_overhead;

    if (ret == 0 && cnx->cnx_state == picoquic_state_closing_received) {
        /* Send a closing frame, move to closing state */
        uint64_t exit_time = cnx->latest_progress_time + 3 * path_x->retransmit_timer;

        length = picoquic_predict_packet_header_length(cnx, packet_type);
        bytes_next = bytes + length;
        packet->ptype = packet_type;
        packet->offset = length;
        header_length = length;
        packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
        packet->send_time = current_time;
        packet->send_path = path_x;

        /* Send the disconnect frame */
        bytes_next = picoquic_format_connection_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
        length = bytes_next - bytes;

        cnx->cnx_state = picoquic_state_draining;
        *next_wake_time = exit_time;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
    } else if (ret == 0 && cnx->cnx_state == picoquic_state_closing) {
        /* if more than 3*RTO is elapsed, move to disconnected */
        uint64_t exit_time = cnx->latest_progress_time + 3 * path_x->retransmit_timer;

        if (current_time >= exit_time) {
            cnx->cnx_state = picoquic_state_disconnected;
            *next_wake_time = current_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }
        else if (current_time >= cnx->next_wake_time) {
            uint64_t delta_t = path_x->rtt_min;
            uint64_t next_time = 0;

            if (delta_t * 2 < path_x->retransmit_timer) {
                delta_t = path_x->retransmit_timer / 2;
            }
            /* if more than N packet received, repeat and erase */
            if (cnx->pkt_ctx[pc].ack_needed) {
                length = picoquic_predict_packet_header_length(
                    cnx, packet_type);
                packet->ptype = packet_type;
                packet->offset = length;
                header_length = length;
                packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
                packet->send_time = current_time;
                packet->send_path = path_x;
                bytes_next = bytes + length;

                /* Resend the disconnect frame */
                if (cnx->local_error == 0) {
                    bytes_next = picoquic_format_application_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                } else {
                    bytes_next = picoquic_format_connection_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                }
                length = bytes_next - bytes;
                cnx->pkt_ctx[pc].ack_needed = 0;
            }
            next_time = current_time + delta_t;
            if (next_time > exit_time) {
                next_time = exit_time;
            }

            *next_wake_time = next_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }
    } else if (ret == 0 && cnx->cnx_state == picoquic_state_draining) {
        /* Nothing is ever sent in the draining state */
        /* if more than 3*RTO is elapsed, move to disconnected */
        uint64_t exit_time = cnx->latest_progress_time + 3 * path_x->retransmit_timer;

        if (current_time >= exit_time) {
            cnx->cnx_state = picoquic_state_disconnected;
            *next_wake_time = current_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }
        else {
            *next_wake_time = exit_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        }
        length = 0;
    } else if (ret == 0 && (cnx->cnx_state == picoquic_state_disconnecting || 
        cnx->cnx_state == picoquic_state_handshake_failure || 
        cnx->cnx_state == picoquic_state_handshake_failure_resend)) {
        length = picoquic_predict_packet_header_length(
            cnx, packet_type);
        bytes_next = bytes + length;
        packet->ptype = packet_type;
        packet->offset = length;
        header_length = length;
        packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
        packet->send_time = current_time;
        packet->send_path = path_x;

        /* send either app close or connection close, depending on error code */
        uint64_t delta_t = path_x->rtt_min;

        if (2 * delta_t < path_x->retransmit_timer) {
            delta_t = path_x->retransmit_timer / 2;
        }

        /* add a final ack so receiver gets clean state */
        bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc);

        /* Send the disconnect frame */
        if (cnx->local_error == 0) {
            bytes_next = picoquic_format_application_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
        }
        else {
            bytes_next = picoquic_format_connection_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
        }
        length = bytes_next - bytes;

        if (cnx->cnx_state == picoquic_state_handshake_failure) {
            if (pc == picoquic_packet_context_initial &&
                cnx->crypto_context[2].aead_encrypt != NULL) {
                cnx->cnx_state = picoquic_state_handshake_failure_resend;
            }
            else {
                cnx->cnx_state = picoquic_state_disconnected;
            }
        }
        else if (cnx->cnx_state == picoquic_state_handshake_failure_resend) {
            cnx->cnx_state = picoquic_state_disconnected;
        }
        else {
            cnx->cnx_state = picoquic_state_closing;
        }
        cnx->latest_progress_time = current_time;
        *next_wake_time = current_time + delta_t;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
        cnx->pkt_ctx[pc].ack_needed = 0;

        if (cnx->callback_fn) {
            (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL);
        }
    }
    else {
        length = 0;
    }

    if (length > 0 && packet->ptype == picoquic_packet_initial && cnx->client_mode) {
        length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead);
    }

    picoquic_finalize_and_protect_packet(cnx, packet,
        ret, length, header_length, checksum_overhead,
        send_length, send_buffer, send_buffer_max,
        &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time);

    return ret;
}

/* Create required ID, register, and format the corresponding connection ID frame */
uint8_t * picoquic_format_new_local_id_as_needed(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack)
{
    while ((cnx->remote_parameters.migration_disabled == 0 || cnx->remote_parameters.prefered_address.is_defined) &&
        cnx->local_parameters.migration_disabled == 0 &&
        cnx->nb_local_cnxid < (int)(cnx->remote_parameters.active_connection_id_limit) &&
        cnx->nb_local_cnxid <= PICOQUIC_NB_PATH_TARGET) {
        uint8_t* bytes0 = bytes;
        picoquic_local_cnxid_t* l_cid = picoquic_create_local_cnxid(cnx, NULL);

        if (l_cid == NULL) {
            /* OOPS, memory error */
            break;
        } else {
            bytes = picoquic_format_new_connection_id_frame(cnx, bytes, bytes_max, more_data, is_pure_ack, l_cid);

            if (bytes == bytes0) {
                /* Oops. Try again next time. */
                picoquic_delete_local_cnxid(cnx, l_cid);
                cnx->local_cnxid_sequence_next--;
                break;
            }
        }
    }

    return bytes;
}


void picoquic_ready_state_transition(picoquic_cnx_t* cnx, uint64_t current_time)
{
    /* Transition to server ready state.
     * The handshake is complete, all the handshake packets are implicitly acknowledged */
    cnx->cnx_state = picoquic_state_ready;
    cnx->is_handshake_finished = 1;
    picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_initial, current_time);
    picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_handshake, current_time);

    (void)picoquic_register_net_secret(cnx);
    picoquic_public_random_seed(cnx->quic);

    if (!cnx->client_mode) {
        (void)picoquic_queue_handshake_done_frame(cnx);
    }


    if (cnx->is_half_open){
        if (cnx->quic->current_number_half_open > 0) {
            cnx->quic->current_number_half_open--;
        }
        cnx->is_half_open = 0;
        if (cnx->quic->current_number_half_open < cnx->quic->max_half_open_before_retry) {
            cnx->quic->check_token = cnx->quic->force_check_token;
        }
    }

    /* Remove handshake and initial keys if they are still around */
    picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_initial]);
    picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_0rtt]);
    picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_handshake]);

    /* Set the confidentiality limit if not already set */
    if (cnx->crypto_epoch_length_max == 0) {
        cnx->crypto_epoch_length_max = 
            picoquic_aead_confidentiality_limit(cnx->crypto_context[picoquic_epoch_1rtt].aead_decrypt);
    }

    /* Start migration to server preferred address if present */
    if (cnx->client_mode) {
        (void)picoquic_prepare_server_address_migration(cnx);
    }

    /* Notify the application */
    if (cnx->callback_fn != NULL) {
        if (cnx->callback_fn(cnx, 0, NULL, 0, picoquic_callback_ready, cnx->callback_ctx, NULL) != 0) {
            picoquic_log_app_message(cnx, "Callback ready returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR);
            picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0);
        }
    }

    /* Ask for ACK frequency update, or initialize variables if not available */
    if (cnx->is_ack_frequency_negotiated) {
        cnx->is_ack_frequency_updated = 1;
    }
    else {
        cnx->ack_gap_remote = picoquic_compute_ack_gap(cnx, cnx->path[0]->receive_rate_max);
        cnx->ack_delay_remote = picoquic_compute_ack_delay_max(cnx->path[0]->rtt_min, PICOQUIC_ACK_DELAY_MIN);
    }

    /* Perform a check of the PN decryption key, for sanity */
    picoquic_log_pn_dec_trial(cnx);
}

/* Prepare the next packet to send when in one the ready states 
 * Lots of the same code as in the "ready" case, but we deal here with extra
 * complexity because the handshake is not finished.
 */
int picoquic_prepare_packet_almost_ready(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time,
    int* is_initial_sent)
{
    int ret = 0;
    picoquic_packet_type_enum packet_type = picoquic_packet_1rtt_protected;
    picoquic_packet_context_enum pc = picoquic_packet_context_application;
    int tls_ready = 0;
    int is_pure_ack = 1;
    size_t header_length = 0;
    uint8_t* bytes = packet->bytes;
    size_t length = 0;
    size_t checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt);
    size_t send_buffer_min_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;
    uint8_t* bytes_max = bytes + send_buffer_min_max - checksum_overhead;
    uint8_t* bytes_next = NULL;
    int more_data = 0;
    int stream_tried_and_failed = 0;
    int is_challenge_padding_needed = 0;

    /* Perform amplification prevention check */
    if (!cnx->initial_validated &&
        (cnx->initial_data_sent + send_buffer_min_max) > 3 * cnx->initial_data_received) {
        *send_length = 0;
        return 0;
    }

    /* Verify first that there is no need for retransmit or ack
     * on initial or handshake context. */
    if (cnx->crypto_context[picoquic_epoch_initial].aead_encrypt != NULL) {
        length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_initial,
            path_x, packet, send_buffer_min_max, current_time, next_wake_time, &header_length);
    }
    else {
        length = 0;
    }

    if (length == 0) {
        length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_handshake,
            path_x, packet, send_buffer_min_max, current_time, next_wake_time, &header_length);
        if (length > 0) {
            checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_handshake);
            bytes_max = bytes + send_buffer_min_max - checksum_overhead;
        }
    }
    else {
        checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_initial);
        bytes_max = bytes + send_buffer_min_max - checksum_overhead;

        *is_initial_sent = 1;
    }

    if (length > 0) {
        cnx->initial_repeat_needed = 0;

        if (cnx->client_mode && *is_initial_sent && send_buffer_min_max < length + checksum_overhead + PICOQUIC_MIN_SEGMENT_SIZE) {
            length = picoquic_pad_to_target_length(packet->bytes, length, send_buffer_min_max - checksum_overhead);
        }
    }

    if (length == 0) {
        tls_ready = picoquic_is_tls_stream_ready(cnx);
        packet->pc = pc;
        length = picoquic_predict_packet_header_length(
            cnx, packet_type);
        packet->ptype = packet_type;
        packet->offset = length;
        header_length = length;
        packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
        packet->send_time = current_time;
        packet->send_path = path_x;
        bytes_next = bytes + length;

        if (path_x->challenge_verified == 0 && path_x->challenge_failed == 0) {
            uint64_t next_challenge_time = picoquic_next_challenge_time(cnx, path_x);
            if (next_challenge_time <= current_time || path_x->challenge_repeat_count == 0) {
                if (path_x->challenge_repeat_count < PICOQUIC_CHALLENGE_REPEAT_MAX) {
                    int ack_needed = cnx->pkt_ctx[pc].ack_needed;
                    uint8_t* bytes_challenge = bytes_next;

                    bytes_next = picoquic_format_path_challenge_frame(bytes_next, bytes_max, &more_data, &is_pure_ack,
                        path_x->challenge[path_x->challenge_repeat_count]);
                    if (bytes_next > bytes_challenge) {
                        path_x->challenge_time = current_time;
                        path_x->challenge_repeat_count++;
                        is_challenge_padding_needed = 1;
                    }

                    /* add an ACK just to be nice */
                    if (ack_needed) {
                        bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc);
                        /* Restore the ACK needed flags, because challenges are not reliable. */
                        cnx->pkt_ctx[pc].ack_needed = ack_needed;
                    }
                }
                else {
                    if (path_x == cnx->path[0]) {
                        /* TODO: consider alt address. Also, consider other available path. */
                        DBG_PRINTF("%s\n", "Too many challenge retransmits, disconnect");
                        picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, disconnect");
                        cnx->cnx_state = picoquic_state_disconnected;
                        if (cnx->callback_fn) {
                            (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL);
                        }
                    }
                    else {
                        DBG_PRINTF("%s\n", "Too many challenge retransmits, abandon path");
                        picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, abandon path");
                        path_x->challenge_failed = 1;
                        cnx->path_demotion_needed = 1;
                    }
                }
            }
            else {
                if (next_challenge_time < *next_wake_time) {
                    *next_wake_time = next_challenge_time;
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
            }
        }

        if (path_x->response_required) {
            uint8_t* bytes_response = bytes_next;
            if ((bytes_next = picoquic_format_path_response_frame(bytes_response, bytes_max,
                &more_data, &is_pure_ack, path_x->challenge_response)) > bytes_response) {
                path_x->response_required = 0;
                is_challenge_padding_needed = 1;
            }
        }

        length = bytes_next - bytes;

        if (cnx->cnx_state != picoquic_state_disconnected && path_x->challenge_verified != 0) {
            /* There are no frames yet that would be exempt from pacing control, but if there
             * was they should be sent here. */

            if (picoquic_is_sending_authorized_by_pacing(cnx, path_x, current_time, next_wake_time)) {
                /* Send here the frames that are not exempt from the pacing control,
                 * but are exempt for congestion control */
                if (picoquic_is_ack_needed(cnx, current_time, next_wake_time, pc)) {
                    bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data,
                        current_time, pc);
                }

                length = bytes_next - bytes;
                if (path_x->cwin < path_x->bytes_in_transit) {
                    cnx->cwin_blocked = 1;
                    if (cnx->congestion_alg != NULL) {
                        cnx->congestion_alg->alg_notify(cnx, path_x,
                            picoquic_congestion_notification_cwin_blocked,
                            0, 0, 0, 0, current_time);
                    }
                }
                else {
                    /* Send here the frames that are subject to both congestion and pacing control.
                     * this includes the PMTU probes.
                     * Check whether PMTU discovery is required. The call will return
                     * three values: not needed at all, optional, or required.
                     * If required, PMTU discovery takes priority over sending stream data.
                     */
                    picoquic_pmtu_discovery_status_enum pmtu_discovery_needed = picoquic_is_mtu_probe_needed(cnx, path_x);

                    /* if present, send tls data */
                    if (tls_ready) {
                        bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[picoquic_epoch_1rtt],
                            bytes_next, bytes_max, &more_data, &is_pure_ack);
                    }

                    length = bytes_next - bytes;

                    if (length > header_length || pmtu_discovery_needed != picoquic_pmtu_discovery_required ||
                        send_buffer_max <= path_x->send_mtu) {
                        /* No need or no way to do pmtu discovery */
                        /* If present, send misc frame */
                        while (cnx->first_misc_frame != NULL) {
                            uint8_t* bytes_misc = bytes_next;
                            bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                            if (bytes_next == bytes_misc) {
                                break;
                            }
                        }

                        /* If there are not enough published CID, create and advertise */
                        if (ret == 0) {
                            bytes_next = picoquic_format_new_local_id_as_needed(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                        }

                        /* Start of CC controlled frames */
                        if (ret == 0 && length <= header_length && cnx->first_datagram != NULL) {
                            bytes_next = picoquic_format_first_datagram_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                        }

                        /* If present, send stream frames queued for retransmission */
                        if (ret == 0) {
                            bytes_next = picoquic_format_stream_frames_queued_for_retransmit(cnx, bytes_next, bytes_max,
                                &more_data, &is_pure_ack);
                        }

                        if (cnx->is_ack_frequency_updated && cnx->is_ack_frequency_negotiated) {
                            bytes_next = picoquic_format_ack_frequency_frame(cnx, bytes_next, bytes_max, &more_data);
                        }

                        bytes_next = picoquic_format_available_stream_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, &stream_tried_and_failed, &ret);

                        length = bytes_next - bytes;

                        if (length <= header_length) {
                            /* Mark the bandwidth estimation as application limited */
                            path_x->delivered_limited_index = path_x->delivered;
                            /* Notify the peer if something is blocked */
                            bytes_next = picoquic_format_blocked_frames(cnx, &bytes[length], bytes_max, &more_data, &is_pure_ack);
                            length = bytes_next - bytes;
                        }

                        if (stream_tried_and_failed) {
                            path_x->last_sender_limited_time = current_time;
                        }
                    } /* end of PMTU not required */

                    if (ret == 0 && length <= header_length && send_buffer_max > path_x->send_mtu
                        && path_x->cwin > path_x->bytes_in_transit&& pmtu_discovery_needed != picoquic_pmtu_discovery_not_needed) {
                        /* Since there is no data to send, this is an opportunity to send an MTU probe */
                        length = picoquic_prepare_mtu_probe(cnx, path_x, header_length, checksum_overhead, bytes, send_buffer_max);
                        packet->length = length;
                        packet->send_path = path_x;
                        packet->is_mtu_probe = 1;
                        path_x->mtu_probe_sent = 1;
                        is_pure_ack = 0;
                    }
                } /* end of PMTU references */
            } /* end of CC */
        } /* End of pacing */
        if (length <= header_length) {
            length = 0;
        }

        if (cnx->cnx_state != picoquic_state_disconnected) {
            /* If necessary, encode and send the keep alive packet!
             * We only send keep alive packets when no other data is sent!
             */
            if (is_pure_ack == 0)
            {
                cnx->latest_progress_time = current_time;
            }
            else if (cnx->keep_alive_interval != 0) {
                if (cnx->latest_progress_time + cnx->keep_alive_interval <= current_time && length == 0) {
                    length = picoquic_predict_packet_header_length(
                        cnx, packet_type);
                    packet->ptype = packet_type;
                    packet->pc = pc;
                    packet->offset = length;
                    header_length = length;
                    packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
                    packet->send_path = path_x;
                    packet->send_time = current_time;
                    bytes[length++] = picoquic_frame_type_ping;
                    bytes[length++] = 0;
                    cnx->latest_progress_time = current_time;
                }
                else if (cnx->latest_progress_time + cnx->keep_alive_interval < *next_wake_time) {
                    *next_wake_time = cnx->latest_progress_time + cnx->keep_alive_interval;
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
            }
        }
    }

    if (ret == 0 && length > header_length) {
        if (more_data) {
            *next_wake_time = current_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
            ret = 0;
        }

        /* Ensure that all packets are properly padded before being sent. */

        if ((*is_initial_sent && cnx->client_mode) || (is_challenge_padding_needed && length < PICOQUIC_ENFORCED_INITIAL_MTU)){
            length = picoquic_pad_to_target_length(bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead));
        }
        else {
            length = picoquic_pad_to_policy(cnx, bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead));
        }
    }

    picoquic_finalize_and_protect_packet(cnx, packet,
        ret, length, header_length, checksum_overhead,
        send_length, send_buffer, send_buffer_min_max,
        &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time);

    if (*send_length > 0) {
        /* Account for data sent during handshake */
        if (!cnx->initial_validated) {
            cnx->initial_data_sent += *send_length;
        }
        *next_wake_time = current_time;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);

        if (picoquic_cnx_is_still_logging(cnx)) {
            picoquic_log_cc_dump(cnx, current_time);
        }
    }

    return ret;
}

/*  Prepare the next packet to send when in the ready state */
int picoquic_prepare_packet_ready(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time,
    int* is_initial_sent)
{
    int ret = 0;
    picoquic_packet_type_enum packet_type = picoquic_packet_1rtt_protected;
    picoquic_packet_context_enum pc = picoquic_packet_context_application;
    int is_pure_ack = 1;
    size_t header_length = 0;
    size_t length = 0;
    size_t checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt);
    size_t send_buffer_min_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max;
    int split_repeat_queued = 0;
    uint8_t* bytes = packet->bytes;
    uint8_t* bytes_max = bytes + send_buffer_min_max - checksum_overhead;
    uint8_t* bytes_next;
    int more_data = 0;
    int ack_sent = 0;
    int is_challenge_padding_needed = 0;

    packet->pc = pc;

    /* If there was no packet sent on this path for a long time, rotate the
     * CID prior to sending a new packet. The point is to make it harder for
     * casual observers to track traffic, especially across NAT resets */
    if (cnx->client_mode &&
        path_x->challenge_verified &&
        !path_x->path_cid_rotated &&
        path_x->latest_sent_time + PICOQUIC_CID_REFRESH_DELAY < current_time)
    {
        /* Ignore renewal failure mode, since this is an optional feature */
        (void)picoquic_renew_path_connection_id(cnx, path_x);
        path_x->path_cid_rotated = 1;
    }

    /* If the number of packets sent is larger that the max length of
     * a crypto epoch, prepare a key rotation */
    if ((cnx->pkt_ctx[picoquic_packet_context_application].send_sequence - cnx->crypto_epoch_sequence >
        cnx->crypto_epoch_length_max) &&
        cnx->crypto_epoch_sequence <
        cnx->pkt_ctx[picoquic_packet_context_application].first_sack_item.end_of_sack_range) {
        if (picoquic_start_key_rotation(cnx) != 0) {
            picoquic_log_app_message(cnx, "Cannot start key rotation after %"PRIu64" packets",
                cnx->pkt_ctx[picoquic_packet_context_application].send_sequence);
        }
    }

    /* The first action is normally to retransmit lost packets. But if retransmit follows an
     * MTU drop, the stream frame will be fragmented and a fragment will be queued as a
     * misc frame. These fragments should have chance to go out before more retransmit is
     * permitted, hence the test here for the misc-frame */
    if (cnx->first_misc_frame == NULL &&
        (length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, 
        send_buffer_min_max, &header_length)) > 0) {
        /* Check whether it makes sense to add an ACK at the end of the retransmission */
        /* Don't do that if it risks mixing clear text and encrypted ack */
        bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data,
            current_time, pc);
        length = bytes_next - bytes;
        /* document the send time & overhead */
        is_pure_ack = 0;
        packet->send_time = current_time;
        packet->checksum_overhead = checksum_overhead;
    }
    else if (ret == 0) {
        length = picoquic_predict_packet_header_length(
            cnx, packet_type);
        packet->ptype = packet_type;
        packet->offset = length;
        header_length = length;
        packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
        packet->send_time = current_time;
        packet->send_path = path_x;
        bytes_next = bytes + length;

        /* If required, prepare challenge and response frames.
         * These frames will be sent immediately, regardless of pacing or flow control.
         */

        if (path_x->challenge_verified == 0 && path_x->challenge_failed == 0) {
            uint64_t next_challenge_time = picoquic_next_challenge_time(cnx, path_x);
            if (next_challenge_time <= current_time || path_x->challenge_repeat_count == 0) {
                if (path_x->challenge_repeat_count < PICOQUIC_CHALLENGE_REPEAT_MAX) {
                    int ack_needed = cnx->pkt_ctx[pc].ack_needed;
                    /* When blocked, repeat the path challenge or wait */
                    uint8_t* bytes_challenge = bytes_next;

                    bytes_next = picoquic_format_path_challenge_frame(bytes_next, bytes_max, &more_data, &is_pure_ack,
                        path_x->challenge[path_x->challenge_repeat_count]);
                    if (bytes_next > bytes_challenge) {
                        path_x->challenge_time = current_time;
                        path_x->challenge_repeat_count++;
                        is_challenge_padding_needed = 1;
                    }

                    /* add an ACK just to be nice */
                    if (ack_needed) {
                        bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data,
                            current_time, pc);
                        /* Restore the ACK needed flags, because challenges are not reliable. */
                        cnx->pkt_ctx[pc].ack_needed = ack_needed;
                    }
                }
                else {
                    if (path_x == cnx->path[0]) {
                        /* Try to find an alternate path */
                        for (int i = 1; i < cnx->nb_paths; i++) {
                            if (cnx->path[i]->challenge_failed == 0) {
                                cnx->path[0] = cnx->path[i];
                                cnx->path[i] = path_x;
                            }
                        }
                    }

                    if (path_x == cnx->path[0]) {
                        DBG_PRINTF("%s\n", "Too many challenge retransmits, disconnect");
                        picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, disconnect");
                        cnx->cnx_state = picoquic_state_disconnected;
                        if (cnx->callback_fn) {
                            (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL);
                        }
                    }
                    else {
                        DBG_PRINTF("%s\n", "Too many challenge retransmits, abandon path");
                        picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, abandon path");
                        path_x->challenge_failed = 1;
                    }
                }
            }
            else {
                if (next_challenge_time < *next_wake_time) {
                    *next_wake_time = next_challenge_time;
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
            }
        }

        if (path_x->response_required) {
            uint8_t* bytes_response = bytes_next;
            if ((bytes_next = picoquic_format_path_response_frame(bytes_response, bytes_max,
                &more_data, &is_pure_ack, path_x->challenge_response)) > bytes_response) {
                path_x->response_required = 0;
                is_challenge_padding_needed = 1;
            }
        }

        /* Compute the length before pacing block */
        length = bytes_next - bytes;

        if (cnx->cnx_state != picoquic_state_disconnected && path_x->challenge_verified != 0) {
            /* There are no frames yet that would be exempt from pacing control, but if there
             * was they should be sent here. */

            if (picoquic_is_sending_authorized_by_pacing(cnx, path_x, current_time, next_wake_time)) {
                /* Send here the frames that are not exempt from the pacing control,
                 * but are exempt for congestion control */
                if (picoquic_is_ack_needed(cnx, current_time, next_wake_time, pc)) {
                    uint8_t* bytes_ack = bytes_next;
                    bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data,
                        current_time, pc);
                    ack_sent = (bytes_next > bytes_ack);
                }

                /* if necessary, prepare the MAX STREAM frames */
                if (ret == 0) {
                    bytes_next = picoquic_format_max_streams_frame_if_needed(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                }

                /* If necessary, encode the max data frame */
                if (ret == 0){
                    if (cnx->is_flow_control_limited) {
                        if (cnx->data_received + (cnx->local_parameters.initial_max_data / 2) > cnx->maxdata_local) {
                            bytes_next = picoquic_format_max_data_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack,
                                cnx->local_parameters.initial_max_data);
                        }
                    }
                    else if (2 * cnx->data_received > cnx->maxdata_local) {
                        bytes_next = picoquic_format_max_data_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack,
                            picoquic_cc_increased_window(cnx, cnx->maxdata_local));
                    }
                }

                /* If necessary, encode the max stream data frames */
                if (ret == 0 && cnx->max_stream_data_needed) {
                    bytes_next = picoquic_format_required_max_stream_data_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                }

                /* If present, send misc frame */
                while (cnx->first_misc_frame != NULL) {
                    uint8_t* bytes_misc = bytes_next;
                    bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                    if (bytes_next > bytes_misc) {
                        split_repeat_queued |=
                            PICOQUIC_IN_RANGE(*bytes_misc, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max);
                    }
                    else {
                        break;
                    }
                }

                /* Compute the length before entering the CC block */
                length = bytes_next - bytes;

                if (path_x->cwin < path_x->bytes_in_transit) {
                    cnx->cwin_blocked = 1;
                    if (cnx->congestion_alg != NULL) {
                        cnx->congestion_alg->alg_notify(cnx, path_x,
                            picoquic_congestion_notification_cwin_blocked,
                            0, 0, 0, 0, current_time);
                    }
                }
                else {
                    /* Send here the frames that are subject to both congestion and pacing control.
                     * this includes the PMTU probes.
                     * Check whether PMTU discovery is required. The call will return
                     * three values: not needed at all, optional, or required.
                     * If required, PMTU discovery takes priority over sending stream data.
                     */
                    int datagram_tried_and_failed = 0;
                    int stream_tried_and_failed = 0;
                    picoquic_pmtu_discovery_status_enum pmtu_discovery_needed = picoquic_is_mtu_probe_needed(cnx, path_x);

                    /* if present, send tls data */
                    if (picoquic_is_tls_stream_ready(cnx)) {
                        bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[picoquic_epoch_1rtt],
                            bytes_next, bytes_max, &more_data, &is_pure_ack);
                    }

                    if (length > header_length || pmtu_discovery_needed != picoquic_pmtu_discovery_required ||
                        send_buffer_max <= path_x->send_mtu) {
                        /* No need or no way to do path MTU discovery, just go on with formatting packets */
                        /* If there are not enough local CID published, create and advertise */
                        if (ret == 0) {
                            bytes_next = picoquic_format_new_local_id_as_needed(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                        }

                        /* Start of CC controlled frames */

                        if (ret == 0 && length <= header_length) {
                            if (cnx->first_datagram != NULL) {
                                bytes_next = picoquic_format_first_datagram_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack);
                            }
                            else {
                                datagram_tried_and_failed = 1;
                            }
                        }

                        /* If present, send stream frames queued for retransmission */
                        if (ret == 0) {
                            bytes_next = picoquic_format_stream_frames_queued_for_retransmit(cnx, bytes_next, bytes_max,
                                &more_data, &is_pure_ack);
                        }

                        if (ret == 0 && cnx->is_ack_frequency_updated && cnx->is_ack_frequency_negotiated) {
                            bytes_next = picoquic_format_ack_frequency_frame(cnx, bytes_next, bytes_max, &more_data);
                        }

                        /* Encode the stream frame, or frames */
                        if (ret == 0 && !split_repeat_queued && bytes_next + 8 < bytes_max) {
                            bytes_next = picoquic_format_available_stream_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, &stream_tried_and_failed, &ret);
                        }

                        length = bytes_next - bytes;

                        if (length <= header_length || is_pure_ack) {
                            /* Mark the bandwidth estimation as application limited */
                            path_x->delivered_limited_index = path_x->delivered;
                            /* Notify the peer if something is blocked */
                            bytes_next = picoquic_format_blocked_frames(cnx, &bytes[length], bytes_max, &more_data, &is_pure_ack);
                            length = bytes_next - bytes;
                        }

                        if (stream_tried_and_failed && datagram_tried_and_failed) {
                            path_x->last_sender_limited_time = current_time;
                        }
                        else if (length <= header_length && stream_tried_and_failed && !cnx->stream_blocked && !cnx->flow_blocked) {
                            /* Consider redundant retransmission:
                             * if the redundant retransmission index is null:
                             * - if the packet loss rate is large enough compared to BDP, set index to last sent packet.
                             * - if not, do not perform redundant retransmission.
                             * if the packet contains a stream frame, if that stream is finished, and if the
                             * data range has not been acked, and it fits: copy it to the data. Move the index to the previous packet.
                             */
                            /* TODO: write that code! */
                        }
                    } /* end of PMTU not required */

                    if (ret == 0 && length <= header_length && send_buffer_max > path_x->send_mtu
                        && path_x->cwin > path_x->bytes_in_transit&& pmtu_discovery_needed != picoquic_pmtu_discovery_not_needed) {
                        /* Since there is no data to send, this is an opportunity to send an MTU probe */
                        length = picoquic_prepare_mtu_probe(cnx, path_x, header_length, checksum_overhead, bytes, send_buffer_max);
                        packet->length = length;
                        packet->send_path = path_x;
                        packet->is_mtu_probe = 1;
                        path_x->mtu_probe_sent = 1;
                        is_pure_ack = 0;
                    }
                } /* end of CC */
            } /* End of pacing */
        } /* End of challenge verified */
    }

    if (length <= header_length) {
        length = 0;
    }

    if (cnx->cnx_state != picoquic_state_disconnected) {
        if (length > 0){
            cnx->pkt_ctx[pc].ack_of_ack_requested |= !is_pure_ack;
            if (!cnx->pkt_ctx[pc].ack_of_ack_requested && ack_sent) {
                /* If we have sent many ACKs, add a PING to get an ack of ack */
                /* The number 24 is chosen to not break any of the unit tests. If the number is
                 * too small, the PING mechanism can cause delayed end of the connection, or 
                 * early breakage */
                bytes_next = bytes + length;
                if (bytes_next < bytes_max &&
                    cnx->pkt_ctx[pc].highest_acknowledged + 24 < cnx->pkt_ctx[pc].send_sequence &&
                    path_x == cnx->path[0] &&
                    cnx->pkt_ctx[pc].highest_acknowledged_time + cnx->path[0]->smoothed_rtt < current_time) {
                    /* Bundle a Ping with ACK, so as to get trigger an Acknowledgement */
                    *bytes_next++ = picoquic_frame_type_ping;
                    cnx->pkt_ctx[pc].ack_of_ack_requested = 1;
                    is_pure_ack = 0;
                    length = bytes_next - bytes;
                }
            }
        }

        if (is_pure_ack == 0)
        {
            cnx->latest_progress_time = current_time;
        }
        else if (cnx->keep_alive_interval != 0) {
            /* If necessary, encode and send the keep alive packet.
             * We only send keep alive packets when no other data is sent.
             */
            if (cnx->latest_progress_time + cnx->keep_alive_interval <= current_time && length == 0) {
                length = picoquic_predict_packet_header_length(
                    cnx, packet_type);
                packet->ptype = packet_type;
                packet->pc = pc;
                packet->offset = length;
                header_length = length;
                packet->sequence_number = cnx->pkt_ctx[pc].send_sequence;
                packet->send_path = path_x;
                packet->send_time = current_time;
                bytes[length++] = picoquic_frame_type_ping;
                bytes[length++] = 0;
                cnx->latest_progress_time = current_time;
            }
            else if (cnx->latest_progress_time + cnx->keep_alive_interval < *next_wake_time) {
                *next_wake_time = cnx->latest_progress_time + cnx->keep_alive_interval;
                SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
            }
        }

        if (more_data) {
            *next_wake_time = current_time;
            SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
            ret = 0;
        }
    }

    if (ret == 0 && length > header_length) {
        /* Ensure that all packets are properly padded before being sent. */

        if ((*is_initial_sent && cnx->client_mode) || (is_challenge_padding_needed && length < PICOQUIC_ENFORCED_INITIAL_MTU)){
            length = picoquic_pad_to_target_length(bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead));
        }
        else {
            length = picoquic_pad_to_policy(cnx, bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead));
        }
    }

    picoquic_finalize_and_protect_packet(cnx, packet,
        ret, length, header_length, checksum_overhead,
        send_length, send_buffer, send_buffer_min_max,
        &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time);

    if (*send_length > 0) {
        *next_wake_time = current_time;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);

        if (ret == 0 && picoquic_cnx_is_still_logging(cnx)) {
            picoquic_log_cc_dump(cnx, current_time);
        }
    }

    return ret;
}


static int picoquic_check_idle_timer(picoquic_cnx_t* cnx, uint64_t* next_wake_time, uint64_t current_time)
{
    int ret = 0;
    uint64_t idle_timer = 0;

    if (cnx->cnx_state >= picoquic_state_ready) {
        uint64_t rto = picoquic_current_retransmit_timer(cnx, picoquic_packet_context_application);
        idle_timer = cnx->idle_timeout;
        if (idle_timer < 3 * rto) {
            idle_timer = 3 * rto;
        }
        idle_timer += cnx->latest_progress_time;

        if (idle_timer < cnx->idle_timeout) {
            idle_timer = UINT64_MAX;
        }
    }
    else {
        idle_timer = cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX;
    }

    if (current_time >= idle_timer) {
        /* Too long silence, break it. */
        cnx->cnx_state = picoquic_state_disconnected;
        ret = PICOQUIC_ERROR_DISCONNECTED;
        if (cnx->callback_fn) {
            (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL);
        }
    } else if (idle_timer < *next_wake_time) {
        *next_wake_time = idle_timer;
        SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
    }

    return ret;
}

/* Prepare next packet to send, or nothing.. */
int picoquic_prepare_segment(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_t* packet,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
    uint64_t* next_wake_time, int* is_initial_sent)
{
    int ret = 0;

    /* Reset the blocked indicators */
    cnx->cwin_blocked = 0;
    cnx->flow_blocked = 0;
    cnx->stream_blocked = 0;

    /* Prepare header -- depend on connection state */
    /* TODO: 0-RTT work. */
    switch (cnx->cnx_state) {
    case picoquic_state_client_init:
    case picoquic_state_client_init_sent:
    case picoquic_state_client_init_resent:
    case picoquic_state_client_renegotiate:
    case picoquic_state_client_handshake_start:
    case picoquic_state_client_almost_ready:
        ret = picoquic_prepare_packet_client_init(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent);
        break;
    case picoquic_state_server_almost_ready:
    case picoquic_state_server_init:
    case picoquic_state_server_handshake:
        ret = picoquic_prepare_packet_server_init(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time);
        break;
    case picoquic_state_server_false_start:
        /*
         * Manage the end of false start transition, and if needed start
         * preparing packet in ready state.
         */
        if (cnx->cnx_state == picoquic_state_server_false_start &&
            cnx->crypto_context[3].aead_decrypt != NULL) {
            picoquic_ready_state_transition(cnx, current_time);
            return picoquic_prepare_packet_ready(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent);
        }
        /* Else, just fall through to almost ready behavior.
         */
    case picoquic_state_client_ready_start:
        ret = picoquic_prepare_packet_almost_ready(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent);
        break;
    case picoquic_state_ready:
        ret = picoquic_prepare_packet_ready(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent);
        break;
    case picoquic_state_handshake_failure:
    case picoquic_state_handshake_failure_resend:
    case picoquic_state_disconnecting:
    case picoquic_state_closing_received:
    case picoquic_state_closing:
    case picoquic_state_draining:
        ret = picoquic_prepare_packet_closing(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time);
        break;
    case picoquic_state_disconnected:
        ret = PICOQUIC_ERROR_DISCONNECTED;
        break;
    case picoquic_state_client_retry_received:
        DBG_PRINTF("Unexpected connection state: %d\n", cnx->cnx_state);
        ret = PICOQUIC_ERROR_UNEXPECTED_STATE;
        break;
    default:
        DBG_PRINTF("Unexpected connection state: %d\n", cnx->cnx_state);
        ret = PICOQUIC_ERROR_UNEXPECTED_STATE;
        break;
    }

    return ret;
}

/*
 * The version 1 of Quic only supports path migration, not full multipath.
 * This code finds whether there is a path being probed that could become the
 * default path, or that needs an immediate challenge sent or replied to.
 *
 * If no other path is suitable, the code returns the default path.
 */

static int picoquic_select_next_path(picoquic_cnx_t * cnx, uint64_t current_time, uint64_t * next_wake_time)
{
    int path_id = -1;
    /* Select the path */
    for (int i = 1; i < cnx->nb_paths; i++) {
        if (cnx->path[i]->path_is_demoted) {
            continue;
        }
        else if (cnx->path[i]->challenge_failed) {
            picoquic_demote_path(cnx, i, current_time);
            continue;
        }
        else if (cnx->path[i]->challenge_verified) {
            /* TODO: selection logic if multiple paths are available! */
            /* This path becomes the new default */
            picoquic_promote_path_to_default(cnx, i, current_time);
            path_id = 0;
            break;
        }
        else if (path_id < 0) {
            if (cnx->path[i]->response_required) {
                path_id = i;
            }
            else if (cnx->path[i]->challenge_required) {
                uint64_t next_challenge_time = picoquic_next_challenge_time(cnx, cnx->path[i]);
                if (cnx->path[i]->challenge_repeat_count == 0 ||
                    current_time >= next_challenge_time) {
                    /* will try this path, unless a validated path came in */
                    path_id = i;
                }
                else if (next_challenge_time < *next_wake_time) {
                    *next_wake_time = next_challenge_time;
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
            }
        }
    }

    if (path_id < 0) {
        path_id = 0;
    }

    return path_id;
}

/* Prepare next packet to send, or nothing.. */
int picoquic_prepare_packet_ex(picoquic_cnx_t* cnx,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
    struct sockaddr_storage * p_addr_to, struct sockaddr_storage * p_addr_from, int* if_index, size_t* send_msg_size)
{

    int ret;
    picoquic_packet_t * packet = NULL;
    struct sockaddr_storage addr_to_log;
    struct sockaddr_storage addr_from_log;
    int is_initial_sent = 0;
    uint64_t next_wake_time = cnx->latest_progress_time + 2*PICOQUIC_MICROSEC_SILENCE_MAX;
    uint64_t initial_next_time;

    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);

    if (cnx->recycle_sooner_needed) {
        picoquic_process_sooner_packets(cnx, current_time);
    }

    memset(&addr_to_log, 0, sizeof(addr_to_log));
    memset(&addr_from_log, 0, sizeof(addr_from_log));
    *send_length = 0;

    ret = picoquic_check_idle_timer(cnx, &next_wake_time, current_time);

    if (send_buffer_max < PICOQUIC_ENFORCED_INITIAL_MTU) {
        DBG_PRINTF("Invalid buffer size: %zu", send_buffer_max);
        ret = -1;
    }

    if (ret == 0) {
        int path_id;

        /* Remove delete paths */
        if (cnx->path_demotion_needed) {
            picoquic_delete_abandoned_paths(cnx, current_time, &next_wake_time);
        }

        /* Check whether to insert a hole in the sequence of packets */
        if (cnx->pkt_ctx[0].send_sequence >= cnx->pkt_ctx[0].next_sequence_hole) {
            picoquic_insert_hole_in_send_sequence_if_needed(cnx, current_time, &next_wake_time);
        }

        /* Select the next path, and the corresponding addresses */
        path_id = picoquic_select_next_path(cnx, current_time, &next_wake_time);


        picoquic_store_addr(&addr_to_log, (struct sockaddr*) & cnx->path[path_id]->peer_addr);
        if (cnx->path[path_id]->local_addr.ss_family != 0) {
            picoquic_store_addr(&addr_from_log, (struct sockaddr*) & cnx->path[path_id]->local_addr);
        }

        if (p_addr_to != NULL) {
            picoquic_store_addr(p_addr_to, (struct sockaddr*) & cnx->path[path_id]->peer_addr);
        }

        if (p_addr_from != NULL) {
            picoquic_store_addr(p_addr_from, (struct sockaddr*) & cnx->path[path_id]->local_addr);
        }

        if (if_index != NULL) {
            *if_index = cnx->path[path_id]->if_index_dest;
        }

        /* Send the available packets */
        if (send_msg_size != NULL) {
            *send_msg_size = cnx->path[path_id]->send_mtu;
        }
        initial_next_time = next_wake_time;

        while (ret == 0)
        {
            /* Create a new packet */
            size_t packet_size = 0;
            size_t packet_max = send_buffer_max - *send_length;
            uint8_t* packet_buffer = send_buffer + *send_length;
            /* Reset the wake time to the initial value after sending packets */
            next_wake_time = initial_next_time;
            /* Send the available segments in that packet. */
            while (ret == 0)
            {
                size_t available = packet_max;
                size_t segment_length = 0;

                if (packet_size > 0) {
                    packet_max = cnx->path[path_id]->send_mtu;

                    if (packet_max < packet_size + PICOQUIC_MIN_SEGMENT_SIZE) {
                        break;
                    }
                    else {
                        available = packet_max - packet_size;
                    }
                }

                packet = picoquic_create_packet(cnx->quic);

                if (packet == NULL) {
                    ret = PICOQUIC_ERROR_MEMORY;
                    break;
                }
                else {
                    ret = picoquic_prepare_segment(cnx, cnx->path[path_id], packet, current_time,
                        packet_buffer + packet_size, available, &segment_length, &next_wake_time, &is_initial_sent);

                    if (ret == 0) {
                        packet_size += segment_length;
                        if (packet->length == 0) {
                            /* Nothing more to send */
                            picoquic_recycle_packet(cnx->quic, packet);
                            break;
                        }
                        else if (packet->ptype == picoquic_packet_1rtt_protected) {
                            /* Cannot coalesce packets after 1 rtt packet */
                            break;
                        }
                        else if (segment_length == 0) {
                            DBG_PRINTF("Send bug: segment length = %zu, packet length = %zu\n", segment_length, packet->length);
                            break;
                        }
                    }
                    else {
                        picoquic_recycle_packet(cnx->quic, packet);
                        packet = NULL;

                        if (packet_size != 0) {
                            ret = 0;
                        }
                        break;
                    }

                    if (cnx->quic->dont_coalesce_init) {
                        break;
                    }
                }
            }
            if (packet_size > 0) {
                cnx->nb_packets_sent++;
                /* if needed, log that the packet is sent */
                picoquic_log_pdu(cnx, 0, current_time,
                    (struct sockaddr*) & addr_to_log, (struct sockaddr*) & addr_from_log, packet_size);
            }

            /* Update the wake up time for the connection */
            if (packet_size > 0 || cnx->cnx_state == picoquic_state_disconnected) {
                next_wake_time = current_time;
                SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
            }

            /* Account for the bytes in the packet. */
            *send_length += packet_size;

            /* Check whether to keep coalescing multiple packets in the send buffer */
            if (send_msg_size == NULL) {
                break;
            }
            else if (packet_size > *send_msg_size) {
                /* This can only happen for the first packet in a batch. */
                *send_msg_size = packet_size;
            }
            else if (packet_size != *send_msg_size) {
                if (*send_length > 0) {
                    if (packet_size == 0 && *send_length < 4*(*send_msg_size)) {
                        if (cnx->path[0]->cwin <= cnx->path[0]->bytes_in_transit) {
                            cnx->nb_trains_blocked_cwin++;
                        }
                        else if (cnx->path[0]->pacing_bucket_nanosec < cnx->path[0]->pacing_packet_time_nanosec){
                            cnx->nb_trains_blocked_pacing++;
                        }
                        else {
                            cnx->nb_trains_blocked_others++;
                        }
                    }
                    else {
                        cnx->nb_trains_short++;
                    }
                }
                break;
            }
            else if (*send_length + *send_msg_size > send_buffer_max) {
                break;
            }
        }
        if (*send_length > 0) {
            cnx->nb_trains_sent++;
        }
    }
    
    picoquic_reinsert_by_wake_time(cnx->quic, cnx, next_wake_time);

    return ret;
}

int picoquic_prepare_packet(picoquic_cnx_t* cnx,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
    struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index)
{
    return picoquic_prepare_packet_ex(cnx, current_time, send_buffer, send_buffer_max, send_length,
        p_addr_to, p_addr_from, if_index, NULL);
}

int picoquic_close(picoquic_cnx_t* cnx, uint16_t reason_code)
{
    int ret = 0;

    if (cnx->cnx_state == picoquic_state_ready ||
        cnx->cnx_state == picoquic_state_server_false_start || cnx->cnx_state == picoquic_state_client_ready_start) {
        cnx->cnx_state = picoquic_state_disconnecting;
        cnx->application_error = reason_code;
    } else if (cnx->cnx_state < picoquic_state_client_ready_start) {
        cnx->cnx_state = picoquic_state_handshake_failure;
        cnx->application_error = 0;
        cnx->local_error = PICOQUIC_TRANSPORT_APPLICATION_ERROR;
    } else {
        ret = -1;
    }
    cnx->offending_frame_type = 0;

    picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic));

    return ret;
}

/* Quic context level call.
 * will send a stateless packet if one is queued, or ask the first connection in
 * the wake list to prepare a packet */

int picoquic_prepare_next_packet_ex(picoquic_quic_t* quic,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
    struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int * if_index,
    picoquic_connection_id_t * log_cid, picoquic_cnx_t** p_last_cnx, size_t * send_msg_size)
{
    int ret = 0;
    picoquic_stateless_packet_t* sp = picoquic_dequeue_stateless_packet(quic);

    if (p_last_cnx) {
        *p_last_cnx = NULL;
    }

    if (sp != NULL) {
        if (sp->length > send_buffer_max) {
            *send_length = 0;
        }
        else {
            memcpy(send_buffer, sp->bytes, sp->length);
            *send_length = sp->length;
            picoquic_store_addr(p_addr_to, (struct sockaddr*) & sp->addr_to);
            picoquic_store_addr(p_addr_from, (struct sockaddr*) & sp->addr_local);
            *if_index = sp->if_index_local;
            if (log_cid != NULL) {
                *log_cid = sp->initial_cid;
            }
        }
        picoquic_delete_stateless_packet(sp);
    }
    else {
        picoquic_cnx_t* cnx = picoquic_get_earliest_cnx_to_wake(quic, current_time);

        if (cnx == NULL) {
            *send_length = 0;
        }
        else {
            ret = picoquic_prepare_packet_ex(cnx, current_time, send_buffer, send_buffer_max, send_length, p_addr_to, p_addr_from, 
                if_index, send_msg_size);
            if (log_cid != NULL) {
                *log_cid = cnx->initial_cnxid;
            }

            if (ret == PICOQUIC_ERROR_DISCONNECTED) {
                ret = 0;

                picoquic_log_app_message(cnx, "Closed. Retrans= %d, spurious= %d, max sp gap = %d, max sp delay = %d, dg-coal: %f",
                    (int)cnx->nb_retransmission_total, (int)cnx->nb_spurious,
                    (int)cnx->path[0]->max_reorder_gap, (int)cnx->path[0]->max_spurious_rtt,
                    (cnx->nb_trains_sent > 0) ? ((double)cnx->nb_packets_sent / (double)cnx->nb_trains_sent) : 0.0);

                if (quic->F_log != NULL) {
                    fflush(quic->F_log);
                }

                if (cnx->f_binlog != NULL) {
                    fflush(cnx->f_binlog);
                }

                if (cnx->client_mode) {
                    /* Do not unilaterally delete the connection context, as it was set by the application */
                    picoquic_reinsert_by_wake_time(cnx->quic, cnx, UINT64_MAX);
                    SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER);
                }
                else {
                    picoquic_delete_cnx(cnx);
                }
            }
            else {
                if (*if_index == -1) {
                    *if_index = picoquic_get_local_if_index(cnx);
                }
                if (p_last_cnx) {
                    *p_last_cnx = cnx;
                }
            }
        }
    }

    return ret;
}

int picoquic_prepare_next_packet(picoquic_quic_t* quic,
    uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length,
    struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index,
    picoquic_connection_id_t* log_cid, picoquic_cnx_t** p_last_cnx)
{
    return picoquic_prepare_next_packet_ex(quic, current_time, send_buffer, send_buffer_max, send_length,
        p_addr_to, p_addr_from, if_index, log_cid, p_last_cnx, NULL);
}