#include "hc-12.h"
#include "mysql.h"

pthread_mutex_t mutex;

void *pull_data_mysql(void *p)
{
    char sql_insert[1024] = { 0 };
    time_t current_time;
    struct tm *time_info;
    char timeString[20];
    static int r = 0;

    Thread *args = (Thread *) p;

    if (args->data->co >= 0 && args->data->co2 >= 0 && args->data->ch4_ >= 0) {
        time(&current_time);    					// 获取当前时间
        time_info = localtime(&current_time); 		// 将时间转换为本地时间
        strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", time_info); // 格式化时间字符串

        sprintf(sql_insert, "INSERT INTO %s (TIME, CO, CO2, CH4, TEMPERATURE, RP2040_TEMPERATURE) VALUES ('%s', '%.0fppm', '%.0fppm', '%.0fppm', '%.3f°C', '%.02f°C');",
                args->conf->MYSQL_TABLES, timeString, args->data->co, args->data->co2, args->data->ch4_, args->data->ds18b20, args->data->rp2040);
        printf("%s\n", sql_insert);

        r = _mysql(sql_insert, args->conf->MYSQL_HOST, args->conf->MYSQL_PORT_, args->conf->MYSQL_USRT, args->conf->MYSQL_PASSWORD, args->conf->MYSQL_DB, args->conf->MYSQL_TABLES);
        if (r == -1) {
            perror("_mysql");
        }
    }

    return NULL;
}

int send_mail(char *mail, const char *string, CONF *conf, DATA *data)
{
    static char *buff;
    const char *subject = "厨房危险(火灾)报警!!!";

    buff = (char *)alloca(BUFFER * 10);
    if (buff == NULL)
        perror("out of memory.");

    sprintf(buff, MAIL, mail, subject, string);
    printf("%s\n", buff);
    system(buff);

    return 0;
}

void *createSharedMemory(int key, size_t size)
{
    static int shm_id;
    static void *shared_memory;

    shm_id = shmget(key, size, IPC_CREAT | 0666);
    if (shm_id == -1) {
        perror("shmget");
        exit(1);
    }

    shared_memory = shmat(shm_id, NULL, 0);
    if (shared_memory == (void *)-1) {
        perror("shmat");
        exit(1);
    }

    return shared_memory;
}

