/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * News ticker implementation
 */

#include "news.h"
#include "panel.h"
#include "config.h"
#include "util.h"
#include "cJSON.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdatomic.h>
#include <curl/curl.h>
#include <X11/Xft/Xft.h>

#define SCROLL_SPEED_PPS 80

#define FETCH_INTERVAL 300000

#define SENTIMENT_COLOR_POSITIVE 0x81C784
#define SENTIMENT_COLOR_NEUTRAL  0xB0BEC5
#define SENTIMENT_COLOR_NEGATIVE 0xE57373

static unsigned long news_sentiment_color(NewsSentiment sentiment)
{
    switch (sentiment) {
        case SENTIMENT_POSITIVE: return SENTIMENT_COLOR_POSITIVE;
        case SENTIMENT_NEGATIVE: return SENTIMENT_COLOR_NEGATIVE;
        default:                 return SENTIMENT_COLOR_NEUTRAL;
    }
}

NewsState news_state = {0};

static pthread_mutex_t news_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t fetch_thread;
static atomic_int fetch_running = 0;

typedef struct {
    char *data;
    size_t size;
} CurlBuffer;

static size_t news_curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t realsize = size * nmemb;
    CurlBuffer *buf = (CurlBuffer *)userp;

    char *ptr = dwn_realloc(buf->data, buf->size + realsize + 1);
    if (ptr == NULL) {
        return 0;
    }

    buf->data = ptr;
    memcpy(&(buf->data[buf->size]), contents, realsize);
    buf->size += realsize;
    buf->data[buf->size] = '\0';

    return realsize;
}

static int decode_numeric_entity(const char *src, char *out, size_t max_out)
{
    if (src[0] != '&' || src[1] != '#') return 0;

    const char *p = src + 2;
    int base = 10;
    if (*p == 'x' || *p == 'X') {
        base = 16;
        p++;
    }

    char *end;
    long code = strtol(p, &end, base);
    if (*end != ';' || code <= 0) return 0;

    int consumed = (end - src) + 1;

    if (code < 128 && max_out >= 1) {
        out[0] = (char)code;
        return consumed;
    } else if (code < 0x800 && max_out >= 2) {
        out[0] = 0xC0 | (code >> 6);
        out[1] = 0x80 | (code & 0x3F);
        return consumed;
    } else if (code < 0x10000 && max_out >= 3) {
        out[0] = 0xE0 | (code >> 12);
        out[1] = 0x80 | ((code >> 6) & 0x3F);
        out[2] = 0x80 | (code & 0x3F);
        return consumed;
    }
    return 0;
}

static void strip_html(char *dst, const char *src, size_t max_len)
{
    size_t j = 0;
    bool in_tag = false;

    for (size_t i = 0; src[i] && j < max_len - 1; i++) {
        if (src[i] == '<') {
            in_tag = true;
        } else if (src[i] == '>') {
            in_tag = false;
        } else if (!in_tag) {
            if (src[i] == '&') {
                if (strncmp(&src[i], "&amp;", 5) == 0) {
                    dst[j++] = '&'; i += 4;
                } else if (strncmp(&src[i], "&lt;", 4) == 0) {
                    dst[j++] = '<'; i += 3;
                } else if (strncmp(&src[i], "&gt;", 4) == 0) {
                    dst[j++] = '>'; i += 3;
                } else if (strncmp(&src[i], "&quot;", 6) == 0) {
                    dst[j++] = '"'; i += 5;
                } else if (strncmp(&src[i], "&apos;", 6) == 0) {
                    dst[j++] = '\''; i += 5;
                } else if (strncmp(&src[i], "&nbsp;", 6) == 0) {
                    dst[j++] = ' '; i += 5;
                } else if (strncmp(&src[i], "&ndash;", 7) == 0) {
                    dst[j++] = '-'; i += 6;
                } else if (strncmp(&src[i], "&mdash;", 7) == 0) {
                    dst[j++] = '-'; dst[j++] = '-'; i += 6;
                } else if (strncmp(&src[i], "&hellip;", 8) == 0) {
                    dst[j++] = '.'; dst[j++] = '.'; dst[j++] = '.'; i += 7;
                } else if (strncmp(&src[i], "&lsquo;", 7) == 0 || strncmp(&src[i], "&rsquo;", 7) == 0) {
                    dst[j++] = '\''; i += 6;
                } else if (strncmp(&src[i], "&ldquo;", 7) == 0 || strncmp(&src[i], "&rdquo;", 7) == 0) {
                    dst[j++] = '"'; i += 6;
                } else if (src[i+1] == '#') {
                    char decoded[4] = {0};
                    int consumed = decode_numeric_entity(&src[i], decoded, sizeof(decoded));
                    if (consumed > 0) {
                        for (int k = 0; decoded[k] && j < max_len - 1; k++) {
                            dst[j++] = decoded[k];
                        }
                        i += consumed - 1;
                    } else {
                        dst[j++] = src[i];
                    }
                } else {
                    dst[j++] = src[i];
                }
            } else if (src[i] == '\n' || src[i] == '\r' || src[i] == '\t') {
                dst[j++] = ' ';
            } else {
                dst[j++] = src[i];
            }
        }
    }
    dst[j] = '\0';

    char *read = dst, *write = dst;
    bool last_space = false;
    while (*read) {
        if (*read == ' ') {
            if (!last_space) {
                *write++ = *read;
            }
            last_space = true;
        } else {
            *write++ = *read;
            last_space = false;
        }
        read++;
    }
    *write = '\0';
}

