//
//  TagLibMetadataReader.m
//  TagLib
//
//  Created by Vincent Spader on 2/24/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "TagLibMetadataReader.h"
#import <taglib/fileref.h>
#import <taglib/tag.h>
#import <taglib/mpcproperties.h>
#import <taglib/audioproperties.h>
#import <taglib/mpeg/mpegfile.h>
#import <taglib/mp4/mp4file.h>
#import <taglib/xiphcomment.h>
#import <taglib/vorbisfile.h>
#import <taglib/flacfile.h>
#import <taglib/mpeg/id3v2/id3v2tag.h>
#import <taglib/mpeg/id3v2/frames/attachedpictureframe.h>

@implementation TagLibMetadataReader

+ (NSDictionary *)metadataForURL:(NSURL *)url
{
	if (![url isFileURL]) {
		return [NSDictionary dictionary];
	}
	
	NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
	
//	if ( !*TagLib::ascii_encoding ) {
//		NSStringEncoding enc = [NSString defaultCStringEncoding];
//		CFStringEncoding cfenc = CFStringConvertNSStringEncodingToEncoding(enc);
//		NSString *ref = (NSString *)CFStringConvertEncodingToIANACharSetName(cfenc);
//		UInt32 cp = CFStringConvertEncodingToWindowsCodepage(cfenc);
//
//		// Most tags are using windows codepage, so remap OS X codepage to Windows one.
//
//		static struct {
//			UInt32 from, to;
//		} codepage_remaps[] = {
//			{ 10001, 932 },		// Japanese Shift-JIS
//			{ 10002, 950 },		// Traditional Chinese
//			{ 10003, 949 },		// Korean
//			{ 10004, 1256 },	// Arabic
//			{ 10005, 1255 },	// Hebrew
//			{ 10006, 1253 },	// Greek
//			{ 10007, 1251 },	// Cyrillic
//			{ 10008, 936 },		// Simplified Chinese
//			{ 10029, 1250 },	// Central European (latin2)
//		};
//
//		int i;
//		int max = sizeof(codepage_remaps)/sizeof(codepage_remaps[0]);
//		for ( i=0; i<max; i++ )
//			if ( codepage_remaps[i].from == cp )
//				break;
//		if ( i < max )
//			sprintf(TagLib::ascii_encoding, "windows-%d", codepage_remaps[i].to);
//		else
//			strcpy(TagLib::ascii_encoding, [ref UTF8String]);
//
//	}
	
	
	TagLib::FileRef f((const char *)[[url path] UTF8String], false);
	if (!f.isNull())
	{
		const TagLib::Tag *tag = f.tag();
		
		if (tag)
		{
			TagLib::String artist, albumartist, title, album, genre, comment;
			int year, track, disc;
            float rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak;
            TagLib::String cuesheet;
			
			artist = tag->artist();
            albumartist = tag->albumartist();
			title = tag->title();;
			album = tag->album();
			genre = tag->genre();
			comment = tag->comment();
            cuesheet = tag->cuesheet();
			
			year = tag->year();
			[dict setObject:[NSNumber numberWithInt:year] forKey:@"year"];
			
			track = tag->track();
			[dict setObject:[NSNumber numberWithInt:track] forKey:@"track"];
            
            disc = tag->disc();
            [dict setObject:[NSNumber numberWithInt:disc] forKey:@"disc"];

            rgAlbumGain = tag->rgAlbumGain();
            rgAlbumPeak = tag->rgAlbumPeak();
            rgTrackGain = tag->rgTrackGain();
            rgTrackPeak = tag->rgTrackPeak();
            [dict setObject:[NSNumber numberWithFloat:rgAlbumGain] forKey:@"replayGainAlbumGain"];
            [dict setObject:[NSNumber numberWithFloat:rgAlbumPeak] forKey:@"replayGainAlbumPeak"];
            [dict setObject:[NSNumber numberWithFloat:rgTrackGain] forKey:@"replayGainTrackGain"];
            [dict setObject:[NSNumber numberWithFloat:rgTrackPeak] forKey:@"replayGainTrackPeak"];
			
			if (!artist.isEmpty())
				[dict setObject:[NSString stringWithUTF8String:artist.toCString(true)] forKey:@"artist"];
            
            if (!albumartist.isEmpty())
                [dict setObject:[NSString stringWithUTF8String:albumartist.toCString(true)] forKey:@"albumartist"];

			if (!album.isEmpty())
				[dict setObject:[NSString stringWithUTF8String:album.toCString(true)] forKey:@"album"];
			
			if (!title.isEmpty())
				[dict setObject:[NSString stringWithUTF8String:title.toCString(true)] forKey:@"title"];
			
			if (!genre.isEmpty())
				[dict setObject:[NSString stringWithUTF8String:genre.toCString(true)] forKey:@"genre"];
            
            if (!cuesheet.isEmpty())
                [dict setObject:[NSString stringWithUTF8String:cuesheet.toCString(true)] forKey:@"cuesheet"];
		}

		// Try to load the image.
		NSData * image = nil;
        
		// Try to load the image.
		// WARNING: HACK
		TagLib::MPEG::File *mf = dynamic_cast<TagLib::MPEG::File *>(f.file());
		if (mf) {
			TagLib::ID3v2::Tag *tag = mf->ID3v2Tag();
			if (tag) {
				TagLib::ID3v2::FrameList pictures = mf->ID3v2Tag()->frameListMap()["APIC"];
				if (!pictures.isEmpty()) {
					TagLib::ID3v2::AttachedPictureFrame *pic = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(pictures.front());

					image = [NSData dataWithBytes:pic->picture().data() length:pic->picture().size()];
                }
            }
		}


        // D-D-D-DOUBLE HACK!
        TagLib::MP4::File *m4f = dynamic_cast<TagLib::MP4::File *>(f.file());
        if (m4f) {
            TagLib::MP4::Tag *tag = m4f->tag();
            if (tag) {
                auto covr = tag->item("covr");
                if (covr.isValid()) {
                    auto coverArtList = covr.toCoverArtList();
                    if (!coverArtList.isEmpty()) {
                        TagLib::MP4::CoverArt coverArt = coverArtList.front();
                        image = [NSData dataWithBytes:coverArt.data().data() length:coverArt.data().size()];
                    }
                }
            }
        }

        TagLib::Ogg::Vorbis::File *vorbis = dynamic_cast<TagLib::Ogg::Vorbis::File*>(f.file());
        if (vorbis) {
            TagLib::Ogg::XiphComment *tag = vorbis->tag();
            if (tag) {
                auto list = tag->pictureList();
                if (!list.isEmpty()) {
                    // Just get the first image for now.
                    TagLib::FLAC::Picture* coverArt = list.front();
                    if (coverArt) {
                        // Look into TagLib::FLAC::Picture::Type for type description.
                        NSLog(@"Loading image metadata from Ogg Vorbis, type = %d",
                             static_cast<int>(coverArt->type()));
                        image = [NSData dataWithBytes:coverArt->data().data()
                                               length:coverArt->data().size()];
                    }
                }
            }
        }

        TagLib::FLAC::File *flac = dynamic_cast<TagLib::FLAC::File*>(f.file());
        if (flac) {
            auto list = flac->pictureList();
            if (!list.isEmpty()) {
                // Just get the first image for now.
                TagLib::FLAC::Picture* coverArt = list.front();
                if (coverArt) {
                    // Look into TagLib::FLAC::Picture::Type for type description.
                    NSLog(@"Loading image metadata from FLAC, type = %d",
                          static_cast<int>(coverArt->type()));
                    image = [NSData dataWithBytes:coverArt->data().data()
                                           length:coverArt->data().size()];
                }
            }
        }

        if (nil == image) {
			// Try to load image from external file

			NSString *path = [[url path] stringByDeletingLastPathComponent];

				// Gather list of candidate image files
				
			NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
            NSArray *types = @[@"jpg", @"jpeg", @"png"];
			NSArray *imageFileNames = [fileNames pathsMatchingExtensions:types];

            for (NSString *fileName in imageFileNames) {
				if ([TagLibMetadataReader isCoverFile:fileName]) {
					image = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:fileName]];
					break;
				}
			}
		}
		
		if (nil != image) {
			[dict setObject:image forKey:@"albumArt"];
		}
	}

	return dict;
}

