#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define INTERVAL_SEC 10
#define LOG_FILE "log"

static unsigned int compute_hash(const char *data, size_t len)
{
    unsigned int hash =0;
    const unsigned char *p = (const unsigned char *)data;
    size_t i,n = len /4;

    for ( i = 0; i < n; i++)
    {
        unsigned int val = (unsigned int)p[i * 4] | ((unsigned int)p[i * 4 + 1] << 8) 
                         | ((unsigned int)p[i * 4 + 2] << 16) | ((unsigned int)p[i * 4 + 3] << 24);
        hash += val;    
    }
    
    size_t rem = len % 4,base = n*4;
    for ( i = 0; i < rem; i++)
    {
        hash += (unsigned int)p[base + i];
    }
    return hash;
}


static char *read_file(const char *path,size_t *out_len)
{
    int fd = open(path,O_RDONLY);
    if (fd < 0)
    {
        return NULL;
    }
    
    off_t size = lseek(fd,0,SEEK_END);
    if (size < 0)
    {
        close(fd);
        return NULL;
    }
    lseek(fd,0,SEEK_SET);

    char *buf = malloc((size_t)size + 1);
    if (!buf)
    {
        close(fd);
        return NULL;
    }
    
    ssize_t n = read(fd,buf,(size_t)size);
    if (n < 0)
    {
        free(buf);
        close(fd);
        return NULL;
    }

    buf[n] = '\0';
    *out_len = (size_t)n;
    close(fd);
    return buf;   
}

static void write_log(unsigned int hash)
{
    FILE *fp = fopen(LOG_FILE,"a");
    if (!fp)
    {
        return;
    }
    
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    char time_str[20];
    strftime(time_str,sizeof(time_str),"%Y-%m-%d %H:%M:%S",tm_info);

    fprintf(fp,"%s  hash=%u\n",time_str,hash);
    fclose(fp);
}

static void daemonize(void)
{
    pid_t pid = fork();
    if (pid < 0)
    {
        exit(1);
    }
    if (pid > 0)
    {
        exit(0);
    }

    setsid();
    umask(0);

    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
}


int main(int argc,char *argv[])
{
    //自检模式
    if (argc > 1 && strcmp(argv[1],"test") == 0)
    {
        /* 测试1: 不同内容应产生不同hash值 */
        printf("自检模式\n");

        const char *test_str = "Hello, World!";
        unsigned int h1 = compute_hash(test_str,strlen(test_str));
        
        const char *test_str2 = "Hello, world!"; // 注意 'W' 和 'w'
        unsigned int h2 = compute_hash(test_str2,strlen(test_str2));

        printf("测试1: hash1=%u, hash2=%u\n",h1,h2);
        if (h1 != h2)
            printf("通过: 不同内容 -> 不同hash值\n");
        else
            printf("失败: 不同内容应产生不同的hash值\n");

        
            /* 测试2: 相同内容应产生相同hash值 */
        unsigned int h3 = compute_hash(test_str,strlen(test_str));
        printf("测试2: hash3=%u\n",h3);
        if (h1 == h3)
            printf("通过: 相同内容 -> 相同hash值\n");
        else
            printf("失败: 相同内容应产生相同的hash值\n");

        /* 测试3: 读取自身并计算hash */
        size_t len;
        char *self = read_file("task53.c",&len);
        if (self)
        {
            unsigned int hself = compute_hash(self,len);
            printf("测试3: 自身hash=%u, 文件大小=%zu字节\n",hself,len);
            free(self);
        }
        return 0;
    }

    /*
     * 守护进程模式
     * -f 表示前台运行(不调用daemonize), 便于测试
     * 第二个参数可指定检查间隔秒数
     */
    int foreground = (argc > 1 && strcmp(argv[1],"-f") == 0);
    int interval = INTERVAL_SEC;
    if (argc > 2) interval = atoi(argv[2]);
    if (interval <= 0) interval = INTERVAL_SEC;

    if (!foreground) 
    {
        daemonize();
    }

    size_t len;
    char *prev_data = read_file("test.log",&len);
    if (!prev_data) return 1;
    unsigned int prev_hash = compute_hash(prev_data,len);
    
    while (1)
    {
        sleep(interval);

        size_t cur_len;
        char *cur_data = read_file("test.log",&cur_len);
        if (!cur_data)
        {
            continue;
        }

        unsigned int cur_hash = compute_hash(cur_data,cur_len);
        if (cur_hash != prev_hash)
        {
            write_log(cur_hash);
            free(prev_data);
            prev_data = cur_data;
            prev_hash = cur_hash;
        }
        else
        {
            free(cur_data);
        }
    }
    free(prev_data);
    return 0;
}