static int parse_news_json(const char *json_str)
{
    cJSON *root = cJSON_Parse(json_str);
    if (root == NULL) {
        LOG_WARN("Failed to parse news JSON");
        return -1;
    }

    cJSON *articles = cJSON_GetObjectItemCaseSensitive(root, "articles");
    if (!cJSON_IsArray(articles)) {
        LOG_WARN("No articles array in news JSON");
        cJSON_Delete(root);
        return -1;
    }

    int count = 0;
    cJSON *article;
    cJSON_ArrayForEach(article, articles) {
        if (count >= MAX_NEWS_ARTICLES) break;

        cJSON *title = cJSON_GetObjectItemCaseSensitive(article, "title");
        cJSON *content = cJSON_GetObjectItemCaseSensitive(article, "content");
        cJSON *description = cJSON_GetObjectItemCaseSensitive(article, "description");
        cJSON *link = cJSON_GetObjectItemCaseSensitive(article, "link");
        cJSON *author = cJSON_GetObjectItemCaseSensitive(article, "author");

        NewsArticle *art = &news_state.articles[count];
        memset(art, 0, sizeof(NewsArticle));

        if (cJSON_IsString(title) && title->valuestring) {
            strip_html(art->title, title->valuestring, sizeof(art->title));
        }

        const char *text = NULL;
        if (cJSON_IsString(content) && content->valuestring && strlen(content->valuestring) > 0) {
            text = content->valuestring;
        } else if (cJSON_IsString(description) && description->valuestring) {
            text = description->valuestring;
        }

        if (text) {
            strip_html(art->content, text, sizeof(art->content));
        } else {
            strncpy(art->content, art->title, sizeof(art->content) - 1);
        }

        if (cJSON_IsString(link) && link->valuestring) {
            strncpy(art->link, link->valuestring, sizeof(art->link) - 1);
        }

        if (cJSON_IsString(author) && author->valuestring) {
            strncpy(art->author, author->valuestring, sizeof(art->author) - 1);
        }

        cJSON *sentiment_obj = cJSON_GetObjectItemCaseSensitive(article, "sentiment");
        if (cJSON_IsObject(sentiment_obj)) {
            cJSON *sentiment_type = cJSON_GetObjectItemCaseSensitive(sentiment_obj, "sentiment");
            cJSON *sentiment_score = cJSON_GetObjectItemCaseSensitive(sentiment_obj, "score");

            if (cJSON_IsString(sentiment_type) && sentiment_type->valuestring) {
                if (strcasecmp(sentiment_type->valuestring, "Positive") == 0) {
                    art->sentiment = SENTIMENT_POSITIVE;
                } else if (strcasecmp(sentiment_type->valuestring, "Negative") == 0) {
                    art->sentiment = SENTIMENT_NEGATIVE;
                } else {
                    art->sentiment = SENTIMENT_NEUTRAL;
                }
            }

            if (cJSON_IsNumber(sentiment_score)) {
                art->sentiment_score = (float)sentiment_score->valuedouble;
            }

        }

        count++;
    }

    cJSON_Delete(root);
    return count;
}

