//
//

#include "SubtitleDecoder.h"

namespace {

double getFrameTime(int64_t pts, AVRational time_base) {
	return pts * av_q2d(time_base);
}

}

namespace cutscene {
namespace ffmpeg {

SubtitleDecoder::SubtitleDecoder(cutscene::ffmpeg::DecoderStatus* status) : FFMPEGStreamDecoder(status) {
}
SubtitleDecoder::~SubtitleDecoder() {

}
void SubtitleDecoder::decodePacket(AVPacket* packet) {
	int finishedFrame = 0;
	AVSubtitle subtitle;
	auto result = avcodec_decode_subtitle2(m_status->subtitleCodecCtx, &subtitle, &finishedFrame, packet);

	if (result >= 0 && finishedFrame) {
		pushSubtitleFrame(packet, &subtitle);

		avsubtitle_free(&subtitle);
	}
}
void SubtitleDecoder::finishDecoding() {
	// Handle those decoders that have a delay
	AVPacket nullPacket;
	memset(&nullPacket, 0, sizeof(nullPacket));
	nullPacket.data = nullptr;
	nullPacket.size = 0;

	AVSubtitle subtitle;
	while (true) {
		int finishedFrame = 1;
		auto err = avcodec_decode_subtitle2(m_status->subtitleCodecCtx, &subtitle, &finishedFrame, &nullPacket);

		if (err < 0 || !finishedFrame) {
			break;
		}

		pushSubtitleFrame(&nullPacket, &subtitle);

		avsubtitle_free(&subtitle);
	}
}
void SubtitleDecoder::pushSubtitleFrame(AVPacket* packet, AVSubtitle* subtitle) {
	if (subtitle->format != 1) {
		// Non-text subtitles are not supported yet.
		mprintf(("FFmpeg: Detected a non-text subtitle! This is not supported yet!\n"));
		return;
	}
	if (subtitle->num_rects < 1) {
		return;
	}

	int64_t pts = subtitle->pts;
	if (pts == AV_NOPTS_VALUE) {
		pts = packet->pts;
	}
	if (pts == AV_NOPTS_VALUE) {
		// Still no valid timestamp...
		return;
	}
	auto packet_time = getFrameTime(pts, m_status->subtitleStream->time_base);

	auto start_time = packet_time + (subtitle->start_display_time / 1000.0);
	auto end_time = packet_time + (subtitle->end_display_time / 1000.0);
	if (subtitle->end_display_time == 0 && m_status->subtitleStream->time_base.num != 0) {
		end_time = packet_time + getFrameTime(packet->duration, m_status->subtitleStream->time_base);
	}

	SCP_string processed_text;
	// For now we only use the first subtitle rectangle
	auto subtitle_rect = subtitle->rects[0];
	if (subtitle_rect->type == SUBTITLE_BITMAP) {
		// Same as above, non-text subtitles are not supported yet.
		mprintf(("FFmpeg: Detected a non-text subtitle! This is not supported yet!\n"));
		return;
	} else if (subtitle_rect->type == SUBTITLE_TEXT) {
		// Subtitle does not need to be processed any further
		processed_text = subtitle_rect->text;
	} else if (subtitle_rect->type == SUBTITLE_ASS) {
		SCP_string ass_text = subtitle_rect->ass;
		
		// We are not interested in any of the other information contained in the ASS line but we still need to figure
		// out where our text starts
		auto comma_pos = ass_text.find(',');
		for (auto i = 0; i < 8 && comma_pos != SCP_string::npos; ++i) {
			comma_pos = ass_text.find(',', comma_pos + 1);
		}
		Assertion(comma_pos != SCP_string::npos, "Received an ill-formed ASS line from FFmpeg! Text was '%s'.", subtitle_rect->ass);

		// This + 1 is safe since comma_pos points to a valid character so the next character is at worst the end of the string
		processed_text = ass_text.substr(comma_pos + 1);

		// ASS has a special sequence for new lines that needs to be converted to an actual new line
		auto newline_pos = processed_text.find("\\N");
		while(newline_pos != SCP_string::npos) {
			processed_text.replace(newline_pos, 2, "\n");

			newline_pos = processed_text.find("\\N");
		}
	} else {
		mprintf(("FFmpeg: Detected unknown subtitle name in movie!\n"));
		return;
	}

	// Remove \r from string. Some subtitles use \r\n for line breaks and we only support \n
	auto ret_pos = processed_text.find('\r');
	while(ret_pos != SCP_string::npos) {
		processed_text.erase(ret_pos, 1);

		ret_pos = processed_text.find('\r');
	}

	SubtitleFramePtr frame(new SubtitleFrame());
	frame->text = std::move(processed_text);

	frame->displayStartTime = start_time;
	frame->displayEndTime = end_time;

	pushFrame(std::move(frame));
}
void SubtitleDecoder::flushBuffers() {
	avcodec_flush_buffers(m_status->subtitleCodecCtx);
}

}
}

