#include "snapshoot.h"
#include "pthread.h"
#include "../tag.h"
#include "../proto_forward.h"
#include "../ems/jsonct.h"
#include "../ems/utils.h"
#include <stdio.h>  
#include <dirent.h>  
#include <string.h>  
#include <sys/stat.h>  
#include <unistd.h> 
#include "pub_fun.h"

#include <unistd.h>
#include <iconv.h>

snapshoot_var_s *snapshoot_var_g = NULL;
int sync_snapshoot_data_and_block() //返回0成功阻塞 其他阻塞中不要重复操作
{
    if(snapshoot_var_g==NULL)return -1;
    if(snapshoot_var_g->state == SNAPSHOOT_FREE)
    {
        ems_syslog(LOG_INFO,"snapshoot_var_g->state is free! dont repeat call!"); 
        return -1;
    }
    
    snapshoot_var_g->sync_data = 1;
    while (snapshoot_var_g->state == SNAPSHOOT_BUSY)
    {
        usleep(100*1000);
    }
    return 0;
}

void complete_and_start_snapshoot()
{
    if(snapshoot_var_g==NULL)return;
        snapshoot_var_g->sync_data = 0;
}
int checek_and_make_dir(char *dir)
{
    mode_t dir_permissions = 0666; // 设置目录权限为 rw  
    // 检查目录是否存在  
    if (access(dir, F_OK) == -1) {  
        // 如果目录不存在，则创建它  
        if (mkdir(dir, dir_permissions) == -1) {  
            perror("Error creating directory");  
            return -1;
        } else {  
            ems_syslog(LOG_NOTICE, "Directory '%s' created successfully.\n", dir);  
        }  
    } else {  
        ems_syslog(LOG_NOTICE, "Directory '%s' already exists.\n", dir);  
    }
    return 0;
}

int compare_timestamps(const char *dir_path, const struct dirent *a, const struct dirent *b) {
    struct stat st_a, st_b;
    char path_a[1024], path_b[1024];
    snprintf(path_a, sizeof(path_a), "%s/%s", dir_path, a->d_name);
    snprintf(path_b, sizeof(path_b), "%s/%s", dir_path, b->d_name);
      
    if (stat(path_a, &st_a) != 0 || stat(path_b, &st_b) != 0) {  
        return -1;  
    }  
    return difftime(st_a.st_mtime, st_b.st_mtime);  
}  
  
int deleteDirectory(const char *path) {
    DIR *dir = opendir(path);
    struct dirent *entry;
    struct stat statbuf;

    if (!dir) {
        ems_syslog(LOG_ERR, "opendir error.\n");
        return -1;
    }

    // 遍历目录中的所有文件和子目录
    while ((entry = readdir(dir))!= NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char subPath[strlen(path) + strlen(entry->d_name) + 2];
        sprintf(subPath, "%s/%s", path, entry->d_name);

        if (lstat(subPath, &statbuf) < 0) {
            ems_syslog(LOG_ERR, "Failed to get file status: %s", subPath);
            continue;
        }

        if (S_ISDIR(statbuf.st_mode)) {
            // 如果是子目录，递归调用
            if (deleteDirectory(subPath) < 0) {
                closedir(dir);
                return -2;
            }
        } else {
            // 如果是文件，直接删除
            if (unlink(subPath) < 0) {
                ems_syslog(LOG_ERR, "unlink error.\n");
                closedir(dir);
                return -3;
            }
        }
    }

    // 关闭目录
    if (closedir(dir) < 0) {
        ems_syslog(LOG_ERR, "Failed to close directory: %s", path);
        return -2;
    }

    // 最后删除空目录本身
    if (rmdir(path) < 0) {
        ems_syslog(LOG_ERR, "Failed to delete directory: %s", path);
        return -3;
    }

    return 0;
}

