#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>

#include "dy_common.h"
#include <regex.h>

int get_software_ver(char *ver)
{
    FILE *fp = NULL;
    int ret = -1;
    char buf[512];
    const char * path0 = SOFTWARE_VER_PATH;
    const char * path1 = SOFTWARE_VER_PATH1;

    if (ver == NULL) {
        return -1;
    }

again:
    fp = fopen(path0, "r");
    if (fp == NULL && path0 != path1) {
        path0 = path1;
        fp = fopen(path0, "r");
    }
    if (fp == NULL) {
        /* The file could not be opened */
        goto out;
    }
    memset(buf, 0, sizeof(buf));
    fgets(buf, sizeof(buf) - 1, fp);
    fclose(fp); fp = NULL;

    ver[0] = ver[1] = '\0';
    sscanf(buf, "%s", ver);
    if (path0 != path1 && access(path1, R_OK) == 0 &&
        (ver[0] < '0' || ver[0] > '9')) {
        path0 = path1;
        goto again;
    }
    ret = 0;

out:
    return ret;
}

int get_hardware_ver(char *ver)
{
    FILE *fp = NULL;
    int ret = -1;
    char buf[512];

    if (ver == NULL)
    {
        return -1;
    }

    fp = fopen(BOARD_INFO_PATH, "r");
    if (fp == NULL)
    {
        /* The file could not be opened */
        goto out;
    }

    while (fgets(buf, sizeof(buf), fp) != NULL)
    {
        if (strstr(buf, "hardware_ver") != NULL)
        {
            sscanf(buf, "%*[^=]=%*[\t| ]%[0-9,.]", ver);
            // sscanf(buf, "hardware_ver = %[\S]", ver);
            ret = 0;
            break;
        }
    }

out:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return ret;
}

int get_board_sn(char *sn)
{
    FILE *fp = NULL;
    int ret = -1, rval;
    char buf[256];
    regex_t regsn;
    const char * regstr = "^\\s*sn\\s*=\\s*([[:alnum:]]+)\\s*$";

    if (sn == NULL)
    {
        return -1;
    }
    sn[0] = '\0';
    memset(&regsn, 0, sizeof(regsn));

    fp = fopen(BOARD_INFO_PATH, "r");
    if (fp == NULL)
    {
        /* The file could not be opened */
        goto out;
    }

    rval = regcomp(&regsn, regstr, REG_EXTENDED | REG_NEWLINE);
    if (rval) {
        dy_syslog(LOG_ERR, "Error, failed to compile regex '%s': %d\n",
            regstr, rval);
        goto out;
    }

    while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
        int rval;
        regmatch_t regm[2];

        buf[sizeof(buf) - 0x1] = '\0';
        regm[0].rm_so = regm[0].rm_eo = 0;
        regm[1].rm_so = regm[1].rm_eo = 0;

        rval = regexec(&regsn, buf, 0x2, regm, 0);
        if (rval == 0) {
            regoff_t idx, jdx;

            jdx = 0;
            idx = regm[1].rm_so;
            while (idx < regm[1].rm_eo) {
                sn[jdx++] = buf[idx++];
                if (jdx >= SN_MAX_LEN)
                    break;
            }
            sn[jdx++] = '\0';
            ret = 0;
            break;
        }
    }

    regfree(&regsn);
out:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return ret;
}

static char g_board_name[128];
const char *get_board_name()
{
    size_t blen;
    FILE *fp = NULL;
    char buf[128];

    if (g_board_name[0] != '\0')
        return (const char *) g_board_name;

    fp = fopen(BOARD_NAME_PATH, "r");
    if (fp == NULL)
    {
        /* The file could not be opened */
        goto out;
    }

    memset(buf, 0, sizeof(buf));
    fgets(buf, sizeof(buf) - 1, fp);
    blen = strlen(buf);
    while (blen > 0) {
        char cha = buf[blen - 1];
        if (cha != ' ' && cha != '\r' && cha != '\n' && cha != '\t')
            break;
        buf[--blen] = '\0';
    }
    strncpy(g_board_name, buf, sizeof(g_board_name) - 1);
    g_board_name[sizeof(g_board_name) - 1] = '\0';

out:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return (const char *) g_board_name;
}

