#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include "../mecha/mecha.h"
#include "../modhttp/http.h"

#include "shoutcast_metadata_writer.h"

struct s_ShoutcastMetadataWriter
{
    httpTtrans *trans;

    bool writeMetadata;

    int bytesUntilNext;

    int metadataInterval;

    bool haveData;

    dynTstring *metadataTitle;
    dynTstring *metadataUrl;
};


static bool shoutcast_metadata_writer_write_http (ShoutcastMetadataWriter *instance, const char *buf, size_t len);
static bool shoutcast_metadata_writer_write_metadata (ShoutcastMetadataWriter *instance);

ShoutcastMetadataWriter *
shoutcast_metadata_writer_new (httpTtrans *trans, bool writeMetadata, int metadataInterval)
{
    ShoutcastMetadataWriter *instance = (ShoutcastMetadataWriter *) malloc (sizeof (ShoutcastMetadataWriter));

    instance->trans = trans;

    instance->writeMetadata = writeMetadata;

    instance->bytesUntilNext = metadataInterval;
    instance->metadataInterval = metadataInterval;

    instance->haveData = false;

    instance->metadataTitle = dynFinit();
    instance->metadataUrl = dynFinit();

    return (instance);
}

void
shoutcast_metadata_writer_destroy (void *ptr)
{
    ShoutcastMetadataWriter *instance = (ShoutcastMetadataWriter *) ptr;

    dynFfree (instance->metadataTitle);
    dynFfree (instance->metadataUrl);

    free (instance);
}

void
shoutcast_metadata_writer_set_metadata (ShoutcastMetadataWriter *instance, const char *title, const char *url)
{
    dynFreset (instance->metadataTitle);
    dynFreset (instance->metadataUrl);

    if (title != NULL)
    {
        dynFsappend (instance->metadataTitle, title);
    }

    if (url != NULL)
    {
        dynFsappend (instance->metadataUrl, url);
    }

    instance->haveData = true;
}

bool
shoutcast_metadata_writer_write (ShoutcastMetadataWriter *instance, dynTstring *data)
{
    /* if we don't need to do anything special.. */
    if (instance->writeMetadata == false)
    {
        bool ret = shoutcast_metadata_writer_write_http (instance, dynFgetstr (data),
                dynFgetlen (data));

        return (ret);
    }
    else
    {
        bool ret;
        const char *dataPtr = dynFgetstr (data);
        int lenRemaining = dynFgetlen (data);

        while (lenRemaining > 0)
        {
            int writeLen;

            if (lenRemaining < instance->bytesUntilNext)
            {
                writeLen = lenRemaining;
            }
            else
            {
                writeLen = instance->bytesUntilNext;
            }

            ret = shoutcast_metadata_writer_write_http (instance, dataPtr, writeLen);

            if (ret == false)
            {
                return (false);
            }

            lenRemaining -= writeLen;
            dataPtr += writeLen;
            instance->bytesUntilNext -= writeLen;

            if (instance->bytesUntilNext == 0)
            {
                ret = shoutcast_metadata_writer_write_metadata (instance);

                if (ret == false)
                {
                    return (false);
                }

                instance->bytesUntilNext = instance->metadataInterval;
            }
        }

        return (true);
    }
}

static bool
shoutcast_metadata_writer_write_http (ShoutcastMetadataWriter *instance, const char *buf, size_t len)
{
    ssize_t ret = httpFwrite (instance->trans, buf, len);

    if (ret != (ssize_t) len)
    {           
        return (false);
    }   
    else
    {   
        return (true);
    }
}

static bool
shoutcast_metadata_writer_write_metadata (ShoutcastMetadataWriter *instance)
{
    if (instance->haveData == true)
    {
        char *metabuf;
        int padding, len;
        int bufsize = dynFgetlen (instance->metadataTitle) + dynFgetlen (instance->metadataUrl) + 32;
        bool ret;

        metabuf = (char *) malloc (bufsize);

        memset (metabuf, 0, bufsize);

        len = snprintf (metabuf + 1, bufsize - 1, "StreamTitle='%s';StreamUrl='%s';", 
                dynFgetstr (instance->metadataTitle), dynFgetstr (instance->metadataUrl));

        padding = 16 - (len % 16);

        len += padding;

        metabuf[0] = len / 16;

        len += 1;

        ret = shoutcast_metadata_writer_write_http (instance, metabuf, len);

        free (metabuf);

        instance->haveData = false;

        return (ret);
    }
    else
    {
        char emptyByte = 0;

        return (shoutcast_metadata_writer_write_http (instance, &emptyByte, 1));
    }
}