#define NEWS_SEPARATOR "  •  "
#define NEWS_SEPARATOR_LEN 5

static int news_find_article_at_x(int click_x);

static XftDraw *news_xft_draw = NULL;
static Drawable news_xft_drawable = None;
static Visual *news_cached_visual = NULL;

#define NEWS_XFT_COLOR_CACHE_SIZE 8

typedef struct {
    unsigned long pixel;
    XftColor xft_color;
    bool valid;
    int lru_counter;
} NewsCachedColor;

static NewsCachedColor news_color_cache[NEWS_XFT_COLOR_CACHE_SIZE];
static int news_color_lru_counter = 0;

static int cached_separator_width = -1;

static XftDraw *news_get_xft_draw(Drawable d)
{
    if (news_xft_drawable == d && news_xft_draw != NULL) {
        return news_xft_draw;
    }
    if (news_xft_draw != NULL) {
        XftDrawDestroy(news_xft_draw);
    }
    if (news_cached_visual == NULL) {
        news_cached_visual = DefaultVisual(dwn->display, dwn->screen);
    }
    news_xft_draw = XftDrawCreate(dwn->display, d, news_cached_visual, dwn->colormap);
    news_xft_drawable = d;
    return news_xft_draw;
}

static XftColor *news_get_cached_color(unsigned long color)
{
    if (news_cached_visual == NULL) {
        news_cached_visual = DefaultVisual(dwn->display, dwn->screen);
    }

    for (int i = 0; i < NEWS_XFT_COLOR_CACHE_SIZE; i++) {
        if (news_color_cache[i].valid && news_color_cache[i].pixel == color) {
            news_color_cache[i].lru_counter = ++news_color_lru_counter;
            return &news_color_cache[i].xft_color;
        }
    }

    int oldest_idx = 0;
    int oldest_lru = news_color_cache[0].lru_counter;
    for (int i = 1; i < NEWS_XFT_COLOR_CACHE_SIZE; i++) {
        if (!news_color_cache[i].valid) {
            oldest_idx = i;
            break;
        }
        if (news_color_cache[i].lru_counter < oldest_lru) {
            oldest_lru = news_color_cache[i].lru_counter;
            oldest_idx = i;
        }
    }

    if (news_color_cache[oldest_idx].valid) {
        XftColorFree(dwn->display, news_cached_visual, dwn->colormap,
                     &news_color_cache[oldest_idx].xft_color);
    }

    XRenderColor render_color;
    render_color.red = ((color >> 16) & 0xFF) * 257;
    render_color.green = ((color >> 8) & 0xFF) * 257;
    render_color.blue = (color & 0xFF) * 257;
    render_color.alpha = 0xFFFF;

    XftColorAllocValue(dwn->display, news_cached_visual, dwn->colormap,
                       &render_color, &news_color_cache[oldest_idx].xft_color);
    news_color_cache[oldest_idx].pixel = color;
    news_color_cache[oldest_idx].valid = true;
    news_color_cache[oldest_idx].lru_counter = ++news_color_lru_counter;

    return &news_color_cache[oldest_idx].xft_color;
}

static int news_text_width(const char *text)
{
    if (dwn == NULL || text == NULL) return 0;

    if (dwn->xft_font != NULL) {
        XGlyphInfo extents;
        XftTextExtentsUtf8(dwn->display, dwn->xft_font,
                           (const FcChar8 *)text, strlen(text), &extents);
        return extents.xOff;
    }

    if (dwn->font != NULL) {
        return XTextWidth(dwn->font, text, strlen(text));
    }

    return strlen(text) * 8;
}

