/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include "syscfg/syscfg.h"

#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif

#if MYNEWT_VAL(BTTESTER_PIPE_RTT)

#include "os/mynewt.h"
#include "console/console.h"
#include "rtt/SEGGER_RTT.h"

#include "bttester_pipe.h"

static struct hal_timer rtt_timer;

static bttester_pipe_recv_cb app_cb;

static uint8_t *recv_buf;
static size_t recv_buf_len;
static size_t recv_off;

static uint8_t rtt_buf_up[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_UP)];
static uint8_t rtt_buf_down[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_DOWN)];
static int rtt_index_up, rtt_index_down;

#define RTT_INPUT_POLL_INTERVAL_MIN     10 /* ms */
#define RTT_INPUT_POLL_INTERVAL_STEP    10 /* ms */
#define RTT_INPUT_POLL_INTERVAL_MAX     250 /* ms */

static int rtt_pipe_get_char(unsigned int index)
{
    char c;
    int r;

    r = (int)SEGGER_RTT_Read(index, &c, 1u);
    if (r == 1) {
        r = (int)(unsigned char)c;
    } else {
        r = -1;
    }

    return r;
}

static void
rtt_pipe_poll_func(void *arg)
{
    static uint32_t itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN;
    static int key = -1;
    int avail = recv_buf_len - recv_off;

    if (key < 0) {
        key = rtt_pipe_get_char((unsigned int) rtt_index_down);
    }

    if (key < 0) {
        itvl_ms += RTT_INPUT_POLL_INTERVAL_STEP;
        itvl_ms = min(itvl_ms, RTT_INPUT_POLL_INTERVAL_MAX);
    } else {
        while (key >= 0 && avail > 0) {
            recv_buf[recv_off] = (uint8_t) key;
            recv_off++;
            avail = recv_buf_len - recv_off;
            key = rtt_pipe_get_char((unsigned int) rtt_index_down);
        }

        /*
         * Call application callback with received data. Application
         * may provide new buffer or alter data offset.
         */
        recv_buf = app_cb(recv_buf, &recv_off);

        itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN;
    }

    os_cputime_timer_relative(&rtt_timer, itvl_ms * 1000);
}

int
bttester_pipe_send(const uint8_t *data, int len)
{
    SEGGER_RTT_Write((unsigned int) rtt_index_up, data, (unsigned int) len);
    return 0;
}

void
bttester_pipe_register(uint8_t *buf, size_t len, bttester_pipe_recv_cb cb)
{
    recv_buf = buf;
    recv_buf_len = len;
    app_cb = cb;
}

int
bttester_pipe_init(void)
{
    rtt_index_up = SEGGER_RTT_AllocUpBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME),
                                            rtt_buf_up, sizeof(rtt_buf_up),
                                            SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);

    if (rtt_index_up < 0) {
        return -1;
    }

    rtt_index_down = SEGGER_RTT_AllocDownBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME),
                                                rtt_buf_down, sizeof(rtt_buf_down),
                                                SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);

    if (rtt_index_down < 0) {
        return -1;
    }

    console_printf("Using up-buffer #%d\n", rtt_index_up);
    console_printf("Using down-buffer #%d\n", rtt_index_down);

    os_cputime_timer_init(&rtt_timer, rtt_pipe_poll_func, NULL);
    os_cputime_timer_relative(&rtt_timer, 200000);
    return 0;
}
#endif /* MYNEWT_VAL(BTTESTER_PIPE_RTT) */
