void packet_transmitted(hw_radio_packet_t* packet)
{
#if HW_NUM_LEDS > 0
    led_toggle(0);
#endif
    DPRINT("%d tx ok\n", counter);
    timer_post_task(&transmit_packet, 1000);

    hw_watchdog_feed();
}
void led_on_callback()
{
	led_on(0);
	timer_post_task_delay(&led_on_callback, TIMER_TICKS_PER_SEC);
	timer_post_task_delay(&led_off_callback, TIMER_TICKS_PER_SEC*0.050);
	log_print_string("Toggled on %d", 0);

	hw_watchdog_feed();

}
void led_on_callback()
{
  led_toggle(0);
  timer_post_task_delay(&led_on_callback, TIMER_TICKS_PER_SEC * 70);
  //timer_post_task_delay(&led_off_callback, TIMER_TICKS_PER_SEC*0.050);
	log_print_string("Toggled on %d", 0);
	console_print("Toggle led 0\r\n");

	hw_watchdog_feed();

}
void read_rssi()
{
    timestamped_rssi_t rssi_measurement;
    rssi_measurement.tick = timer_get_counter_value();

    char rssi_samples_str[5 * RSSI_SAMPLES_PER_MEASUREMENT] = "";
    int16_t max_rssi_sample = -200;
    for(int i = 0; i < RSSI_SAMPLES_PER_MEASUREMENT; i++)
    {
        rssi_measurement.rssi[i] = hw_radio_get_rssi();
        if(rssi_measurement.rssi[i] > max_rssi_sample)
            max_rssi_sample = rssi_measurement.rssi[i];

        sprintf(rssi_samples_str + (i * 5), ",%04i", rssi_measurement.rssi[i]);
        // TODO delay?
    }

    char str[80];
    char channel_str[8] = "";


    channel_id_to_string(&rx_cfg.channel_id, channel_str, sizeof(channel_str));
    lcd_write_string(channel_str);
    sprintf(str, "%7s,%i%s\n", channel_str, rssi_measurement.tick, rssi_samples_str);
    console_print(str);

#ifdef PLATFORM_EFM32GG_STK3700
    //lcd_all_on();
    lcd_write_number(max_rssi_sample);
#elif defined HAS_LCD
    sprintf(str, "%7s,%d\n", channel_str, max_rssi_sample);
    lcd_write_string(str);
#endif

    if(!use_manual_channel_switching)
    {
        switch_next_channel();
        sched_post_task(&start_rx);
    }
    else
    {
    	sched_post_task(&process_uart_rx_fifo); // check for UART commands first
        uint16_t delay = rand() % 5000;
        timer_post_task_delay(&read_rssi, delay);
    }

    hw_watchdog_feed();
}
void packet_received(hw_radio_packet_t* packet)
{
    DPRINT("packet received @ %i , RSSI = %i\n", packet->rx_meta.timestamp, packet->rx_meta.rssi);
#ifdef HAL_RADIO_USE_HW_CRC
    int cmp = memcmp(data, packet->data, packet->length-2);
#else
    int cmp = memcmp(data, packet->data, packet->length);
#endif
    if(cmp != 0)
    {
        DPRINT("Unexpected data received! %d\n", cmp);
    }
    else
    {
        DPRINT("RX OK!\n");
    }

    hw_watchdog_feed();
}
// TODO we assume a fifo contains only ALP commands, but according to spec this can be any kind of "Request"
// we will see later what this means. For instance how to add a request which starts D7AAdvP etc
d7asp_queue_result_t d7asp_queue_alp_actions(d7asp_master_session_t* session, uint8_t* alp_payload_buffer, uint8_t alp_payload_length)
{
    DPRINT("Queuing ALP actions");
    // TODO can be called in all session states?
    assert(session == &current_master_session); // TODO tmp
    assert(session->request_buffer_tail_idx + alp_payload_length < MODULE_D7AP_FIFO_COMMAND_BUFFER_SIZE);
    assert(session->next_request_id < MODULE_D7AP_FIFO_MAX_REQUESTS_COUNT); // TODO do not assert but let upper layer handle this

    single_request_retry_limit = 3; // TODO read from SEL config file

    // add request to buffer
    // TODO request can contain 1 or more ALP commands, find a way to group commands in requests instead of dumping all requests in one buffer
    uint8_t request_id = session->next_request_id;
    session->requests_indices[request_id] = session->request_buffer_tail_idx;
    session->requests_lengths[request_id] = alp_payload_length;
    memcpy(session->request_buffer + session->request_buffer_tail_idx, alp_payload_buffer, alp_payload_length);
    session->request_buffer_tail_idx += alp_payload_length + 1;
    session->next_request_id++;

    // TODO for master only set to pending when asked by upper layer (ie new function call)
    if(d7asp_state == D7ASP_STATE_IDLE)
        switch_state(D7ASP_STATE_MASTER);
    else if(d7asp_state == D7ASP_STATE_SLAVE)
        switch_state(D7ASP_STATE_SLAVE_PENDING_MASTER);

    return (d7asp_queue_result_t){ .fifo_token = session->token, .request_id = request_id };
}

