#include "tm1640_gpio.h"
#include "sysfs_gpio.h"
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "dy_utils/dy_common.h"


static inline void bit_delay(void) { };

static int tm1640_gpio_start(tm1640_gpio *gpio_handler)
{
    if (sysfs_gpio_set_value(gpio_handler->gpio_din, 0)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_start failed\n");
        return -1;
    }
    bit_delay();

    if (sysfs_gpio_set_value(gpio_handler->gpio_sclk, 0)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_start failed\n");
        return -1;
    }
    bit_delay();

    return 0;
}

static int tm1640_gpio_end(tm1640_gpio *gpio_handler)
{
    if (sysfs_gpio_set_value(gpio_handler->gpio_din, 0)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_end failed\n");
        return -1;
    }
    bit_delay();

    if (sysfs_gpio_set_value(gpio_handler->gpio_sclk, 1)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_end failed\n");
        return -1;
    }
    bit_delay();

    if (sysfs_gpio_set_value(gpio_handler->gpio_din, 1)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_end failed\n");
        return -1;
    }
    bit_delay();

    return 0;
}

static int tm1640_gpio_send_byte(tm1640_gpio *gpio_handler, unsigned char v)
{
    unsigned char b = 0x01; /* start at LSB */
    while(b) {
        if (sysfs_gpio_set_value(gpio_handler->gpio_din, !! (v & b))) {
            dy_syslog(LOG_ERR, "tm1640_gpio_send_byte failed\n");
            return -1;
        }
        bit_delay();

        if (sysfs_gpio_set_value(gpio_handler->gpio_sclk, 1)) {
            dy_syslog(LOG_ERR, "tm1640_gpio_send_byte failed\n");
            return -1;
        }
        bit_delay();

        if (sysfs_gpio_set_value(gpio_handler->gpio_sclk, 0)) {
            dy_syslog(LOG_ERR, "tm1640_gpio_send_byte failed\n");
            return -1;
        }
        bit_delay();
        b <<= 1; /* move from LSB to MSB */
    }

    return 0;
}

tm1640_gpio*tm1640_gpio_init(int n_gpio_din, int n_gpio_sclk)
{
    tm1640_gpio *gpio_handler = calloc(1, sizeof(*gpio_handler));

    gpio_handler->gpio_sclk = sysfs_gpio_open(n_gpio_sclk);
    gpio_handler->gpio_din  = sysfs_gpio_open(n_gpio_din);

    if (gpio_handler->gpio_din == NULL || gpio_handler->gpio_sclk == NULL) {
        goto err_out;
    }

    if (sysfs_gpio_set_direction(gpio_handler->gpio_din, 1) || 
        sysfs_gpio_set_direction(gpio_handler->gpio_sclk, 1) || 
        sysfs_gpio_set_value(gpio_handler->gpio_din, 1) || 
        sysfs_gpio_set_value(gpio_handler->gpio_sclk, 1)) {
        goto err_out;
    }

    return gpio_handler;

err_out:
    dy_syslog(LOG_ERR, "(%d, %d): error during initialization.\n", n_gpio_din, n_gpio_sclk);
    if(gpio_handler) {
        tm1640_gpio_close(gpio_handler);
    }

    return NULL;
}

void tm1640_gpio_close(tm1640_gpio *gpio_handler)
{
    if (gpio_handler)
    {
        if (gpio_handler->gpio_din)
            sysfs_gpio_close(gpio_handler->gpio_din);
        if (gpio_handler->gpio_sclk)
            sysfs_gpio_close(gpio_handler->gpio_sclk);
        free(gpio_handler);
    }
}

int tm1640_gpio_command(tm1640_gpio *gpio_handler, unsigned char v)
{
    if (tm1640_gpio_start(gpio_handler) || tm1640_gpio_send_byte(gpio_handler, v) || tm1640_gpio_end(gpio_handler)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_command failed\n");
        return -1;
    }
        
    return 0;
}


int tm1640_gpio_byte(tm1640_gpio *gpio_handler, const unsigned char group_id, const unsigned char data)
{
    if (tm1640_gpio_start(gpio_handler) || tm1640_gpio_send_byte(gpio_handler, TM1640_ADDR(group_id))) {
        dy_syslog(LOG_ERR, "tm1640_gpio_byte failed\n");
        return -1;
    }

	if (tm1640_gpio_send_byte(gpio_handler, data)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_byte failed\n");
		return -1;
    }

	if (tm1640_gpio_end(gpio_handler)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_byte failed\n");
		return -1;
    }

	return 0;
}

int tm1640_gpio_data(tm1640_gpio *gpio_handler, const unsigned char *data, int len)
{
    int i;
    if (tm1640_gpio_start(gpio_handler) || tm1640_gpio_send_byte(gpio_handler, TM1640_ADDR(0))) {
        dy_syslog(LOG_ERR, "tm1640_gpio_data failed\n");
        return -1;
    }

    for (i=0; i<len; i++) {
        if (tm1640_gpio_send_byte(gpio_handler, data[i])) {
            dy_syslog(LOG_ERR, "tm1640_gpio_data failed\n");
            return -1;
        }
    }

    if (tm1640_gpio_end(gpio_handler)) {
        dy_syslog(LOG_ERR, "tm1640_gpio_data failed\n");
        return -1;
    }

    return 0;
}