char *read_file_data(const char *file)
{
    char *data = NULL;
    int size;
    FILE *fp = NULL;

    fp = fopen(file, "r");
    if (fp == NULL)
    {
        dy_syslog(LOG_ERR, "open file %s error", file);
        return NULL;
    }

    size = file_size2(file);
    data = malloc(size + 1);
    if (!data)
    {
        fclose(fp);
        dy_syslog(LOG_ERR, "[UTILS] malloc faile, size:%d", size);
        return NULL;
    }

    fread(data, sizeof(char), size, fp);
    fclose(fp);
    data[size] = 0;

    return data;
}

ssize_t safe_read(int fd, void *buf, size_t count)
{
    ssize_t n;

    do
    {
        n = read(fd, buf, count);
    } while (n < 0 && errno == EINTR);

    return n;
}

ssize_t open_read_close(const char *filename, void *buf, size_t len)
{
    int fd = open(filename, O_RDONLY);
    if (fd < 0)
        return fd;
    ssize_t total = 0;
    ssize_t cc;

    while (len)
    {
        cc = safe_read(fd, buf, len);

        if (cc < 0)
        {
            if (total)
            {
                /* we already have some! */
                /* user can do another read to know the error code */
                return total;
            }
            return cc; /* read() returns -1 on failure. */
        }
        if (cc == 0)
            break;
        buf = ((char *)buf) + cc;
        total += cc;
        len -= cc;
    }

    close(fd);
    return total;
}

char *read_file_data_and_lens(const char *file, int *len)
{
    char *data = NULL;
    int size;
    FILE *fp = NULL;

    fp = fopen(file, "r");
    if (fp == NULL)
    {
        dy_syslog(LOG_ERR, "open file %s error", file);
        return NULL;
    }

    size = file_size2(file);
    data = malloc(size + 1);
    if (!data)
    {
        fclose(fp);
        dy_syslog(LOG_ERR, "[UTILS] malloc faile, size:%d", size);
        return NULL;
    }

    fread(data, sizeof(char), size, fp);
    fclose(fp);
    data[size] = 0;
    if (len != NULL)
    {
        *len = size;
    }

    return data;
}

int write_file_data(const char *file, char *data, int len)
{
    FILE *fp;

    fp = fopen(file, "w");
    if (fp == NULL)
    {
        dy_syslog(LOG_ERR, "open file %s error", file);
        return -1;
    }
    fwrite(data, sizeof(char), len, fp);

    fclose(fp);

    return (0);
}

int get_process_name(char *process_name)
{
    char process_path[64] = {0};
    char *path_end = NULL;

    if (process_name == NULL)
    {
        return -1;
    }
    if (readlink("/proc/self/exe", process_path, sizeof(process_path)) <= 0)
    {
        return -1;
    }
    path_end = strrchr(process_path, '/');
    if (path_end == NULL)
    {
        return -1;
    }
    ++path_end;
    strcpy(process_name, path_end);

    return 0;
}

int format_hex(void *p, size_t l, char *hex_buff, int size)
{
    char symbol[5];
    char line_size = 16;
    char *ptr = p;
    int i, k;
    int n = 0;
    char *pad = "|            | ";

    memset(hex_buff, 0, size);
    strncat(hex_buff, "\n", size);
    n++;
    for (i = 0; i < l; i += line_size)
    {
        strncat(hex_buff, "| ", size > n ? size - n : 0);
        n += 2;
        for (k = 0; k < line_size; k++)
        {
            if (i + k < l)
            {
                sprintf(symbol, "%02X ", (uint8_t)ptr[i + k]);
            }
            else
            {
                sprintf(symbol, "   ");
            }
            strncat(hex_buff, symbol, size > n ? size - n : 0);
            n += strlen(symbol);
        }
        strncat(hex_buff, pad, size > n ? size - n : 0);
        n += strlen(pad);

        for (k = 0; k < line_size; k++)
        {
            if (0x20 <= ptr[i + k] && ptr[i + k] <= 0x7E)
            {
                sprintf(symbol, "%c", (uint8_t)ptr[i + k]);
            }
            else
            {
                sprintf(symbol, ".");
            }
            strncat(hex_buff, symbol, size > n ? size - n : 0);
            n += strlen(symbol);
        }
        strncat(hex_buff, "|\n", size > n ? size - n : 0);
        n += 2;
        if (n >= size)
            break;
    }
    return 0;
}