bool d7asp_process_received_packet(packet_t* packet, bool extension)
{
    hw_watchdog_feed(); // TODO do here?
    d7asp_result_t result = {
        .channel = packet->hw_radio_packet.rx_meta.rx_cfg.channel_id,
        .rx_level =  - packet->hw_radio_packet.rx_meta.rssi,
        .link_budget = (packet->dll_header.control_eirp_index + 32) - packet->hw_radio_packet.rx_meta.rssi,
        .target_rx_level = 80, // TODO not implemented yet, use default for now
        .status = {
            .ucast = 0, // TODO
            .nls = packet->d7anp_ctrl.origin_addressee_ctrl_nls_enabled,
            .retry = false, // TODO
            .missed = false, // TODO
        },
        .response_to = packet->d7atp_tc,
        .addressee = packet->d7anp_addressee
        // .fifo_token and .seqnr filled below
    };

    if(d7asp_state == D7ASP_STATE_MASTER)
    {
        assert(packet->d7atp_dialog_id == current_master_session.token);
        assert(packet->d7atp_transaction_id == current_request_id);

        // received ack
        DPRINT("Received ACK");
        if(current_master_session.config.qos.qos_resp_mode != SESSION_RESP_MODE_NO
           && current_master_session.config.qos.qos_resp_mode != SESSION_RESP_MODE_NO_RPT)
        {
          // for SESSION_RESP_MODE_NO and SESSION_RESP_MODE_NO_RPT the request was already marked as done
          // upon successfull CSMA insertion. We don't care about response in these cases.

          result.fifo_token = current_master_session.token;
          result.seqnr = current_request_id;
          bitmap_set(current_master_session.success_bitmap, current_request_id);
          mark_current_request_done();
          assert(packet != current_request_packet);
        }

        alp_d7asp_request_completed(result, packet->payload, packet->payload_length);
//          if(d7asp_init_args != NULL && d7asp_init_args->d7asp_fifo_request_completed_cb != NULL)
//              d7asp_init_args->d7asp_fifo_request_completed_cb(result, packet->payload, packet->payload_length); // TODO ALP should notify app if needed, refactor

        packet_queue_free_packet(packet); // ACK can be cleaned

        // switch to the state slave when the D7ATP Dialog Extension Procedure is initiated and all request are handled
        if ((extension) && (current_request_id == current_master_session.next_request_id - 1))
        {
            DPRINT("Dialog Extension Procedure is initiated, mark the FIFO flush"
                    " completed before switching to a responder state");
            alp_d7asp_fifo_flush_completed(current_master_session.token, current_master_session.progress_bitmap,
                                           current_master_session.success_bitmap, REQUESTS_BITMAP_BYTE_COUNT);
            current_master_session.state = D7ASP_MASTER_SESSION_IDLE;
            d7atp_signal_dialog_termination();
            switch_state(D7ASP_STATE_SLAVE);
        }
        return true;
    }
    else if(d7asp_state == D7ASP_STATE_IDLE || d7asp_state == D7ASP_STATE_SLAVE)
    {
        // received a request, start slave session, process and respond
        if(d7asp_state == D7ASP_STATE_IDLE)
            switch_state(D7ASP_STATE_SLAVE); // don't switch when already in slave state

        result.fifo_token = packet->d7atp_dialog_id;
        result.seqnr = packet->d7atp_transaction_id;


        // TODO move to ALP
        if(packet->payload_length > 0)
        {
            if(alp_get_operation(packet->payload) == ALP_OP_RETURN_FILE_DATA)
            {
                // received unsollicited data, notify appl
                DPRINT("Received unsollicited data");
                if(d7asp_init_args != NULL && d7asp_init_args->d7asp_received_unsollicited_data_cb != NULL)
                    d7asp_init_args->d7asp_received_unsollicited_data_cb(result, packet->payload, packet->payload_length);

                packet->payload_length = 0; // no response payload
            }
            else
            {
                // build response, we will reuse the same packet for this
                // we will first try to process the command against the local FS
                // if the FS handler cannot process this, and a status response is requested, a status operand will be present in the response payload
                bool handled = alp_process_command(packet->payload, packet->payload_length, packet->payload, &packet->payload_length, ALP_CMD_ORIGIN_D7ASP);

                // ... and if not handled we'll give the application a chance to handle this by returning an ALP response.
                // if the application fails to handle the request as well the ALP status operand supplied by alp_process_command_fs_itf() will be transmitted (if requested)
                if(!handled)
                {
                  DPRINT("ALP command could not be processed by local FS");
                  if(d7asp_init_args != NULL && d7asp_init_args->d7asp_received_unhandled_alp_command_cb != NULL)
                  {
                      DPRINT("ALP command passed to application for processing");
                      d7asp_init_args->d7asp_received_unhandled_alp_command_cb(packet->payload, packet->payload_length, packet->payload, &packet->payload_length);
                  }
                }
            }
        }

        // TODO notify upper layer?

        // execute slave transaction
        if(packet->payload_length == 0 && !packet->d7atp_ctrl.ctrl_is_ack_requested)
            goto discard_request; // no need to respond, clean up

        DPRINT("Sending response");

        current_response_packet = packet;

        /*
         * activate the dialog extension procedure in the unicast response if the dialog is terminated
         * and a master session is pending
         */
        if((packet->dll_header.control_target_address_set) && (packet->d7atp_ctrl.ctrl_is_stop)
                && (d7asp_state == D7ASP_STATE_SLAVE_PENDING_MASTER))
        {
            packet->d7atp_ctrl.ctrl_is_start = true;
            // TODO set packet->d7anp_listen_timeout according the time remaining in the current transaction
            // + the maximum time to send the first request of the pending session.
        }
        else
            packet->d7atp_ctrl.ctrl_is_start = 0;

        d7atp_respond_dialog(packet);
        return true;
    }
    else
        assert(false);

    discard_request:
        packet_queue_free_packet(packet);
        return false;
}
static void flush_fifos()
{
    assert(d7asp_state == D7ASP_STATE_MASTER);
    DPRINT("Flushing FIFOs");
    hw_watchdog_feed(); // TODO do here?

    if(current_request_id == NO_ACTIVE_REQUEST_ID)
    {
        // find first request which is not acked or dropped
        int8_t found_next_req_index = bitmap_search(current_master_session.progress_bitmap, false, MODULE_D7AP_FIFO_MAX_REQUESTS_COUNT);
        if(found_next_req_index == -1 || found_next_req_index == current_master_session.next_request_id)
        {
            // we handled all requests ...
            flush_completed();

            // TODO move callback to ALP?
//            if(d7asp_init_args != NULL && d7asp_init_args->d7asp_fifo_flush_completed_cb != NULL)
//                d7asp_init_args->d7asp_fifo_flush_completed_cb(fifo.token, fifo.progress_bitmap, fifo.success_bitmap, REQUESTS_BITMAP_BYTE_COUNT);

            return;
        }

        current_request_id = found_next_req_index;
        current_request_retry_count = 0;

        current_request_packet = packet_queue_alloc_packet();
        packet_queue_mark_processing(current_request_packet);
        current_request_packet->d7anp_addressee = &(current_master_session.config.addressee); // TODO explicitly pass addressee down the stack layers?

        memcpy(current_request_packet->payload, current_master_session.request_buffer + current_master_session.requests_indices[current_request_id], current_master_session.requests_lengths[current_request_id]);
        current_request_packet->payload_length = current_master_session.requests_lengths[current_request_id];

        // TODO calculate Tl and Tc
        // Tc(NB, LEN, CH) = (SFC  * NB  + 1) * TTX(CH, LEN) + TG with NB the number of concurrent devices and SF the collision Avoidance Spreading Factor
        // Tl should correspond to the maximum time needed to send the remaining requests in the FIFO including the RETRY parameter

        // For now, set Tc and Tl according the transmission_timeout_period set in the access profile
        dae_access_profile_t active_addressee_access_profile;
        fs_read_access_class(current_request_packet->d7anp_addressee->ctrl.access_class, &active_addressee_access_profile);
        current_request_packet->d7atp_tc = active_addressee_access_profile.transmission_timeout_period;
        current_request_packet->d7anp_listen_timeout = active_addressee_access_profile.transmission_timeout_period;
    }
    else
    {
        // retrying request ...
        DPRINT("Current request retry count: %i", current_request_retry_count);
        if(current_request_retry_count == single_request_retry_limit)
        {
            // mark request as failed and pop
            mark_current_request_done();
            DPRINT("Request reached single request retry limit (%i), skipping request", single_request_retry_limit);
            packet_queue_free_packet(current_request_packet);
            current_request_id = NO_ACTIVE_REQUEST_ID;
            sched_post_task(&flush_fifos); // continue flushing until all request handled ...
            return;
        }

        // TODO stop on error
    }

    // TODO calculate D7ANP timeout (and update during transaction lifetime) (based on Tc, channel, cs, payload size, # msgs, # retries)
    d7atp_start_dialog(current_master_session.token, current_request_id, (current_request_id == current_master_session.next_request_id - 1), current_request_packet, &current_master_session.config.qos);
}