#include "msystem.h"
#include "mgpio.h"
#include <gpiod.h>

typedef struct mgpio
{
    struct gpiod_chip *chip[7];

    struct gpiod_line *mcu_gpio0_line[24];
    struct gpiod_line *soc_gpio0_line[92];
    struct gpiod_line *soc_gpio1_line[52];
    struct gpiod_line *tca6416_0_line[16];
    struct gpiod_line *tca6408_1_line[8];
    struct gpiod_line *tca6416_2_line[16];
    struct gpiod_line *tca6416_3_line[16];
}mgpio_t;

static mgpio_t mgpio;

void config_gpio_free(int chip, int index)
{

}

void config_gpio_out(int group, int index, int default_v)
{
    mgpio_t* dev = (mgpio_t*)&mgpio;
    struct gpiod_chip *chip = dev->chip[group];
    struct gpiod_line *line = gpiod_chip_get_line(chip, index);
    printv("config(%d, %2d) output(%d).\n", group, index, default_v);
    switch(group)
    {
        case MCU_GPIO0:{ dev->mcu_gpio0_line[index] = line; }break;
        case SOC_GPIO0:{ dev->soc_gpio0_line[index] = line; }break;
        case SOC_GPIO1:{ dev->soc_gpio1_line[index] = line; }break;
        case TCA6416_0:{ dev->tca6416_0_line[index] = line; }break;
        case TCA6408_1:{ dev->tca6408_1_line[index] = line; }break;
        case TCA6416_2:{ dev->tca6416_2_line[index] = line; }break;
        case TCA6416_3:{ dev->tca6416_3_line[index] = line; }break;
    }
    int req = gpiod_line_request_output(line, "gpio-bline", default_v);
    if (req) { printv("Fail to config chip(%d, %d) output(%d)!\n", group, index, default_v); }
}

void config_gpio_in(int group, int index)
{
    mgpio_t* dev = (mgpio_t*)&mgpio;
    struct gpiod_chip *chip = dev->chip[group];
    struct gpiod_line *line = gpiod_chip_get_line(chip, index);
    printv("config(%d, %2d) input.\n", group, index);
    switch(group)
    {
        case MCU_GPIO0:{ dev->mcu_gpio0_line[index] = line; }break;
        case SOC_GPIO0:{ dev->soc_gpio0_line[index] = line; }break;
        case SOC_GPIO1:{ dev->soc_gpio1_line[index] = line; }break;
        case TCA6416_0:{ dev->tca6416_0_line[index] = line; }break;
        case TCA6408_1:{ dev->tca6408_1_line[index] = line; }break;
        case TCA6416_2:{ dev->tca6416_2_line[index] = line; }break;
        case TCA6416_3:{ dev->tca6416_3_line[index] = line; }break;
    }
    int req = gpiod_line_request_input(line, "gpio_state");
    if (req) { printv("Fail to config chip(%d, %d) input!\n", group, index); }
}

int get_gpio_value(int group, int index)
{
    mgpio_t* dev = (mgpio_t*)&mgpio;
    struct gpiod_chip *chip = dev->chip[group];
    struct gpiod_line *line = NULL;
    int value = -1;
    switch(group)
    {
        case MCU_GPIO0:{ line = dev->mcu_gpio0_line[index]; }break;
        case SOC_GPIO0:{ line = dev->soc_gpio0_line[index]; }break;
        case SOC_GPIO1:{ line = dev->soc_gpio1_line[index]; }break;
        case TCA6416_0:{ line = dev->tca6416_0_line[index]; }break;
        case TCA6408_1:{ line = dev->tca6408_1_line[index]; }break;
        case TCA6416_2:{ line = dev->tca6416_2_line[index]; }break;
        case TCA6416_3:{ line = dev->tca6416_3_line[index]; }break;
    }
    if (line)
    {
        value = gpiod_line_get_value(line);
    }
    // printv("get(%d, %2d)=%d \n", group, index, value);
    return value;
}

