#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "TM1637.h"
#include "tm1637out.pio.h"

const uint8_t digit_seg[] = {
    0b00111111,     // 0
    0b00000110,     // 1
    0b01011011,     // 2
    0b01001111,     // 3
    0b01100110,     // 4
    0b01101101,     // 5
    0b01111101,     // 6
    0b00000111,     // 7
    0b01111111,     // 8
    0b01101111,     // 9
};

const uint8_t letter_seg[] = {
    0b01110111,     // A
    0b01111100,     // b
    0b00111001,     // C
    0b01011110,     // d
    0b01111001,     // E
    0b01110001,     // F
    0b00111101,     // G
    0b01110110,     // H
    0b00000100,     // i
    0b00001110,     // J
    0b00000000,     // K
    0b00111000,     // L
    0b00000000,     // M
    0b01010100,     // n
    0b01011100,     // o
    0b01110011,     // P
    0b01100111,     // q
    0b01010000,     // r
    0b01101101,     // S
    0b01111000,     // t
    0b00011100,     // u
    0b00000000,     // V
    0b00000000,     // W
    0b00000000,     // X
    0b01101110,     // y
    0b00000000,     // z
};


// 先頭のTM1637_tのポインタ
static TM1637_t    *first = NULL;

// 文字コード→セグメントコード
uint32_t chr_to_seg(char c)
{
    if(c >= '0' && c <= '9')
        return digit_seg[c - '0'];
    if(c >= 'a' && c <= 'z')
        return letter_seg[c - 'a'];
    if( c >= 'A' && c <= 'Z')
        return letter_seg[c - 'A'];
    if( c == ':' || c == '.')
        return DOT_SEG;
    if( c == '-' )
        return MINUS_SEG;

    return 0;
}

// TM1637を追加する
int TM1637_add(TM1637_t *next)
{
    if (first == NULL) {
        first = next;
        return 0;
    }

    TM1637_t *p = first;

    while(p->next != NULL)
        p = p->next;
    
    p->next = next;

    return 0;
}

TM1637_t *TM1637_init(uint8_t sda_base_pin, uint8_t col, uint8_t cont)
{
    uint8_t colmns, contrast;
    PIO pio;
    uint    sm, pio_offset;
    TM1637_t    *p;

    // デフォルト値の設定
    if(col <= TM1637_MAX_COL && col > 0)
        colmns = col;
    else
        return NULL;
    
    if(cont <= TM1637_MAX_CONTRAST)
        contrast = cont;
    else
        return NULL;
    
    if(pio_claim_free_sm_and_add_program(&tm1637out_program, &pio, &sm, &pio_offset)) {
        // PIO初期化
        tm1637out_program_init(pio, sm, pio_offset, sda_base_pin, sda_base_pin+1);
        // SM起動
        pio_sm_set_enabled(pio, sm, true);
    }
    else    // PIOを確保できなかった
        return NULL;
    
    // ここまでできたら初期化成功と見て構造体を作成する
    p = (TM1637_t *)malloc(sizeof(TM1637_t));
    p->colmns = colmns;
    p->contrast = contrast;
    p->pio = pio;
    p->sm = sm;
    p->offset = pio_offset;
    p->next = NULL;
    p->initialized = true;
    // 末尾に追加
    TM1637_add(p);
    
    // TM1637初期化
    // データセット（自動アドレスインクリメント）
    pio_sm_put_blocking(pio, sm, TM1637_AUTOINC | STOP_COND);
    // アドレス0からカラム数分を0で初期化
    pio_sm_put_blocking(pio, sm, TM1637_ADDRSET| 0 );
    for(int i = 0; i < colmns; i++) {
        uint32_t data = 0;
        if( i == (colmns - 1) )
            data = STOP_COND;
        pio_sm_put_blocking(pio, sm, data);
    }
    // コントラスト値で表示オン
    TM1637_set_contrast(p, contrast);

    return p;
}

int TM1637_set_contrast(TM1637_t *p, uint8_t cont)
{
    if(cont <= TM1637_MAX_CONTRAST)
        p->contrast = cont;
    else
        return TM1637_ERR_INVALID_PARAM;

    pio_sm_put_blocking(p->pio, p->sm, TM1637_DISPON | cont | STOP_COND);
    return 0;
}

int TM1637_putchar(TM1637_t *p, char c, bool dot,uint8_t col)
{
    if(col >= p->colmns)
        return TM1637_ERR_INVALID_PARAM;

    // 固定アドレスデータセット
    pio_sm_put_blocking(p->pio, p->sm, TM1637_DATASET | STOP_COND);
    // アドレス
    pio_sm_put_blocking(p->pio, p->sm, TM1637_ADDRSET | col);
    // セグメントデータ
    pio_sm_put_blocking(p->pio, p->sm, chr_to_seg(c) | (dot ? DOT_SEG : 0) | STOP_COND);
    return 0;
}

int TM1637_putstr(TM1637_t *t, char *str)
{
    int col = 0, p = 0;

    // データセット（自動アドレスインクリメント）
    pio_sm_put_blocking(t->pio, t->sm, TM1637_AUTOINC | STOP_COND);
    // アドレス0セット
    pio_sm_put_blocking(t->pio, t->sm, TM1637_ADDRSET| 0 );
    // 文字数分を出力
    while(str[col] != '\0') {
        uint32_t data = chr_to_seg(str[col++]);
        // ドットマーク
        if(str[col] == '.' || str[col] == ':') {
            data |= DOT_SEG;
            col++;
        }
        // 最後はStop Condition
        if(str[col] == '\0' || ++p >= t->colmns) data |= STOP_COND;
        pio_sm_put_blocking(t->pio, t->sm, data);
        // カラム数に達したら終わり
        if( p >= t->colmns )
            break;
    }
    return 0;
}