static void news_draw_text_clipped(Drawable d, int x, int y, const char *text,
                                    unsigned long color, XRectangle *clip)
{
    if (dwn == NULL || dwn->display == NULL || text == NULL) return;

    if (dwn->xft_font != NULL) {
        XftDraw *xft_draw = news_get_xft_draw(d);
        if (xft_draw != NULL) {
            if (clip != NULL) {
                Region region = XCreateRegion();
                XUnionRectWithRegion(clip, region, region);
                XftDrawSetClip(xft_draw, region);
                XDestroyRegion(region);
            }

            XftColor *xft_color = news_get_cached_color(color);
            XftDrawStringUtf8(xft_draw, xft_color, dwn->xft_font,
                              x, y, (const FcChar8 *)text, strlen(text));

            if (clip != NULL) {
                XftDrawSetClip(xft_draw, None);
            }
            return;
        }
    }

    if (clip != NULL) {
        XSetClipRectangles(dwn->display, dwn->gc, 0, 0, clip, 1, Unsorted);
    }
    XSetForeground(dwn->display, dwn->gc, color);
    XDrawString(dwn->display, d, dwn->gc, x, y, text, strlen(text));
    if (clip != NULL) {
        XSetClipMask(dwn->display, dwn->gc, None);
    }
}

static void get_article_display_text(int index, char *buf, size_t buf_size)
{
    if (index < 0 || index >= news_state.article_count) {
        buf[0] = '\0';
        return;
    }
    NewsArticle *art = &news_state.articles[index];
    if (art->content[0] && strcmp(art->content, art->title) != 0) {
        snprintf(buf, buf_size, "%s: %s", art->title, art->content);
    } else {
        snprintf(buf, buf_size, "%s", art->title);
    }
}

static void news_recalc_widths(void)
{
    if (dwn == NULL) {
        return;
    }

    if (cached_separator_width < 0) {
        cached_separator_width = news_text_width(NEWS_SEPARATOR);
    }
    int separator_width = cached_separator_width;
    news_state.total_width = 0;

    for (int i = 0; i < news_state.article_count; i++) {
        char display_text[2048];
        get_article_display_text(i, display_text, sizeof(display_text));
        news_state.display_widths[i] = news_text_width(display_text);
        news_state.total_width += news_state.display_widths[i] + separator_width;
    }
}

static void *news_fetch_thread(void *arg)
{
    (void)arg;

    CURL *curl = curl_easy_init();
    if (curl == NULL) {
        pthread_mutex_lock(&news_mutex);
        news_state.fetching = false;
        news_state.has_error = true;
        pthread_mutex_unlock(&news_mutex);
        return NULL;
    }

    CurlBuffer buf = {0};
    buf.data = dwn_malloc(1);
    if (buf.data == NULL) {
        curl_easy_cleanup(curl);
        pthread_mutex_lock(&news_mutex);
        news_state.fetching = false;
        news_state.has_error = true;
        pthread_mutex_unlock(&news_mutex);
        return NULL;
    }
    buf.data[0] = '\0';

    curl_easy_setopt(curl, CURLOPT_URL, NEWS_API_URL);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, news_curl_write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "DWN/1.0");

    CURLcode res = curl_easy_perform(curl);

    pthread_mutex_lock(&news_mutex);
    if (res == CURLE_OK && buf.data && buf.size > 0) {
        int count = parse_news_json(buf.data);
        if (count > 0) {
            news_state.article_count = count;
            news_state.has_error = false;
            news_state.current_article = 0;
            news_state.scroll_offset = 0.0;
            news_state.last_scroll_update = 0;
            news_state.widths_dirty = true;
            LOG_INFO("Fetched %d news articles", count);
        } else {
            news_state.has_error = true;
        }
    } else {
        news_state.has_error = true;
        LOG_DEBUG("News fetch failed: %s", curl_easy_strerror(res));
    }
    news_state.fetching = false;
    news_state.last_fetch = get_time_ms();
    pthread_mutex_unlock(&news_mutex);

    free(buf.data);
    curl_easy_cleanup(curl);

    return NULL;
}


void news_init(void)
{
    memset(&news_state, 0, sizeof(news_state));
    news_state.last_fetch = 0;

    news_fetch_async();

    LOG_INFO("News ticker initialized");
}

void news_cleanup(void)
{
    if (atomic_load(&fetch_running)) {
        pthread_join(fetch_thread, NULL);
        atomic_store(&fetch_running, 0);
    }
}