ssize_t socket_send(int sockfd, const char *buffer, size_t buflen)
{
    ssize_t tmp;
    size_t total = buflen;
    const char *p = buffer;

    while (1)
    {
        tmp = send(sockfd, p, total, 0);
        if (tmp < 0)
        {
            // 当send收到信号时,可以继续写,但这里返回-1.
            if (errno == EINTR)
            {
                continue;
            }

            // 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,
            // 在这里做延时后再重试.
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                usleep(1000);
                continue;
            }

            return -1;
        }

        if ((size_t)tmp == total)
        {
            return buflen;
        }

        total -= tmp;
        p += tmp;
    }

    return tmp;
}

/*
 * Set the state of the GPIO port.
 * @gpio the GPIO pin to set the state for.
 * @state 1 or 0
 * @return true if the state change was successful.
 */
bool gpio_set_value(int gpio, int state)
{
    int fd;       /* File descriptor for GPIO port */
    char buf[29]; /* Write buffer */

    /* Make the GPIO port path */
    snprintf(buf, 29, "/sys/class/gpio/gpio%d/value", gpio);

    /* Try to open GPIO port */
    fd = open(buf, O_WRONLY | O_SYNC);
    if (fd < 0)
    {
        /* The file could not be opened */
        return false;
    }

    /* Set the port state */
    if (write(fd, (state == 1 ? "1" : "0"), 1) < 0)
    {
        close(fd);
        return false;
    }

    /* Close the GPIO port */
    if (close(fd) < 0)
    {
        return false;
    }

    /* Success */
    return true;
}

int string_is_number(char *s)
{
    if (strspn(s, "0123456789") == strlen(s))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

#define LNXALL_KEY 0x9A
int lnxall_encrypt_string(unsigned char *in, int len, unsigned char *out)
{
    unsigned char key = LNXALL_KEY;
    int i = 0;
    unsigned char c;
    unsigned char o;

    if (in == NULL || out == NULL)
    {
        dy_syslog(LOG_ERR, "invalid paramters");
        return -1;
    }

    for (i = 0; i < len; i++)
    {
        c = in[i];
        o = c ^ key;
        out[i] = o;
        key = c;
    }

    return len;
}

int lnxall_decrypt_string(unsigned char *in, int len, unsigned char *out)
{
    unsigned char key = LNXALL_KEY;
    int i = 0;
    unsigned char c;
    unsigned char o;

    if (in == NULL || out == NULL)
    {
        dy_syslog(LOG_ERR, "invalid paramters");
        return -1;
    }

    for (i = 0; i < len; i++)
    {
        c = in[i];
        o = c ^ key;
        out[i] = o;
        key = c ^ key;
    }

    return len;
}

int lnxall_encrypt_file(const char *file)
{
    int lens = 0;
    char *data = read_file_data_and_lens(file, &lens);
    int ret = -1;
    char *output = NULL;

    if (lens <= 0)
    {
        goto out;
    }

    output = malloc(lens);
    if (output == NULL)
    {
        goto out;
    }

    ret = lnxall_encrypt_string((unsigned char *) data, lens, (unsigned char *) output);
    if (ret > 0)
    {
        ret = write_file_data(file, output, ret);
    }
    else
    {
        goto out;
    }

out:
    free(output);
    free(data);
    return ret;
}

char *lnxall_read_encrypted_file(const char *file, int *len)
{
    int lens = 0;
    char *data = read_file_data_and_lens(file, &lens);
    char *output = NULL;
    int ret = 0;

    if (lens <= 0)
    {
        goto out;
    }

    output = malloc(lens);
    if (output == NULL)
    {
        goto out;
    }

    ret = lnxall_decrypt_string((unsigned char *) data, lens, (unsigned char *) output);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "decrypt failed");
    }
    if (len != NULL)
    {
        *len = ret;
    }

out:
    free(data);
    return output;
}
