/**********************************************************************************************/
/* The MIT License                                                                            */
/*                                                                                            */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */
/*                                                                                            */
/* Permission is hereby granted, free of charge, to any person obtaining a copy               */
/* of this software and associated documentation files (the "Software"), to deal              */
/* in the Software without restriction, including without limitation the rights               */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */
/* copies of the Software, and to permit persons to whom the Software is                      */
/* furnished to do so, subject to the following conditions:                                   */
/*                                                                                            */
/* The above copyright notice and this permission notice shall be included in                 */
/* all copies or substantial portions of the Software.                                        */
/*                                                                                            */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */
/* THE SOFTWARE.                                                                              */
/**********************************************************************************************/
#include "srt.h"
#include "utf8.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

srt_t* srt_new(const utf8_char_t* data, size_t size, double timestamp, srt_t* prev, srt_t** head)
{
    srt_t* srt = malloc(sizeof(srt_t) + size + 1);
    srt->next = 0;
    srt->duration = 0;
    srt->aloc = size;
    srt->timestamp = timestamp;
    utf8_char_t* dest = (utf8_char_t*)srt_data(srt);

    if (prev) {
        prev->next = srt;

        if (0 >= prev->duration) {
            // Don't modify the duration if it is already set
            prev->duration = timestamp - prev->timestamp;
        }
    }

    if (head && 0 == (*head)) {
        (*head) = srt;
    }

    if (data) {
        memcpy(dest, data, size);
    } else {
        memset(dest, 0, size);
    }

    dest[size] = '\0';
    return srt;
}

srt_t* srt_free_head(srt_t* head)
{
    srt_t* next = head->next;
    free(head);
    return next;
}

void srt_free(srt_t* srt)
{
    while (srt) {
        srt = srt_free_head(srt);
    }
}

#define SRTTIME2SECONDS(HH, MM, SS, MS) ((HH * 3600.0) + (MM * 60.0) + SS + (MS / 1000.0))
srt_t* srt_parse(const utf8_char_t* data, size_t size)
{
    srt_t *head = 0, *prev = 0;
    double str_pts = 0, end_pts = 0;
    size_t line_length = 0, trimmed_length = 0;
    int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2;

    if (!data || !size) {
        return 0;
    }

    for (;;) {
        line_length = 0;

        do {
            data += line_length;
            size -= line_length;
            line_length = utf8_line_length(data);
            trimmed_length = utf8_trimmed_length(data, line_length);
            // Skip empty lines
        } while (0 < line_length && 0 == trimmed_length);

        // linelength cant be zero before EOF
        if (0 == line_length) {
            break;
        }

        data += line_length;
        size -= line_length;

        line_length = utf8_line_length(data);
        // printf ("time (%d): '%.*s'\n", line_length, (int) line_length, data);

        {
            if (8 == sscanf(data, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d", &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2)) {
                str_pts = SRTTIME2SECONDS(hh1, mm1, ss1, ms1);
                end_pts = SRTTIME2SECONDS(hh2, mm2, ss2, ms2);
            }

            data += line_length;
            size -= line_length;
        }

        // Caption text starts here
        const utf8_char_t* text = data;
        size_t text_size = 0;

        line_length = 0;

        do {
            text_size += line_length;
            line_length = utf8_line_length(data);
            trimmed_length = utf8_trimmed_length(data, line_length);
            // printf ("cap (%d): '%.*s'\n", line_length, (int) trimmed_length, data);
            data += line_length;
            size -= line_length;
        } while (trimmed_length);

        // should we trim here?
        srt_t* srt = srt_new(text, text_size, str_pts, prev, &head);
        prev = srt;
        srt->duration = end_pts - str_pts;
    }

    return head;
}

int srt_to_caption_frame(srt_t* srt, caption_frame_t* frame)
{
    const char* data = srt_data(srt);
    return caption_frame_from_text(frame, data);
}

srt_t* srt_from_caption_frame(caption_frame_t* frame, srt_t* prev, srt_t** head)
{
    // CRLF per row, plus an extra at the end
    srt_t* srt = srt_new(NULL, 2 + CAPTION_FRAME_TEXT_BYTES, frame->timestamp, prev, head);
    utf8_char_t* data = srt_data(srt);

    caption_frame_to_text(frame, data);
    if (0 == utf8_trimmed_length(data, CAPTION_FRAME_TEXT_BYTES)) {
        // If the string is blank (possibly due to a eia608_control_erase_display_memory)
        // erase this cue and return the previous
        srt_free_head(srt);
        srt = prev;
        if (!prev) {
            (*head) = 0;
        } else {
            prev->next = 0;
        }
    }

    return srt;
}

static inline void _crack_time(double tt, int* hh, int* mm, int* ss, int* ms)
{
    (*ms) = (int)((int64_t)(tt * 1000) % 1000);
    (*ss) = (int)((int64_t)(tt) % 60);
    (*mm) = (int)((int64_t)(tt / (60)) % 60);
    (*hh) = (int)((int64_t)(tt / (60 * 60)));
}

static void _dump(srt_t* head, char type)
{
    int i;
    srt_t* srt;

    if ('v' == type) {
        printf("WEBVTT\r\n");
    }

    for (srt = head, i = 1; srt; srt = srt_next(srt), ++i) {
        int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2;
        _crack_time(srt->timestamp, &hh1, &mm1, &ss1, &ms1);
        _crack_time(srt->timestamp + srt->duration, &hh2, &mm2, &ss2, &ms2);

        if ('s' == type) {
            printf("%02d\r\n%d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\r\n%s\r\n\r\n", i,
                hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, srt_data(srt));
        }

        else if ('v' == type) {
            printf("%d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d\r\n%s\r\n\r\n",
                hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, srt_data(srt));
        }
    }
}

void srt_dump(srt_t* head) { _dump(head, 's'); }
void vtt_dump(srt_t* head) { _dump(head, 'v'); }