void news_fetch_async(void)
{
    pthread_mutex_lock(&news_mutex);
    if (news_state.fetching) {
        pthread_mutex_unlock(&news_mutex);
        return;
    }
    news_state.fetching = true;
    pthread_mutex_unlock(&news_mutex);

    if (atomic_load(&fetch_running)) {
        pthread_join(fetch_thread, NULL);
    }

    atomic_store(&fetch_running, 1);
    if (pthread_create(&fetch_thread, NULL, news_fetch_thread, NULL) != 0) {
        pthread_mutex_lock(&news_mutex);
        news_state.fetching = false;
        news_state.has_error = true;
        pthread_mutex_unlock(&news_mutex);
        atomic_store(&fetch_running, 0);
    }
}

void news_update(void)
{
    long now = get_time_ms();

    pthread_mutex_lock(&news_mutex);

    if (!news_state.fetching && (now - news_state.last_fetch) >= FETCH_INTERVAL) {
        pthread_mutex_unlock(&news_mutex);
        news_fetch_async();
        pthread_mutex_lock(&news_mutex);
    }

    bool scrolled = false;
    if (!news_state.interactive_mode && news_state.article_count > 0) {
        if (news_state.last_scroll_update > 0) {
            long delta_ms = now - news_state.last_scroll_update;
            double scroll_amount = (SCROLL_SPEED_PPS * delta_ms) / 1000.0;
            news_state.scroll_offset += scroll_amount;
            scrolled = true;
        }
        news_state.last_scroll_update = now;
    }

    pthread_mutex_unlock(&news_mutex);

    if (scrolled) {
        panel_invalidate_news();
    }
}

void news_next_article(void)
{
    pthread_mutex_lock(&news_mutex);
    if (news_state.article_count > 0) {
        news_state.current_article = (news_state.current_article + 1) % news_state.article_count;
        news_state.scroll_offset = 0.0;
        news_state.last_scroll_update = 0;
        news_state.interactive_mode = true;
    }
    pthread_mutex_unlock(&news_mutex);
}

void news_prev_article(void)
{
    pthread_mutex_lock(&news_mutex);
    if (news_state.article_count > 0) {
        news_state.current_article--;
        if (news_state.current_article < 0) {
            news_state.current_article = news_state.article_count - 1;
        }
        news_state.scroll_offset = 0.0;
        news_state.last_scroll_update = 0;
        news_state.interactive_mode = true;
    }
    pthread_mutex_unlock(&news_mutex);
}

void news_open_current(void)
{
    pthread_mutex_lock(&news_mutex);
    if (news_state.article_count > 0 && news_state.total_width > 0) {
        int article_index = news_find_article_at_x(news_state.render_x);
        if (article_index < 0) article_index = 0;

        char url[512];
        strncpy(url, news_state.articles[article_index].link, sizeof(url) - 1);
        url[sizeof(url) - 1] = '\0';
        pthread_mutex_unlock(&news_mutex);

        pid_t pid = fork();
        if (pid == 0) {
            setsid();
            pid_t pid2 = fork();
            if (pid2 == 0) {
                execlp("xdg-open", "xdg-open", url, NULL);
                _exit(1);
            }
            _exit(0);
        } else if (pid > 0) {
            int status;
            waitpid(pid, &status, 0);
        }
    } else {
        pthread_mutex_unlock(&news_mutex);
    }
}

static int news_find_article_at_x(int click_x)
{
    if (news_state.article_count == 0 || news_state.total_width == 0) {
        return -1;
    }

    if (cached_separator_width < 0) {
        cached_separator_width = news_text_width(NEWS_SEPARATOR);
    }
    int separator_width = cached_separator_width;
    int scroll_offset = (int)news_state.scroll_offset % news_state.total_width;
    int rel_x = click_x - news_state.render_x;

    int content_x = scroll_offset + rel_x;
    if (content_x < 0) {
        content_x += news_state.total_width;
    }
    content_x = content_x % news_state.total_width;

    int pos = 0;
    for (int i = 0; i < news_state.article_count; i++) {
        int article_total = news_state.display_widths[i] + separator_width;
        if (content_x < pos + article_total) {
            return i;
        }
        pos += article_total;
    }
    return 0;
}