void set_gpio_value(int group, int index, int value)
{
    mgpio_t* dev = (mgpio_t*)&mgpio;
    struct gpiod_chip *chip = dev->chip[group];
    struct gpiod_line *line = NULL;
    switch(group)
    {
        case MCU_GPIO0:{ line = dev->mcu_gpio0_line[index]; }break;
        case SOC_GPIO0:{ line = dev->soc_gpio0_line[index]; }break;
        case SOC_GPIO1:{ line = dev->soc_gpio1_line[index]; }break;
        case TCA6416_0:{ line = dev->tca6416_0_line[index]; }break;
        case TCA6408_1:{ line = dev->tca6408_1_line[index]; }break;
        case TCA6416_2:{ line = dev->tca6416_2_line[index]; }break;
        case TCA6416_3:{ line = dev->tca6416_3_line[index]; }break;
    }
    if (line)
    {
        gpiod_line_set_value(line, !!value);
        // printv("control(%d, %2d)=%d\n", group, index, value);
    }
}

int mgpio_init(void)
{
    int i, j;
    char filename[128];
    mgpio_t* dev = (mgpio_t*)&mgpio;
    memset(dev, 0, sizeof(mgpio_t));

    for (j=0; j<24; j++) { dev->mcu_gpio0_line[j] = NULL; }
    for (j=0; j<92; j++) { dev->soc_gpio0_line[j] = NULL; }
    for (j=0; j<52; j++) { dev->soc_gpio1_line[j] = NULL; }
    for (j=0; j<16; j++) { dev->tca6416_0_line[j] = NULL; }
    for (j=0; j<8; j++)  { dev->tca6408_1_line[j] = NULL; }
    for (j=0; j<16; j++) { dev->tca6416_2_line[j] = NULL; }
    for (j=0; j<16; j++) { dev->tca6416_3_line[j] = NULL; }

    for (i=0; i<=TCA6416_3; i++)
    {
        sprintf(filename, "/dev/gpiochip%d", i);
        dev->chip[i] = gpiod_chip_open(filename);
        if (dev->chip[i] == NULL)
        {
            printe("open %s failed.\n", filename);
        }else{
            printv("open %s success.\n", filename);
        }
    }
}

void mgpio_exit(void)
{
    int i, j;
    mgpio_t* dev = (mgpio_t*)&mgpio;
    for (j=0; j<24; j++)
    {
        if (dev->mcu_gpio0_line[j])
        {
            if(gpiod_line_is_requested(dev->mcu_gpio0_line[j])) 
            {
                gpiod_line_release(dev->mcu_gpio0_line[j]);
            }
        }
    }
    for (j=0; j<92; j++)
    {
        if (dev->soc_gpio0_line[j])
        {
            if(gpiod_line_is_requested(dev->soc_gpio0_line[j])) 
            {
                gpiod_line_release(dev->soc_gpio0_line[j]);
            }
        }
    }
    for (j=0; j<52; j++)
    {
        if (dev->soc_gpio1_line[j])
        {
            if(gpiod_line_is_requested(dev->soc_gpio1_line[j])) 
            {
                gpiod_line_release(dev->soc_gpio1_line[j]);
            }
        }
    }
    for (j=0; j<16; j++)
    {
        if (dev->tca6416_0_line[j])
        {
            if(gpiod_line_is_requested(dev->tca6416_0_line[j])) 
            {
                gpiod_line_release(dev->tca6416_0_line[j]);
            }
        }
    }
    for (j=0; j<8; j++)
    {
        if (dev->tca6408_1_line[j])
        {
            if(gpiod_line_is_requested(dev->tca6408_1_line[j])) 
            {
                gpiod_line_release(dev->tca6408_1_line[j]);
            }
        }
    }
    for (j=0; j<16; j++)
    {
        if (dev->tca6416_2_line[j])
        {
            if(gpiod_line_is_requested(dev->tca6416_2_line[j])) 
            {
                gpiod_line_release(dev->tca6416_2_line[j]);
            }
        }
    }
    for (j=0; j<16; j++)
    {
        if (dev->tca6416_3_line[j])
        {
            if(gpiod_line_is_requested(dev->tca6416_3_line[j])) 
            {
                gpiod_line_release(dev->tca6416_3_line[j]);
            }
        }
    }
    for (i=0; i<=TCA6416_3; i++)
    {
        if (dev->chip[i] != NULL)
        {
            gpiod_chip_close(dev->chip[i]);
            dev->chip[i] = NULL;
        }
    }
}