/*
 * Copyright (c) 2022, Kentaro Sekimoto
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  -Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *  -Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * 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 THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS 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 <stdint.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "pico/time.h"
#include "hardware/gpio.h"
#include "hardware/resets.h"
#include "hardware/spi.h"
#include "common.h"
#include "mcu_hw.h"

void mdelay(uint32_t m) {
    sleep_us((uint64_t)m * 1000ull);
}

void mcu_init(void) {
    stdio_init_all();
}

void mcu_gpio_mode_output(uint32_t pin) {
    (void)pin;
    gpio_init((uint)pin);
    gpio_set_dir((uint)pin, GPIO_OUT);
}

void mcu_gpio_mode_input(uint32_t pin) {
    (void)pin;
    gpio_init((uint)pin);
    gpio_set_dir((uint)pin, GPIO_IN);
}

void mcu_gpio_write(uint32_t pin, bool level) {
    (void)pin;
    (void)level;
    gpio_put((uint)pin, level);
}

bool mcu_gpio_read(uint32_t pin) {
    (void)pin;
    return (bool)gpio_get((uint)pin);
}

void mcu_spi_write_byte(uint32_t ch, uint8_t dat) {
    // ToDo: implement
    (void)ch;
    (void)dat;
    spi_write_blocking((spi_inst_t *)spi_default, (const uint8_t *)&dat, (size_t)1);
}

static inline void spi_reset(spi_inst_t *spi) {
    invalid_params_if(SPI, spi != spi0 && spi != spi1);
    reset_block(spi == spi0 ? RESETS_RESET_SPI0_BITS : RESETS_RESET_SPI1_BITS);
}

static inline void spi_unreset(spi_inst_t *spi) {
    invalid_params_if(SPI, spi != spi0 && spi != spi1);
    unreset_block_wait(spi == spi0 ? RESETS_RESET_SPI0_BITS : RESETS_RESET_SPI1_BITS);
}

void mcu_spi_init(uint32_t ch, uint32_t mosi, uint32_t miso, uint32_t clk, uint32_t cs, uint32_t speed, uint32_t bits, uint32_t mode) {
    (void)ch;
    (void)mosi;
    (void)miso;
    (void)clk;
    (void)cs;
    (void)bits;

    uint polarity = 0;
    uint phase = 0;
    switch(mode) {
    case 0:
        polarity = 0;
        phase = 0;
        break;
    case 1:
        polarity = 0;
        phase = 1;
        break;
    case 2:
        polarity = 1;
        phase = 0;
        break;
    case 3:
        polarity = 1;
        phase = 1;
        break;
    default:
        break;
    }
    spi_init(spi_default, (uint)speed);
    spi_set_baudrate(spi_default, (uint)speed);
    spi_set_format(spi_default, (uint)bits, polarity, phase, SPI_MSB_FIRST);
    gpio_set_function((uint)clk, GPIO_FUNC_SPI);
    gpio_set_function((uint)miso, GPIO_FUNC_SPI);
    gpio_set_function((uint)mosi, GPIO_FUNC_SPI);

    gpio_init((uint)cs);
    gpio_set_dir((uint)cs, GPIO_OUT);
    gpio_put((uint)cs, 1);
}

void mcu_spi_get_conf(uint32_t ch, uint16_t *spcmd, uint8_t *spbr){
    // ToDo: implement
    (void)ch;
    (void)spcmd;
    (void)spbr;
}

void mcu_spi_start_xfer(uint32_t ch, uint16_t spcmd, uint8_t spbr) {
    // ToDo: implement
    (void)ch;
    (void)spcmd;
    (void)spbr;
}

void mcu_spi_end_xfer(uint32_t ch) {
    // ToDo: impelemt
    (void)ch;

}

void mcu_spi_transfer(size_t len, const uint8_t *src, uint8_t *dst) {
    spi_write_read_blocking((spi_inst_t *)spi_default, (const uint8_t *)src, (uint8_t *)dst, (size_t)len);
}
