/*
 * VFR translation functions for OverLua
 *

    Copyright 2007  Niels Martin Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

    Contact:
    E-mail: <jiifurusu@gmail.com>
    IRC: jfs in #aegisub on irc.rizon.net

 */

#include "vfr.h"
#include <stdio.h>
#include <string.h>
#include <vector>


// Work with seconds per frame (spf) here instead of fps since that's more natural for the translation we're doing


class TimecodesV1 : public VFRTranslator {
private:
	// Used when sections run out
	double default_spf;
	double first_non_section_timestamp;
	int first_non_section_frame;

	// Also generate sections for unspecified ones
	// (use the default framerate then)
	struct FrameRateSection {
		double start_time;
		double spf;
		int start_frame;
		int end_frame;
	};
	std::vector<FrameRateSection> sections;

public:
	virtual double TimeStampFromFrameNumber(int n)
	{
		// Find correct section
		for (size_t i = 0; i < sections.size(); i++) {
			FrameRateSection &sect = sections[i];
			if (n >= sect.start_frame && n <= sect.end_frame) {
				return sect.start_time + (n - sect.start_frame) * sect.spf;
			}
		}
		// Not in a section
		if (n < 0) return 0.0;
		return first_non_section_timestamp + (n - first_non_section_frame) * default_spf;
	}

	TimecodesV1(FILE *vfrfile)
	{
		char buf[100];

		default_spf = -1;
		double cur_time = 0.0;

		FrameRateSection temp_section;
		temp_section.start_time = 0.0;
		temp_section.spf = -1;
		temp_section.start_frame = 0;
		temp_section.end_frame = 0;

		while (fgets(buf, 100, vfrfile)) {
			// Comment?
			if (buf[0] == '#') continue;

			if (strncmp(buf, "Assume ", 7) == 0 && default_spf < 0) {
				char *num = buf+7;
				default_spf = atof(num);
				if (default_spf > 0)
					default_spf = 1 / default_spf;
				else
					default_spf = -1;
				temp_section.spf = default_spf;
				continue;
			}

			int start_frame, end_frame;
			double fps;
			if (scanf("%d,%d,%f", &start_frame, &end_frame, &fps) == 3) {
				// Finish the current temp section
				temp_section.end_frame = start_frame - 1;
				if (temp_section.end_frame >= temp_section.start_frame) {
					cur_time += (temp_section.end_frame - temp_section.start_frame + 1) * temp_section.spf;
					sections.push_back(temp_section);
				}
				// Insert the section corresponding to this line
				temp_section.spf = 1/fps;
				temp_section.start_frame = start_frame;
				temp_section.end_frame = end_frame;
				temp_section.start_time = cur_time;
				cur_time += (end_frame - start_frame + 1) / fps;
				sections.push_back(temp_section);
				// Begin new temp section
				temp_section.spf = default_spf;
				temp_section.start_frame = end_frame + 1;
				temp_section.end_frame = end_frame; // yes, negative duration
				temp_section.start_time = cur_time;
			}
		}

		first_non_section_timestamp = cur_time;
		first_non_section_frame = temp_section.start_frame;
	}

};


class TimecodesV2 : public VFRTranslator {
private:
	// Main data
	std::vector<double> timestamps;
	// For when data are exhausted (well, they shouldn't, then the vfr file is bad)
	int last_known_frame;
	double last_known_timestamp;
	double assumed_spf;

public:
	virtual double TimeStampFromFrameNumber(int n)
	{
		if (n < (int)timestamps.size() && n >= 0) {
			return timestamps[n];
		}
		if (n < 0) return 0.0;
		return last_known_timestamp + (n - last_known_frame) * assumed_spf;
	}

	TimecodesV2(FILE *vfrfile)
	{
		char buf[50];

		timestamps.reserve(8192); // should be enough for most cases

		while (fgets(buf, 50, vfrfile)) {
			// Comment?
			if (buf[0] == '#') continue;
			// Otherwise assume it's a good timestamp
			timestamps.push_back(atof(buf));
		}

		last_known_frame = (int)timestamps.size()-1;
		last_known_timestamp = timestamps[last_known_frame];
		assumed_spf = 1/25.0; // yes, this is stupid - anyone got a better idea?
	}

};


VFRTranslator *GetVFRTranslator(const char *vfrfile)
{
	char buf[32];
	buf[19] = 0; // In "# timecode format v1" the version number is character index 19
	FILE *f = fopen(vfrfile, "r");
	VFRTranslator *res = 0;
	if (fgets(buf, 32, f) && buf[0] == '#') {
		// So do some really shoddy parsing here, assume the file is good
		if (buf[19] == '1') {
			res = new TimecodesV1(f);
		} else if (buf[19] == '2') {
			res = new TimecodesV2(f);
		}
	}
	fclose(f);
	return res;
}