void detachAndDeleteSharedMemory(int key, void *shared_memory)
{
    static int shm_id;

    if (shmdt(shared_memory) == -1) {
        perror("shmdt");
        exit(1);
    }

    shm_id = shmget(key, 0, 0);
    if (shmctl(shm_id, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }
}

int loop(int fd, CONF *conf, DATA *DATA)
{
    static char receivedString[BUFFER];
    static int index = 0;
    static int timing = 0;

    DATA->ds18b20_shm = (int *)createSharedMemory(1, sizeof(int));
    DATA->co_shm = (int *)createSharedMemory(2, sizeof(int));
    DATA->co2_shm = (int *)createSharedMemory(3, sizeof(int));
    *DATA->ds18b20_shm = 0;
    *DATA->co_shm = 0;
    *DATA->co2_shm = 0;

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0)               // 子进程
    {
        int conut = 0;

        while (1) {

            pthread_mutex_lock(&mutex); // 加锁
            if (conut >= DATA->_time) {
                *DATA->ds18b20_shm = 1;
                *DATA->co_shm = 1;
                *DATA->co2_shm = 1;

                conut = 0;
            }
            pthread_mutex_unlock(&mutex); // 解锁

            conut++;
            sleep(1);
        }

    } else                      // 父进程
    {
        while (1) {
            if (serialDataAvail(fd)) {
                // 串口有数据可读
                char data = serialGetchar(fd);
                if (data != '\n') {
                    // 如果接收到的不是回车符，则添加到接收字符串中
                    receivedString[index] = data;
                    index++;
                    // 检查是否超出数组长度，如果超出则重置索引
                    if (index >= BUFFER) {
                        index = 0;
                    }
                } else {
                    // 如果接收到回车符，则打印接收到的字符串，并清空接收字符串和索引
                    receivedString[index] = '\0'; // 添加字符串结束符
                    if (index > 0) {

                        // RP2040
                        if (strstr(receivedString, "Onboard")) {

                            if (2 == (sscanf(receivedString, "Onboard temperature %f°C %f°F\n", &(DATA->rp2040), &(DATA->rp2040_f)))) {
                                DATA->rp2040_num++;
                                printf("Onboard temperature %.02f°C %.02f°F\n", DATA->rp2040, DATA->rp2040_f);
                            }

                        }
                        // DS18B20
                        if (strstr(receivedString, "Temperature")) {

                            if (1 == (sscanf(receivedString, "Temperature: %f°C\n", &DATA->ds18b20))) {
                                printf("Temperature: %.3f°C\n", DATA->ds18b20);

                                if (DATA->ds18b20 >= atoi(conf->temperature)) {
                                    DATA->ds18b20_num++;
                                    printf("请注意室内温度%.3f°C超过%d次!\n", DATA->ds18b20, DATA->ds18b20_num);

                                    if (DATA->ds18b20_num >= 3) {
                                        // 第一次发送
                                        if (DATA->ds18b20_1st == 1) {
                                            DATA->ds18b20_1st = 0;
                                            send_mail(conf->mail, receivedString, conf, DATA);
                                        }
                                        // 如果浓度持续一段时间, (降低发送频率, 若干秒后再次发送)
                                        pthread_mutex_lock(&mutex); // 加锁
                                        if (*DATA->ds18b20_shm == 1) {
                                            send_mail(conf->mail, receivedString, conf, DATA);
                                            *DATA->ds18b20_shm = 0;
                                        }
                                        pthread_mutex_unlock(&mutex); // 解锁

                                        DATA->ds18b20_num = 0;
                                    }

                                }

                            }

                        }
                        // CH4
                        if (strstr(receivedString, "CH4 Concentration")) {
                            if (1 == (sscanf(receivedString, "CH4 Concentration: %f\n", &DATA->ch4_))) {
                                printf("CH4 Concentration: %.0F ppm\n", DATA->ch4_);
                            }

                        }
                        // CO
                        if (strstr(receivedString, "CO")) {

                            if (1 == (sscanf(receivedString, "CO Concentration: %f ppm", &DATA->co))) {
                                printf("CO Concentration: %.2f ppm\n", DATA->co);

                                if (DATA->co >= atoi(conf->co)) {
                                    DATA->co_num++;
                                    printf("请注意室内CO浓度%.3f°C超过%d次!\n", DATA->co, DATA->co_num);

                                    if (DATA->co_num >= 3) {
                                        if (DATA->co_1st == 1) {
                                            DATA->co_1st = 0;
                                            send_mail(conf->mail, receivedString, conf, DATA);
                                        }
                                        pthread_mutex_lock(&mutex);
                                        if (*DATA->co_shm == 1) {
                                            send_mail(conf->mail, receivedString, conf, DATA);
                                            *DATA->co_shm = 0;
                                        }
                                        pthread_mutex_unlock(&mutex);

                                        DATA->co_num = 0;
                                    }

                                }
                            }
                        }
                        // CO2
                        if (strstr(receivedString, "CO2")) {

                            if (1 == (sscanf(receivedString, "CO2 Concentration: %f ppm", &DATA->co2))) {
                                printf("CO2 Concentration: %.2f ppm\n", DATA->co2);

                                if (DATA->co2 >= atoi(conf->co2)) {
                                    DATA->co2_num++;
                                    printf("请注意室内CO2浓度%.3f°C超过%d次!\n", DATA->co2, DATA->co2_num);

                                    if (DATA->co2_num >= 3) {
                                        if (DATA->co2_1st == 1) {
                                            DATA->co2_1st = 0;
                                            send_mail(conf->mail, receivedString, conf, DATA);
                                        }
                                        pthread_mutex_lock(&mutex);
                                        if (*DATA->co2_shm == 1) {
                                            send_mail(conf->mail, receivedString, conf, DATA);
                                            *DATA->co2_shm = 0;
                                        }
                                        pthread_mutex_unlock(&mutex);

                                        DATA->co2_num = 0;
                                    }
                                }
                            }

                            printf("\n");
                        }
                    }

                    receivedString[0] = '\0';
                    index = 0;
                }

            }

            // 上传传感器数据到Mysql数据库
            timing++;
            if (0 == strcasecmp(conf->MYSQL_ON, "on")) {
                if (timing >= (atoi(conf->PUSH_MYSQL_DATA_TIME) * 100)) {

                    Thread args = { conf, DATA };
                    pthread_t tid;
                    // 创建线程，并传递包含两个结构体的参数
                    if (pthread_create(&tid, NULL, pull_data_mysql, &args) != 0) {
                        perror("pthread_create");
                        return 1;
                    }
                    // 等待线程结束
                    pthread_join(tid, NULL);

                    timing = 0;
                }
            }

            delay(10);
        }

    }

    waitpid(pid, NULL, 0);
    // 断开共享内存连接
    detachAndDeleteSharedMemory(1, DATA->ds18b20_shm);
    detachAndDeleteSharedMemory(2, DATA->co_shm);
    detachAndDeleteSharedMemory(3, DATA->co2_shm);

    return 0;
}

