void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
	agi::vfr::Framerate fps = AskForFPS(true, false);
	if (!fps.IsLoaded()) return;

	AssFile copy(*src);
	copy.Sort();
	StripComments(copy.Line);
	RecombineOverlaps(copy.Line);
	MergeIdentical(copy.Line);
	StripTags(copy.Line);
	ConvertNewlines(copy.Line, "|");

	TextFileWriter file(filename, encoding);

	// Write FPS line
	if (!fps.IsVFR()) {
		file.WriteLineToFile(wxString::Format("{1}{1}%.6f", fps.FPS()));
	}

	// Write lines
	for (LineList::const_iterator cur = copy.Line.begin(); cur != copy.Line.end(); ++cur) {
		if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
			int start = fps.FrameAtTime(current->Start, agi::vfr::START);
			int end = fps.FrameAtTime(current->End, agi::vfr::END);

			file.WriteLineToFile(wxString::Format("{%i}{%i}%s", start, end, current->Text));
		}
	}
}
void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const {
	agi::vfr::Framerate fps = AskForFPS(true, false);
	if (!fps.IsLoaded()) return;

	AssFile copy(*src);
	copy.Sort();
	StripComments(copy);
	RecombineOverlaps(copy);
	MergeIdentical(copy);
	StripTags(copy);
	ConvertNewlines(copy, "|");

	TextFileWriter file(filename, encoding);

	// Write FPS line
	if (!fps.IsVFR())
		file.WriteLineToFile(str(boost::format("{1}{1}%.6f") % fps.FPS()));

	// Write lines
	for (auto current : copy.Line | agi::of_type<AssDialogue>()) {
		int start = fps.FrameAtTime(current->Start, agi::vfr::START);
		int end = fps.FrameAtTime(current->End, agi::vfr::END);

		file.WriteLineToFile(str(boost::format("{%i}{%i}%s") % start % end % boost::replace_all_copy(current->Text.get(), "\\N", "|")));
	}
}
void EncoreSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const&) const {
	agi::vfr::Framerate fps = AskForFPS(false, true);
	if (!fps.IsLoaded()) return;

	// Convert to encore
	AssFile copy(*src);
	copy.Sort();
	StripComments(copy.Line);
	RecombineOverlaps(copy.Line);
	MergeIdentical(copy.Line);
	StripTags(copy.Line);
	ConvertNewlines(copy.Line, "\r\n");


	// Encode wants ; for NTSC and : for PAL
	// The manual suggests no other frame rates are supported
	char sep = fps.NeedsDropFrames() ? ';' : ':';
	SmpteFormatter ft(fps, sep);

	// Write lines
	int i = 0;
	TextFileWriter file(filename, "UTF-8");
	for (LineList::const_iterator cur = copy.Line.begin(); cur != copy.Line.end(); ++cur) {
		if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
			++i;
			file.WriteLineToFile(wxString::Format("%i %s %s %s", i, ft.ToSMPTE(current->Start), ft.ToSMPTE(current->End), current->Text));
		}
	}
}
void MicroDVDSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const {
	TextFileReader file(filename, encoding);
	wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED);

	target->LoadDefault(false);

	agi::vfr::Framerate fps;

	bool isFirst = true;
	while (file.HasMoreLines()) {
		wxString line = file.ReadLineFromFile();
		if (exp.Matches(line)) {
			long f1, f2;
			exp.GetMatch(line, 1).ToLong(&f1);
			exp.GetMatch(line, 2).ToLong(&f2);
			wxString text = exp.GetMatch(line, 3);

			// If it's the first, check if it contains fps information
			if (isFirst) {
				isFirst = false;

				if (f1 == 1 && f2 == 1) {
					// Convert fps
					double cfr;
					if (text.ToDouble(&cfr)) {
						fps = cfr;
						continue;
					}
				}

				// If it wasn't an fps line, ask the user for it
				fps = AskForFPS(true, false);
				if (!fps.IsLoaded()) return;
			}

			text.Replace("|", "\\N");

			AssDialogue *diag = new AssDialogue;
			diag->Start = fps.TimeAtFrame(f1, agi::vfr::START);
			diag->End = fps.TimeAtFrame(f2, agi::vfr::END);
			diag->Text = text;
			target->Line.push_back(diag);
		}
	}
}
void MicroDVDSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const {
	TextFileReader file(filename, encoding);

	target->LoadDefault(false);

	agi::vfr::Framerate fps;

	bool isFirst = true;
	while (file.HasMoreLines()) {
		boost::smatch match;
		std::string line = file.ReadLineFromFile();
		if (!regex_match(line, match, line_regex)) continue;

		std::string text = match[3].str();

		// If it's the first, check if it contains fps information
		if (isFirst) {
			isFirst = false;

			double cfr;
			if (agi::util::try_parse(text, &cfr)) {
				fps = cfr;
				continue;
			}

			// If it wasn't an fps line, ask the user for it
			fps = AskForFPS(true, false);
			if (!fps.IsLoaded()) return;
		}

		int f1 = boost::lexical_cast<int>(match[1]);
		int f2 = boost::lexical_cast<int>(match[2]);

		boost::replace_all(text, "|", "\\N");

		auto diag = new AssDialogue;
		diag->Start = fps.TimeAtFrame(f1, agi::vfr::START);
		diag->End = fps.TimeAtFrame(f2, agi::vfr::END);
		diag->Text = text;
		target->Line.push_back(*diag);
	}
}
void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& video_fps, std::string const&) const {
	agi::vfr::Framerate fps = AskForFPS(false, true, video_fps);
	if (!fps.IsLoaded()) return;

	// Convert to encore
	AssFile copy(*src);
	copy.Sort();
	StripComments(copy);
	RecombineOverlaps(copy);
	MergeIdentical(copy);
	StripTags(copy);
	ConvertNewlines(copy, "\r\n");

	// Encore wants ; for NTSC and : for PAL
	// The manual suggests no other frame rates are supported
	SmpteFormatter ft(fps, fps.NeedsDropFrames() ? ";" : ":");

	// Write lines
	int i = 0;
	TextFileWriter file(filename, "UTF-8");
	for (auto const& current : copy.Events)
		file.WriteLineToFile(str(boost::format("%i %s %s %s") % ++i % ft.ToSMPTE(current.Start) % ft.ToSMPTE(current.End) % current.Text));
}