#include "mov-internal.h"
#include <errno.h>
#include <stdlib.h>
#include <assert.h>

// 8.6.6 Edit List Box (p53)
int mov_read_elst(struct mov_t* mov, const struct mov_box_t* box)
{
	uint32_t i, entry_count;
	uint32_t version;
	struct mov_track_t* track = mov->track;

	version = mov_buffer_r8(&mov->io); /* version */
	mov_buffer_r24(&mov->io); /* flags */
	entry_count = mov_buffer_r32(&mov->io);

	assert(0 == track->elst_count && NULL == track->elst);
	if (track->elst_count < entry_count)
	{
		void* p = realloc(track->elst, sizeof(struct mov_elst_t) * entry_count);
		if (NULL == p) return -ENOMEM;
		track->elst = (struct mov_elst_t*)p;
	}
	track->elst_count = entry_count;

	for (i = 0; i < entry_count; i++)
	{
		if (1 == version)
		{
			track->elst[i].segment_duration = mov_buffer_r64(&mov->io);
			track->elst[i].media_time = (int64_t)mov_buffer_r64(&mov->io);
		}
		else
		{
			assert(0 == version);
			track->elst[i].segment_duration = mov_buffer_r32(&mov->io);
			track->elst[i].media_time = (int32_t)mov_buffer_r32(&mov->io);
		}
		track->elst[i].media_rate_integer = (int16_t)mov_buffer_r16(&mov->io);
		track->elst[i].media_rate_fraction = (int16_t)mov_buffer_r16(&mov->io);
	}

	(void)box;
	return mov_buffer_error(&mov->io);
}

size_t mov_write_elst(const struct mov_t* mov)
{
	uint32_t size;
	int64_t time;
	int64_t delay;
	uint8_t version;
	const struct mov_track_t* track = mov->track;

    assert(track->start_dts == track->samples[0].dts);
	version = track->tkhd.duration > UINT32_MAX ? 1 : 0;

    // in media time scale units, in composition time
	time = track->samples[0].pts - track->samples[0].dts;
    // in units of the timescale in the Movie Header Box
	delay = track->samples[0].pts * mov->mvhd.timescale / track->mdhd.timescale;
	if (delay > UINT32_MAX)
		version = 1;

	time = time < 0 ? 0 : time;
	size = 12/* full box */ + 4/* entry count */ + (delay > 0 ? 2 : 1) * (version ? 20 : 12);

	mov_buffer_w32(&mov->io, size); /* size */
	mov_buffer_write(&mov->io, "elst", 4);
	mov_buffer_w8(&mov->io, version); /* version */
	mov_buffer_w24(&mov->io, 0); /* flags */
	mov_buffer_w32(&mov->io, delay > 0 ? 2 : 1); /* entry count */

	if (delay > 0)
	{
		if (1 == version)
		{
			mov_buffer_w64(&mov->io, (uint64_t)delay); /* segment_duration */
			mov_buffer_w64(&mov->io, (uint64_t)-1); /* media_time */
		}
		else
		{
			mov_buffer_w32(&mov->io, (uint32_t)delay);
			mov_buffer_w32(&mov->io, (uint32_t)-1);
		}

		mov_buffer_w16(&mov->io, 1); /* media_rate_integer */
		mov_buffer_w16(&mov->io, 0); /* media_rate_fraction */
	}

	/* duration */
	if (version == 1) 
	{
		mov_buffer_w64(&mov->io, track->tkhd.duration);
		mov_buffer_w64(&mov->io, time);
	}
	else 
	{
		mov_buffer_w32(&mov->io, (uint32_t)track->tkhd.duration);
		mov_buffer_w32(&mov->io, (uint32_t)time);
	}
	mov_buffer_w16(&mov->io, 1); /* media_rate_integer */
	mov_buffer_w16(&mov->io, 0); /* media_rate_fraction */

	return size;
}

void mov_apply_elst(struct mov_track_t *track, uint32_t timescale)
{
    size_t i;

    // edit list
    track->samples[0].dts = 0;
    track->samples[0].pts = 0;
    for (i = 0; i < track->elst_count; i++)
    {
        if (-1 == track->elst[i].media_time)
        {
            track->samples[0].dts = track->elst[i].segment_duration * track->mdhd.timescale / timescale; // movie timescale -> track timescale
            track->samples[0].pts = track->samples[0].dts;
        }
    }
}

void mov_apply_elst_tfdt(struct mov_track_t *track, uint32_t timescale)
{
    size_t i;

    for (i = 0; i < track->elst_count; i++)
    {
        if (-1 == track->elst[i].media_time)
        {
            track->tfdt_dts += track->elst[i].segment_duration * track->mdhd.timescale / timescale; // movie timescale -> track timescale
        }
    }
}