void news_render(Panel *panel, int x, int max_width, int *used_width)
{
    if (panel == NULL || used_width == NULL || dwn == NULL) {
        *used_width = 0;
        return;
    }

    if (dwn->xft_font == NULL && dwn->font == NULL) {
        *used_width = 0;
        return;
    }

    int text_y;
    if (dwn->xft_font != NULL) {
        text_y = (panel->height + dwn->xft_font->ascent - dwn->xft_font->descent) / 2;
    } else {
        text_y = (panel->height + dwn->font->ascent - dwn->font->descent) / 2;
    }

    const ColorScheme *colors = config_get_colors();

    pthread_mutex_lock(&news_mutex);

    if (news_state.article_count == 0) {
        pthread_mutex_unlock(&news_mutex);
        *used_width = 0;
        return;
    }

    if (news_state.widths_dirty) {
        news_recalc_widths();
        news_state.widths_dirty = false;
    }

    if (news_state.total_width == 0) {
        pthread_mutex_unlock(&news_mutex);
        *used_width = 0;
        return;
    }

    news_state.render_x = x;
    news_state.render_width = max_width;

    if (cached_separator_width < 0) {
        cached_separator_width = news_text_width(NEWS_SEPARATOR);
    }
    int separator_width = cached_separator_width;
    int scroll_offset = (int)news_state.scroll_offset % news_state.total_width;

    XRectangle clip_rect = { x, 0, max_width, panel->height };

    int draw_x = x - scroll_offset;
    int articles_drawn = 0;
    int start_article = 0;

    int pos = 0;
    for (int i = 0; i < news_state.article_count; i++) {
        int article_total = news_state.display_widths[i] + separator_width;
        if (pos + article_total > scroll_offset) {
            start_article = i;
            draw_x = x + (pos - scroll_offset);
            break;
        }
        pos += article_total;
    }

    int current_article = start_article;
    while (draw_x < x + max_width && articles_drawn < news_state.article_count * 2) {
        char display_text[2048];
        get_article_display_text(current_article, display_text, sizeof(display_text));

        unsigned long article_color = news_sentiment_color(
            news_state.articles[current_article].sentiment);
        article_color = ambient_glow_bg(article_color, dwn->ambient_phase + PHASE_OFFSET_NEWS);

        if (draw_x + news_state.display_widths[current_article] > x) {
            news_draw_text_clipped(panel->buffer, draw_x, text_y, display_text,
                                   article_color, &clip_rect);
        }
        draw_x += news_state.display_widths[current_article];

        if (draw_x < x + max_width) {
            news_draw_text_clipped(panel->buffer, draw_x, text_y, NEWS_SEPARATOR,
                                   ambient_glow_bg(colors->panel_fg, dwn->ambient_phase + PHASE_OFFSET_NEWS), &clip_rect);
        }
        draw_x += separator_width;

        current_article = (current_article + 1) % news_state.article_count;
        articles_drawn++;
    }

    pthread_mutex_unlock(&news_mutex);

    *used_width = max_width;
}

void news_handle_click(int x, int y)
{
    (void)y;

    pthread_mutex_lock(&news_mutex);
    if (news_state.article_count == 0) {
        pthread_mutex_unlock(&news_mutex);
        return;
    }

    int article_index = news_find_article_at_x(x);
    if (article_index >= 0 && article_index < news_state.article_count) {
        char url[512];
        strncpy(url, news_state.articles[article_index].link, sizeof(url) - 1);
        url[sizeof(url) - 1] = '\0';
        pthread_mutex_unlock(&news_mutex);

        pid_t pid = fork();
        if (pid == 0) {
            setsid();
            pid_t pid2 = fork();
            if (pid2 == 0) {
                execlp("xdg-open", "xdg-open", url, NULL);
                _exit(1);
            }
            _exit(0);
        } else if (pid > 0) {
            int status;
            waitpid(pid, &status, 0);
        }
    } else {
        pthread_mutex_unlock(&news_mutex);
    }
}

void news_lock(void)
{
    pthread_mutex_lock(&news_mutex);
}

void news_unlock(void)
{
    pthread_mutex_unlock(&news_mutex);
}