int delete_oldest_directory(const char *dir_path) {  
    DIR *dir;  
    struct dirent *entry;  
    struct dirent *oldest_entry = NULL;  
      
    dir = opendir(dir_path);  
    if (dir == NULL) {  
        perror("opendir");  
        return -1;  
    }  
      
    while ((entry = readdir(dir)) != NULL) {  
        if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
            if (oldest_entry == NULL || compare_timestamps(dir_path, entry, oldest_entry) < 0) {
                oldest_entry = entry;  
            }  
        }  
    }  
      
    closedir(dir);  
      
    if (oldest_entry != NULL) {  
        char oldest_path[1024];  
        snprintf(oldest_path, sizeof(oldest_path), "%s/%s", dir_path, oldest_entry->d_name);  
        if (deleteDirectory(oldest_path) != 0) {  
            return -2;
        } else {  
            ems_syslog(LOG_NOTICE, "Deleted oldest directory: %s\n", oldest_path);  
        }  
    } else {  
        ems_syslog(LOG_NOTICE, "No directories found in %s\n", dir_path);  
        return -3;
    }  
    return 0;
}  
  

void check_ssd_space()
{
    double mem_used_percent = 0.0f;
    int max_deletion_attempts = 10; // 最大删除次数
    int attempts = 0;
    get_disk_usage(get_disk_path(),&mem_used_percent,NULL,NULL);
    ems_syslog(LOG_INFO,"check ssd percent:%f\n",mem_used_percent);
    while (mem_used_percent > USE_PERCENT && attempts < max_deletion_attempts)
    {
        // 删除最早的目录
        delete_oldest_directory(snapshoot_var_g->history_dir);

        // 再次获取磁盘使用率
        get_disk_usage(get_disk_path(), &mem_used_percent, NULL, NULL);
        ems_syslog(LOG_INFO, "After deletion, check ssd percent: %f\n", mem_used_percent);

        // 增加删除次数
        attempts++;
    }

    if (attempts >= max_deletion_attempts)
    {
        ems_syslog(LOG_ERR, "Reached maximum deletion attempts, disk still full.\n");
    }
}