int redirect_stdout_to_file(int fd, const char *filename)
{
    fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        perror("dup2");
        return 1;
    }

    return 0;
}

int main()
{
    int logfd = -1;
    int fd = -1;

    if (nice(-20) == -1) {
        perror("nice");
        return 1;
    }

    if (0 != daemon(1, 1)) {
        perror("daemon");
    }

    if ((fd = serialOpen("/dev/serial0", 9600)) < 0) {
        printf("Unable to open device\n");
        return 1;
    } else {
        printf("Serial port opened\n");
    }

    CONF *conf = (struct CONF *)malloc(sizeof(struct CONF));
    memset(conf, 0, sizeof(struct CONF));
    conf->mail = strdup(read_conf("hc-12.conf", "global", "MAIL"));
    conf->temperature = strdup(read_conf("hc-12.conf", "global", "TEMPERATURE"));
    conf->co = strdup(read_conf("hc-12.conf", "global", "CO"));
    conf->co2 = strdup(read_conf("hc-12.conf", "global", "CO2"));

    conf->MYSQL_ON = strdup(read_conf("hc-12.conf", "global", "MYSQL"));
    if (0 == strcasecmp(conf->MYSQL_ON, "on")) {
        conf->PUSH_MYSQL_DATA_TIME = strdup(read_conf("hc-12.conf", "global", "PUSH_MYSQL_DATA_TIME"));
        conf->MYSQL_HOST = strdup(read_conf("hc-12.conf", "global", "MYSQL_HOST"));
        conf->MYSQL_PORT_ = strdup(read_conf("hc-12.conf", "global", "MYSQL_PORT"));
        conf->MYSQL_USRT = strdup(read_conf("hc-12.conf", "global", "MYSQL_USRT"));
        conf->MYSQL_PASSWORD = strdup(read_conf("hc-12.conf", "global", "MYSQL_PASSWORD"));
        conf->MYSQL_DB = strdup(read_conf("hc-12.conf", "global", "MYSQL_DB"));
        conf->MYSQL_TABLES = strdup(read_conf("hc-12.conf", "global", "MYSQL_TABLES"));
    }

    DATA *data = (struct DATA *)malloc(sizeof(struct DATA));
    memset(data, 0, sizeof(struct DATA));
    data->ds18b20 = 0;
    data->ch4 = 0;
    data->rp2040 = 0;
    data->rp2040_f = 0;
    data->co = 0;
    data->co2 = 0;
    data->rp2040_num = 0;
    data->ch4_num = 0;
    data->ds18b20_num = 0;
    data->co_num = 0;
    data->co2_num = 0;
    data->ds18b20_1st = 1;
    data->ch4_1st = 1;
    data->co_1st = 1;
    data->co2_1st = 1;
    data->_time = 180;

    redirect_stdout_to_file(logfd, "hc-12.log");
    pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
    loop(fd, conf, data);
    pthread_mutex_destroy(&mutex); // 销毁互斥锁

    free(conf->mail);
    free(conf->temperature);
    free(conf->co);
    free(conf->co2);
    free(conf->PUSH_MYSQL_DATA_TIME);
    free(conf->MYSQL_HOST);
    free(conf->MYSQL_PORT_);
    free(conf->MYSQL_USRT);
    free(conf->MYSQL_PASSWORD);
    free(conf->MYSQL_DB);
    free(conf->MYSQL_TABLES);
    free(conf);
    free(data);
    close(fd);
    close(logfd);
    return 0;
}
