
#include <stdio.h>

#include "mp3_reader.h"

#include "music_packet.h"
#include "idata_reader.h"
#include "file_reader.h"

#include "mp3_frame.h"

struct s_Mp3Reader
{
    IDataReader *reader;
};

Mp3Reader *
mp3_reader_new (void)
{
    Mp3Reader *instance = (Mp3Reader *) malloc (sizeof (Mp3Reader));

    instance->reader = NULL;

    return (instance);
}

void
mp3_reader_destroy (void *ptr)
{
    Mp3Reader *instance = (Mp3Reader *) ptr;

    if (instance->reader != NULL)
    {
        instance->reader->destroy (instance->reader);
    }

    free (instance);
}

bool
mp3_reader_open (Mp3Reader *instance, const char *uri)
{
    /* this should be using DataReaderFactory -- if such a thing existed */
    instance->reader = (IDataReader *) file_reader_new();

    if (instance->reader->open (instance->reader, uri) == false)
    {
        return (false);
    }
    else
    {
        return (true);
    }
}

static bool
mp3_reader_parse_frame_header (MECHA_UNUSED_ARGUMENT Mp3Reader *instance, char *frameHeader, struct mp3frame *frameToFill)
{
    frameToFill->version = ((frameHeader[1] & 0x18) >> 3);

    if (frameToFill->version == 1)
    {
        logFmsg (CONT_LOG_ERROR, "found reserved version set");

        return (false);
    }

    if (frameToFill->version < 0 || frameToFill->version > 3)
    {
        logFmsg (CONT_LOG_ERROR, "unknown version %x", frameToFill->version);

        return (false);
    }

    if (frameToFill->version == 3)
    {
        frameToFill->version = 1;
    }
    else if (frameToFill->version == 0)
    {
        frameToFill->version = 3;
    }

    frameToFill->layer = ((frameHeader[1] & 0x06) >> 1);

    if (frameToFill->layer == 0)
    {
        logFmsg (CONT_LOG_ERROR, "found reserved layer set");

        return (false);
    }
    if (frameToFill->layer < 1 || frameToFill->layer > 3)
    {
        logFmsg (CONT_LOG_ERROR, "unknown layer %x", frameToFill->layer);

        return (false);
    }

    if (frameToFill->layer == 1)
    {
        frameToFill->layer = 3;
    }
    else if (frameToFill->layer == 3)
    {
        frameToFill->layer = 1;
    }

    frameToFill->protection = frameHeader[1] & 0x01;

    frameToFill->bitrate = bitrate_index[frameToFill->version][frameToFill->layer][(frameHeader[2] & 0xf0) >> 4];

    if (frameToFill->bitrate == -1)
    {
        logFmsg (CONT_LOG_ERROR, "don't know how to handle free format frames");

        return (false);
    }
    else if (frameToFill->bitrate == -2)
    {
        logFmsg (CONT_LOG_ERROR, "found a frame with bad bitrate");

        return (false);
    }
    else
    {
        frameToFill->bitrate *= 1000;
    }

    frameToFill->frequency = samplerate_index[frameToFill->version][(frameHeader[2] & 0x0f) >> 2];

    if (frameToFill->frequency == -1)
    {
        logFmsg (CONT_LOG_ERROR, "frequency was set to a reserved value");

        return (false);
    }

    frameToFill->padding = (frameHeader[2] & 0x02) >> 1;

    if (frameToFill->version == 1)
    {
        switch (frameToFill->layer)
        {
            case 1:
                frameToFill->frame_byte_length = (384 * frameToFill->bitrate / 8 / frameToFill->frequency + frameToFill->padding) * 4;
                frameToFill->frame_usec_length = 1000000L * 384 / frameToFill->frequency;
                break;
            case 2:
            case 3:
                frameToFill->frame_byte_length = 1152 * frameToFill->bitrate / 8 / frameToFill->frequency + frameToFill->padding;
                frameToFill->frame_usec_length = 1000000L * 1152 / frameToFill->frequency;
                break;
        }
    }
    else
    {
        /* if we're not mpeg version 1 -- we don't know what to do */
    }

    frameToFill->private = frameHeader[2] & 0x01;
    frameToFill->channel_mode = frameHeader[3] & 0xc0 >> 6;
    frameToFill->channel_extension = frameHeader[3] & 0x30 >> 4;
    frameToFill->copyright = frameHeader[3] & 0x08 >> 3;
    frameToFill->original = frameHeader[3] & 0x04 >> 2;
    frameToFill->emphasis = frameHeader[3] & 0x03;

    return (true);
}