int check_file(void)
{
    proto_forward_t *proto_var = get_proto_forward_var();
    int index = 0;
    char tmp_char[192] = {0},tmp_char2[256] = {0};
    time_t now = 0;
    FILE *file = NULL;
    struct tm target_time = { };  
    struct tm *local_time = &target_time;

    now = time(NULL);
    localtime_r(&now, local_time);
    ems_syslog(LOG_ERR, ">>>>>>>>>>>>>>> 进入系统\n");
    sprintf(tmp_char,"%s/%02d-%02d-%02d/", snapshoot_var_g->history_dir, local_time->tm_year+1900,local_time->tm_mon+1,local_time->tm_mday);

    if (checek_and_make_dir(tmp_char)<0)
    {
        return -1;
    }

    for (size_t i = 0; i < proto_var->channels_size; i++)
    {

        for (size_t j = 0; j < proto_var->channels[i]->devs_size; j++)
        {
            // 对snapshoot_dev_list的数组下标做保护
            if (index >= snapshoot_var_g->dev_num) {
                ems_syslog(LOG_ERR,"check_file: i=%zu, j = %zu, index:%d, dev_num:%d\n", i, j, index, snapshoot_var_g->dev_num);
                continue;
            }
            
            if (snapshoot_var_g->snapshoot_dev_list[index].file)
            {
                fflush(snapshoot_var_g->snapshoot_dev_list[index].file);
                fclose(snapshoot_var_g->snapshoot_dev_list[index].file);
                snapshoot_var_g->snapshoot_dev_list[index].file = NULL;
            }
            snapshoot_var_g->snapshoot_dev_list[index].dev = proto_var->channels[i]->devs[j];
            snprintf(tmp_char2, sizeof(tmp_char2), "%s%s.csv", tmp_char, proto_var->channels[i]->devs[j]->no);// 文件名

            ems_syslog(LOG_INFO,"tmp_char2:%d,%s__:%s %d\n",__LINE__,tmp_char2,snapshoot_var_g->snapshoot_dev_list[index].dev->no,access(tmp_char2, F_OK)); 
            if (access(tmp_char2, F_OK) == -1) {   //不存在创建
                file = fopen(tmp_char2, "a+");
                if (file == NULL) {
                    ems_syslog(LOG_ERR, "fopen fail: %s, errno:%d. \n", tmp_char2, errno);
                    continue;
                }
                StringBuffer *str_buff = StringBuffer_Create(1024);
                if (str_buff == NULL) {
                    ems_syslog(LOG_ERR, "str_buff fail \n");
                    fclose(file);
                    continue;
                }
                snapshoot_var_g->snapshoot_dev_list[index].file = file;
                fputc(0xEF, file);
                fputc(0xBB, file);
                fputc(0xBF, file);

                StringBuffer_Append(str_buff, "time");
                for (size_t k = 0; k < proto_var->channels[i]->devs[j]->tags.sys_info_tags_size; k++) 
                {
                    ems_syslog(LOG_INFO,"name:%s,disname%s\n",proto_var->channels[i]->devs[j]->tags.sys_info_tags[k]->name,proto_var->channels[i]->devs[j]->tags.sys_info_tags[k]->disp_name); 
                    StringBuffer_Append(str_buff, ",%s",proto_var->channels[i]->devs[j]->tags.sys_info_tags[k]->disp_name);
                }  
                for (size_t k = 0; k < proto_var->channels[i]->devs[j]->tags.ro_tags_size; k++)
                {
                    ems_syslog(LOG_INFO,"name:%s,disname%s\n",proto_var->channels[i]->devs[j]->tags.ro_tags[k]->name,proto_var->channels[i]->devs[j]->tags.ro_tags[k]->disp_name); 
                    if (proto_var->channels[i]->devs[j]->tags.ro_tags[k]->property == PROPERTY_ALARM || proto_var->channels[i]->devs[j]->tags.ro_tags[k]->no_snapshoot)
                        continue;
                    StringBuffer_Append(str_buff, ",%s",proto_var->channels[i]->devs[j]->tags.ro_tags[k]->disp_name);
                }

                fprintf(file, "%s", str_buff->buffer_str);
                fprintf(file,"\n");
                StringBuffer_Free(str_buff);
            }
            else
            {
                ems_syslog(LOG_INFO,"file exists\n"); 
                file = fopen(tmp_char2, "a+"); 
                if (file == NULL) {  
                    perror("openfile err :");
                    ems_syslog(LOG_ERR, "fopen fail: %s, errno:%d. \n", tmp_char2, errno);
                    continue; 
                }
                if (fseek(file, 0, SEEK_END) != 0) {  
                    perror("fseek");
                    ems_syslog(LOG_ERR, "fseek fail: %s, errno:%d. \n", tmp_char2, errno);
                    fclose(file);
                    continue;  
                }
                if (ftell(file) > 0)
                {
                    fprintf(file,"\n");
                }
                snapshoot_var_g->snapshoot_dev_list[index].file = file;
            }
            index++;
        }
    }

    snapshoot_var_g->dev_file_num = index;
    
    if (index == 0) {
        return -1;
    }
    return 0;
}
void *snapshoot_task(void *arg)
{
    time_t now = 0;
    snapshoot_param_s *snapshoot_param_t = (snapshoot_param_s *)arg;
    struct tm target_time = { };
    int cur_year = 0;
    struct tm *local_time = &target_time;
    time_t last_snapshoot = 0;
    proto_forward_t *proto_var = get_proto_forward_var();
    int dev_num = 0;
    pthread_detach(pthread_self());
     
    for (size_t i = 0; i < proto_var->channels_size; i++)
    {
        dev_num += proto_var->channels[i]->devs_size;
    }

    if (dev_num <= 0) {
        ems_syslog(LOG_ERR, "dev_num: %d \n", dev_num);
        return NULL;
    }

    snapshoot_var_g = calloc(1,sizeof(snapshoot_dev_list_s)*dev_num+sizeof(snapshoot_var_s));
    if (snapshoot_var_g == NULL) {
        ems_syslog(LOG_ERR, "snapshoot_var_g calloc fail \n");
        return NULL;
    }
    snapshoot_var_g->dev_num = dev_num;
    snprintf(snapshoot_var_g->history_dir,sizeof(snapshoot_var_g->history_dir),"%s/history/",get_disk_path());

    snapshoot_var_g->check_file_ok_state = true;
    if (checek_and_make_dir(snapshoot_var_g->history_dir)<0)
    {
        snapshoot_var_g->check_file_ok_state = false;
    }

    if(check_file()<0) {
        snapshoot_var_g->check_file_ok_state = false;
    }
 
    int changed = 0;
    sleep(20);
    snapshoot_var_g->state = SNAPSHOOT_BUSY;
    now = time(NULL);
    localtime_r(&now, local_time);
    cur_year = local_time->tm_year;
    while (1)
    {
        usleep(500*1000);
        now = time(NULL);
        localtime_r(&now, local_time);
        if (((local_time->tm_hour == 0)&&(local_time->tm_min <= 10)) || (cur_year != local_time->tm_year))
        {
            if (changed == 0)
            {
                changed = 1;
                snapshoot_var_g->check_file_ok_state = true;
                if(check_file() < 0) {
                    snapshoot_var_g->check_file_ok_state = false;
                }
                check_ssd_space();
            }
        }
        else
        {
            changed = 0;
        }
        cur_year = local_time->tm_year;

        if (snapshoot_var_g->check_file_ok_state != true) {
            continue;
        }

        if (snapshoot_var_g->sync_data)
        {
            for (size_t i = 0; i < snapshoot_var_g->dev_file_num; i++)
            {
                fflush(snapshoot_var_g->snapshoot_dev_list[i].file);
            }
            snapshoot_var_g->state = SNAPSHOOT_FREE;
            while (snapshoot_var_g->sync_data)
            {
                sleep(1);
            }
            snapshoot_var_g->state = SNAPSHOOT_BUSY;
        }else if ((now%snapshoot_param_t->snapshoot_interval == 0)&&check_timeout_second(now, &last_snapshoot, snapshoot_param_t->snapshoot_interval)) //时间对齐
        {
            for (size_t i = 0; i < snapshoot_var_g->dev_file_num; i++)
            {
                fprintf(snapshoot_var_g->snapshoot_dev_list[i].file,"%04d-%02d-%02d %02d:%02d:%02d",local_time->tm_year+1900,local_time->tm_mon+1,local_time->tm_mday,local_time->tm_hour,local_time->tm_min,local_time->tm_sec);  
                for (size_t j = 0; j < snapshoot_var_g->snapshoot_dev_list[i].dev->tags.sys_info_tags_size; j++)
                {
                    tag_t * tag = snapshoot_var_g->snapshoot_dev_list[i].dev->tags.sys_info_tags[j];
                    if (tag->data_type == TYPE_TAG_INT)
                    {
                        fprintf(snapshoot_var_g->snapshoot_dev_list[i].file,",%ld",tag->read_cache.to_int);
                    }
                    else
                    {
                        fprintf(snapshoot_var_g->snapshoot_dev_list[i].file,",%.2f",tag->read_cache.to_float);
                    }
                }
                for (size_t j = 0; j < snapshoot_var_g->snapshoot_dev_list[i].dev->tags.ro_tags_size; j++)
                {
                    tag_t * tag = snapshoot_var_g->snapshoot_dev_list[i].dev->tags.ro_tags[j];
                    if (tag->property == PROPERTY_ALARM || tag->no_snapshoot)
                            continue; 
                    if (tag->data_type == TYPE_TAG_INT)
                    {
                        fprintf(snapshoot_var_g->snapshoot_dev_list[i].file,",%ld",tag->read_cache.to_int);
                    }
                    else
                    {
                        fprintf(snapshoot_var_g->snapshoot_dev_list[i].file,",%.2f",tag->read_cache.to_float);
                    }
                }
                fprintf(snapshoot_var_g->snapshoot_dev_list[i].file,"\n");
            }
        }
    }
    
}

void snapshoot_init(snapshoot_param_s *snapshoot_param)
{
    pthread_t tid;
    if ((snapshoot_param->snapshoot_interval >0) && (is_mounted(get_disk_path()) == 0))
    {
        snapshoot_param_s *snapshoot_param_t = calloc(1,sizeof(snapshoot_param_s));
        memcpy(snapshoot_param_t,snapshoot_param,sizeof(snapshoot_param_s));
        if (0 == pthread_create(&tid,NULL,snapshoot_task,snapshoot_param_t))
        {
            pthread_setname_np(tid, "snapshoot_task");
        }
    }
    else
    {
        ems_syslog(LOG_ERR,"no ssd or snapshoot_interval is zero\n"); 
    }
}

const char *get_history_dir(void)
{
    return (snapshoot_var_g != NULL) ? snapshoot_var_g->history_dir : NULL;
}