+ (BOOL)isCoverFile:(NSString *)fileName
{
    for (NSString *coverFileName in [TagLibMetadataReader coverNames]) {
		if ([[[[fileName lastPathComponent] stringByDeletingPathExtension] lowercaseString] hasSuffix:coverFileName]) {
			return true;
		}
	}
	return false;
			
}

+ (NSArray *)coverNames
{
	return @[@"cover", @"folder", @"album", @"front"];
}

+ (NSArray *)fileTypes
{
	//May be a way to get a list of supported formats
	return @[@"ape", @"asf", @"wma", @"ogg", @"opus", @"mpc", @"flac", @"m4a", @"mp3", @"tak", @"ac3", @"apl", @"dts", @"dtshd", @"tta", @"wav", @"aif", @"aiff", @"wv", @"wvp"];
}

+ (NSArray *)mimeTypes
{
	return @[@"audio/x-ape", @"audio/x-ms-wma", @"application/ogg", @"application/x-ogg", @"audio/x-vorbis+ogg", @"audio/x-musepack", @"audio/x-flac", @"audio/x-m4a", @"audio/mpeg", @"audio/x-mp3", @"audio/x-tak", @"audio/x-ac3", @"audio/x-apl", @"audio/x-dts", @"audio/x-dtshd", @"audio/x-tta", @"audio/wav", @"audio/aiff", @"audio/x-wavpack"];
}

+ (float)priority
{
    return 1.0f;
}

@end