static int
mp3_reader_validate_sync_frame (const char *ptr)
{
    if ((ptr[0] & 0xff) == 0xff && (ptr[1] & 0xe0) == 0xe0)
    {
        return (0);
    }
    else
    {
        return (1);
    }
}

static bool
mp3_reader_get_next_frame (Mp3Reader *instance, struct mp3frame *frameToFill)
{
    ssize_t skippedBytes = 0;
    char frameHeader[4];

    while (true)
    {
        int thisSkippedBytes = instance->reader->seek_to_data_func (instance->reader, 2, mp3_reader_validate_sync_frame);

        if (thisSkippedBytes == -1)
        {
            /* we found the end of the file before we found the next sync frame */

            return (false);
        }

        skippedBytes += thisSkippedBytes;

        if (instance->reader->peek_bytes (instance->reader, frameHeader, sizeof (frameHeader)) == false)
        {
            /* 
             * we couldn't get all three bytes we needed.. we obviously got the
             * end of the file early.
             */

            logFmsg (CONT_LOG_DEBUG, "mp3_reader: Reached the end of the file after finding the sync frame.");

            return (false);
        }

        if (mp3_reader_parse_frame_header (instance, frameHeader, frameToFill) == false)
        {
            /* eat two bytes to progress on the stream */
            instance->reader->read_exact (instance->reader, frameHeader, 2);

            continue;
        }

        if (frameToFill->version == 3)
        {
            /* logFmsg (CONT_LOG_ERROR, "not sure if we like version 2.5"); */

            logFmsg (CONT_LOG_ERROR, "we don't support version 2.5");

            /* eat two bytes to progress on the stream */
            instance->reader->read_exact (instance->reader, frameHeader, 2);

            continue;
        }

        if (frameToFill->version != 1)
        {
            logFmsg (CONT_LOG_ERROR, "mp3_reader: Unknown mpeg version: %d", frameToFill->version);

            return (false);
        }

        /* 
         * NOTE: we may not like:
         * (frameToFill->version == 1 && frameToFill->layer == 1)
         * either!
         */

        if (skippedBytes > 0)
        {
            logFmsg (CONT_LOG_DEBUG, "skipped %d bytes", skippedBytes);
        }

        return (true); /* return offset */
    }
}


static bool
mp3_reader_add_next_frame (Mp3Reader *instance, MusicPacket *packet)
{
    struct mp3frame frame;
    char frameBuffer[4096];

    if (mp3_reader_get_next_frame (instance, &frame) == false)
    {
        return (false);
    }

    assert (frame.frame_byte_length < 4096);

    if (instance->reader->read_exact (instance->reader, frameBuffer, frame.frame_byte_length) == true)
    {
        dynFappend (music_packet_data_get (packet), frameBuffer, frame.frame_byte_length);

        music_packet_time_length_add_usec (packet, frame.frame_usec_length);

        return (true);
    }
    else
    {
        return (false);
    }
}

MusicPacket *
mp3_reader_get_packet (Mp3Reader *instance, int requestedUsecs)
{
    MusicPacket *packet;
    bool addedFrame = false;

    packet = music_packet_new();

    while (music_packet_time_length_usecs_get (packet) < requestedUsecs)
    {
        if (mp3_reader_add_next_frame (instance, packet) == false)
        {
            break;
        }

        addedFrame = true;
    }

    if (addedFrame == false)
    {
        music_packet_destroy (packet);

        return (NULL);
    }
    else
    {
        return (packet);
    }
}

