
package com.limegroup.gnutella.metadata.audio.reader;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;

import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import org.jaudiotagger.tag.id3.ID3v1Tag;
import org.jaudiotagger.tag.id3.ID3v24Frames;

/**
 *  Reads MetaData from MP3 files. This extends AudioDataReader which also
 *  handles this format. However, store files need to get checked and parsed
 *  correctly so we do that here. 
 */
public class MP3MetaData extends AudioDataReader {
	
	public MP3MetaData(File f) throws IOException, IllegalArgumentException {
		super(f);
	}
	
    @Override
    protected void readTag(AudioFile audioFile, Tag tag) {
        
        MP3File mp3File = ((MP3File)audioFile);
        mp3File.getID3v1Tag();
        
        AbstractID3v2Tag v2Tag = mp3File.getID3v2Tag();
        ID3v1Tag v1Tag = mp3File.getID3v1Tag();

        // check v2 tags first if they exist
        if( v2Tag != null )
            readV2Tag(v2Tag);

        // check v1 tags next
        if( v1Tag != null )
            readV1Tag(v1Tag);
    }
    
    /**
     * Reads v1 tags from the mp3. Only writes the field to the AudioData if
     * it has not been filled in by v2 tags
     */
    private void readV1Tag(ID3v1Tag tag){
        if( audioData.getTitle() == null || audioData.getTitle().length() == 0)
            audioData.setTitle(tag.getFirstTitle());
        if( audioData.getArtist() == null || audioData.getArtist().length() == 0)
            audioData.setArtist(tag.getFirstArtist());
        if( audioData.getAlbum() == null || audioData.getAlbum().length() == 0)
            audioData.setAlbum(tag.getFirstAlbum());
        if( audioData.getYear() == null || audioData.getYear().length() == 0)
            audioData.setYear(tag.getFirstYear()); 
        if( audioData.getComment() == null || audioData.getComment().length() == 0)
            audioData.setComment(tag.getFirstComment());
        if( audioData.getGenre() == null || audioData.getGenre().length() == 0)
            audioData.setGenre(tag.getFirstGenre()); 
        if( audioData.getTrack() == null || audioData.getTrack().length() == 0) {
            try {
                audioData.setTrack(tag.getFirstTrack());
            }
            catch(UnsupportedOperationException e) {
                // id3v1.0 tags dont have tracks
            }
        }
    }
    
    /**
     * Reads v2 tags from the mp3. 
     */
    private void readV2Tag(AbstractID3v2Tag tag){
        audioData.setTitle(tag.getFirstTitle());
        audioData.setArtist(tag.getFirstArtist());
        audioData.setAlbum(tag.getFirstAlbum());
        audioData.setYear(tag.getFirstYear()); 
        audioData.setComment(tag.getFirstComment());
        audioData.setGenre(parseGenre(tag.getFirstGenre()));
        audioData.setTrack(tag.getFirstTrack());
        audioData.setLicense(tag.getFirst(ID3v24Frames.FRAME_ID_COPYRIGHTINFO));
        
        Iterator iter = tag.iterator();
        while(iter.hasNext()) {
            if( audioData.getLicenseType() != null && audioData.getLicenseType().equals(MAGIC_KEY) )
                return;
            Object nextFrame = iter.next();

            if( !(nextFrame instanceof AbstractID3v2Frame)) 
                continue;

            AbstractID3v2Frame o = (AbstractID3v2Frame)nextFrame;           

            if( !(o.getId().equals("TIT2") || o.getId().equals("TALB") || o.getId().equals("TOAL") ||
                    o.getId().equals("TOPE") || o.getId().equals("TPE1") || o.getId().equals("TPE2")
                    || o.getId().equals("TPE3") || o.getId().equals("TPE4")) )
            
            if( o.getBody().getObject("Text") != null )
                checkLWS(o.getBody().getObject("Text").toString());
            else 
                isRawCheck(o.getRawContent());
        }
    }
       
    /**
     * Checks a raw content field for the magic String. This is always in UTF-8 encoding so use the 
     * content byte array instead. 
     */
    private void isRawCheck(byte[] contentBytes) {
        try {
            String content = new String(contentBytes,"UTF-8"); 
            checkLWS(content);
        } catch (UnsupportedEncodingException e) {
        }
    }

    /**
     * If the song is not a LWS already, do a substring search to see if
     * it is a LWS song
     * @param content - ID3 tag to scan for a substring
     */
    private void checkLWS(String content) { 
        if( audioData.getLicenseType() == null || !audioData.getLicenseType().equals(MAGIC_KEY))
            if( content.indexOf(MAGIC_KEY) != -1) { 
                audioData.setLicenseType(MAGIC_KEY);
            }
    }
    
    /**
     * Some genres in ID3v2 tags are displaying (XXX) numbers along side the genre.
     * If this exists it hides the number from the user
     */
    private String parseGenre(String genre){
        if( genre == null || genre.length() <= 0) 
            return genre;
        String cleanGenre = genre;
        if( genre.charAt(0) == '(') {
            int startIndex = 0;
            for(int i = 0; i < genre.length(); i++) {
                if( genre.charAt(i) == ')') {
                    startIndex = i + 1;
                }
            }            
            cleanGenre = genre.substring(startIndex);
        }
        return cleanGenre;
        
    }
}
