//
//  FFMPEGContainer.m
//  FFMPEG Plugin
//
//  Created by Christopher Snowhill on 6/25/22.
//

#import "FFMPEGContainer.h"

#import "FFMPEGDecoder.h"

#import "Logging.h"

@implementation FFMPEGContainer

+ (NSArray *)fileTypes {
	return [FFMPEGDecoder fileTypes];
}

+ (NSArray *)mimeTypes {
	return [FFMPEGDecoder fileTypes];
}

+ (float)priority {
	return [FFMPEGDecoder priority];
}

+ (NSArray *)urlsForContainerURL:(NSURL *)url {
	if([url fragment]) {
		// input url already has fragment defined - no need to expand further
		return [NSMutableArray arrayWithObject:url];
	}

	id audioSourceClass = NSClassFromString(@"AudioSource");
	id<CogSource> source = [audioSourceClass audioSourceForURL:url];

	if(![source open:url])
		return [NSArray array];

	int errcode, i;
	AVStream *stream;

	AVFormatContext *formatCtx = NULL;
	AVIOContext *ioCtx = NULL;

	BOOL isStream = NO;

	uint8_t *buffer = NULL;

	// register all available codecs

	if(([[url scheme] isEqualToString:@"http"] ||
	    [[url scheme] isEqualToString:@"https"]) &&
	   [[url pathExtension] isEqualToString:@"m3u8"]) {
		[source close];
		source = nil;

		isStream = YES;

		formatCtx = avformat_alloc_context();
		if(!formatCtx) {
			ALog(@"Unable to allocate AVFormat context");
			return [NSArray array];
		}

		NSString *urlString = [url absoluteString];
		if((errcode = avformat_open_input(&formatCtx, [urlString UTF8String], NULL, NULL)) < 0) {
			char errDescr[4096];
			av_strerror(errcode, errDescr, 4096);
			ALog(@"Error opening file, errcode = %d, error = %s", errcode, errDescr);
			return [NSArray array];
		}
	} else {
		buffer = av_malloc(32 * 1024);
		if(!buffer) {
			ALog(@"Out of memory!");
			[source close];
			source = nil;
			return [NSArray array];
		}

		ioCtx = avio_alloc_context(buffer, 32 * 1024, 0, (__bridge void *)source, ffmpeg_read, ffmpeg_write, ffmpeg_seek);
		if(!ioCtx) {
			ALog(@"Unable to create AVIO context");
			av_free(buffer);
			[source close];
			source = nil;
			return [NSArray array];
		}

		formatCtx = avformat_alloc_context();
		if(!formatCtx) {
			ALog(@"Unable to allocate AVFormat context");
			buffer = ioCtx->buffer;
			av_free(ioCtx);
			av_free(buffer);
			[source close];
			source = nil;
			return [NSArray array];
		}

		formatCtx->pb = ioCtx;

		if((errcode = avformat_open_input(&formatCtx, "", NULL, NULL)) < 0) {
			char errDescr[4096];
			av_strerror(errcode, errDescr, 4096);
			ALog(@"Error opening file, errcode = %d, error = %s", errcode, errDescr);
			avformat_close_input(&(formatCtx));
			buffer = ioCtx->buffer;
			av_free(ioCtx);
			av_free(buffer);
			[source close];
			source = nil;
			return [NSArray array];
		}
	}

	if((errcode = avformat_find_stream_info(formatCtx, NULL)) < 0) {
		char errDescr[4096];
		av_strerror(errcode, errDescr, 4096);
		ALog(@"Can't find stream info, errcode = %d, error = %s", errcode, errDescr);
		avformat_close_input(&(formatCtx));
		if(ioCtx) {
			buffer = ioCtx->buffer;
			av_free(ioCtx);
		}
		if(buffer) {
			av_free(buffer);
		}
		if(source) {
			[source close];
			source = nil;
		}
		return [NSArray array];
	}

	int streamIndex = -1;
	int metadataIndex = -1;
	int attachedPicIndex = -1;
	AVCodecParameters *codecPar;

	for(i = 0; i < formatCtx->nb_streams; i++) {
		stream = formatCtx->streams[i];
		codecPar = stream->codecpar;
		if(streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) {
			DLog(@"audio codec found");
			streamIndex = i;
		} else if(codecPar->codec_id == AV_CODEC_ID_TIMED_ID3) {
			metadataIndex = i;
		} else if(stream->disposition & AV_DISPOSITION_ATTACHED_PIC) {
			attachedPicIndex = i;
		} else {
			stream->discard = AVDISCARD_ALL;
		}
	}

	if(streamIndex < 0) {
		ALog(@"no audio codec found");
		avformat_close_input(&(formatCtx));
		if(ioCtx) {
			buffer = ioCtx->buffer;
			av_free(ioCtx);
		}
		if(buffer) {
			av_free(buffer);
		}
		if(source) {
			[source close];
			source = nil;
		}
		return [NSArray array];
	}

	NSMutableArray *tracks = [NSMutableArray array];

	int subsongs = formatCtx->nb_chapters;
	if(subsongs < 1) subsongs = 1;

	for(i = 0; i < subsongs; ++i) {
		[tracks addObject:[NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%i", i]]];
	}

	avformat_close_input(&(formatCtx));
	if(ioCtx) {
		buffer = ioCtx->buffer;
		av_free(ioCtx);
	}
	if(buffer) {
		av_free(buffer);
	}
	if(source) {
		[source close];
		source = nil;
	}

	return tracks;
}

@end
