/*
 * Created on May 11, 2004
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package com.limegroup.gnutella.metadata;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.limegroup.gnutella.util.NameValue;
import com.limegroup.gnutella.xml.LimeXMLDocument;
import com.limegroup.gnutella.xml.LimeXMLUtils;
import com.limegroup.gnutella.xml.XMLStringUtils;

/**
 * Encapsulates audio metadata.  Subclasses must implement parseFile.
 */
public abstract class AudioMetaData extends MetaData {
    private String title ;
    private String artist;
    private String album ;
    private String year;
    private String comment;
    private short track = -1;
    private String genre;
    private int bitrate = -1;
    private int length = -1;
    private short totalTracks =-1;
    private short disk=-1;
    private short totalDisks=-1;
    private String license;
    private String price;
    private String licensetype;
    
    public static final String ISO_LATIN_1 = "8859_1";
    public static final String UNICODE = "Unicode";
    
    public static String schemaURI = "http://www.limewire.com/schemas/audio.xsd";
    
    private static final String DLM = XMLStringUtils.DELIMITER;
    private static final String KPX = "audios" + DLM + "audio" + DLM;
    
    public static final String TRACK_KEY    = KPX + "track"    + DLM;
    public static final String ARTIST_KEY   = KPX + "artist"   + DLM;
    public static final String ALBUM_KEY    = KPX + "album"    + DLM;
    public static final String TITLE_KEY    = KPX + "title"    + DLM;
    public static final String GENRE_KEY    = KPX + "genre"    + DLM;
    public static final String YEAR_KEY     = KPX + "year"     + DLM;
    public static final String COMMENTS_KEY = KPX + "comments" + DLM;
    public static final String BITRATE_KEY  = KPX + "bitrate"  + DLM;
    public static final String SECONDS_KEY  = KPX + "seconds"  + DLM;
    public static final String LICENSE_KEY  = KPX + "license"  + DLM;
    public static final String PRICE_KEY    = KPX + "price"    + DLM;
    public static final String LICENSE_TYPE_KEY = KPX + "licensetype" + DLM;
        
    protected AudioMetaData() throws IOException {
    }

    public AudioMetaData(File f) throws IOException{
    	parseFile(f);
    }
    
    
    public static AudioMetaData parseAudioFile(File f) throws IOException{
    	if (LimeXMLUtils.isMP3File(f))
    		return new MP3MetaData(f);
    	if (LimeXMLUtils.isOGGFile(f))
			return new OGGMetaData(f);
		if (LimeXMLUtils.isFLACFile(f))
    		return new FLACMetaData(f);
    	if (LimeXMLUtils.isM4AFile(f))
    		return new M4AMetaData(f);
        if (LimeXMLUtils.isWMAFile(f))
            return new WMAMetaData(f);
    	
    	//TODO: add future supported audio types here
    	
    	return null;
    	
    }
    
    public String getSchemaURI() {
        return schemaURI;
    }
    
    public String toString() {
        return "ID3Data: title[" + title + "], artist[" + artist +
               "], album[" + album + "], year[" + year + "], comment["
               + comment + "], track[" + track + "], genre[" + genre +
               "], bitrate[" + bitrate + "], length[" + length +
               "], license[" + license + "], price[" + price + 
               "], licensetype[" + licensetype + "]";
    }          
    
    public String getTitle() { return title; }
    public String getArtist() { return artist; }
    public String getAlbum() { return album; }
    public String getYear() { return year; }
    public String getComment()  { return comment; }
    public short getTrack() { return track; }
    public short getTotalTracks() {return totalTracks;}
    public short getDisk() {return disk;}
    public short getTotalDisks() {return totalDisks;}
    public String getGenre() { return genre; }
    public int getBitrate() { return bitrate; }
    public int getLength() { return length; }
    public String getLicense() { return license; }
    public String getLicenseType() { return licensetype; }
    
    void setPrice(String price)  { this.price = price; }
    void setTitle(String title) { this.title = title; }
    void setArtist(String artist) { this.artist = artist; }    
    void setAlbum(String album) { this.album = album; }
    void setYear(String year) { this.year = year; }
    void setComment(String comment) { this.comment = comment; }    
    void setTrack(short track) { this.track = track; }    
    void setTotalTracks(short total) { totalTracks = total; }    
    void setDisk(short disk) { this.disk =disk; }
    void setTotalDisks(short total) { totalDisks=total; }
    void setGenre(String genre) { this.genre = genre; }
    void setBitrate(int bitrate) { this.bitrate = bitrate; }    
    void setLength(int length) { this.length = length; }    
    void setLicense(String license) { this.license = license; }
    void setLicenseType(String licensetype) { this.licensetype = licensetype; }
    
    /**
     * Determines if all fields are valid.
     */
    public boolean isComplete() {
        return isValid(title)
            && isValid(artist)
            && isValid(album)
            && isValid(year)
            && isValid(comment)
            && isValid(track)
            && isValid(genre)
            && isValid(bitrate)
            && isValid(length)
            && isValid(license)
            && isValid(licensetype);
    }

    /**
     * Writes the data to a NameValue list.
     */
    public List toNameValueList() {
        List list = new ArrayList();
        add(list, title, TITLE_KEY);
        add(list, artist, ARTIST_KEY);
        add(list, album, ALBUM_KEY);
        add(list, year, YEAR_KEY);
        add(list, comment, COMMENTS_KEY);
        add(list, track, TRACK_KEY);
        add(list, genre, GENRE_KEY);
        add(list, bitrate, BITRATE_KEY);
        add(list, length, SECONDS_KEY);
        add(list, license, LICENSE_KEY);
        add(list, licensetype, LICENSE_TYPE_KEY);
        return list;
    }
    
    private void add(List list, String value, String key) {
        if(isValid(value))
            list.add(new NameValue(key, value.trim()));
    }
    
    private void add(List list, int value, String key) {
        if(isValid(value))
            list.add(new NameValue(key, "" + value));
    }
    
    private boolean isValid(String s) {
        return s != null && !s.trim().equals("");
    }
    
    private boolean isValid(int i) {
        return i >= 0;
    }
    
    /**
     * Appends the key/value & a "\" to the string buffer.
     */
    protected void appendStrings(String key, String value, StringBuffer appendTo) {
        appendTo.append(key);
        appendTo.append(value);
        appendTo.append("\"");
    }

	/**
	 * Walks back through the byte array to trim off null characters and
	 * spaces.  A helper for read(...) above.
	 * @return the number of bytes with nulls and spaces trimmed.
	 */
	protected int getTrimmedLength(byte[] bytes, int includedLength) {
	    int i;
	    for(i = includedLength - 1;
	        (i >= 0) && ((bytes[i] == 0) || (bytes[i] == 32));
	        i--);
	    //replace the nulls with spaces in the array upto i
	    for(int j=0; j<=i; j++) 
	        if(bytes[j]==0)
	            bytes[j]=(byte)32;
	    return i + 1;
	}
	
    /**
     * Determines whether a LimeXMLDocument was corrupted by
     * ID3Editor in the past.
     */
    public static boolean isCorrupted(LimeXMLDocument doc) {
        if(!schemaURI.equals(doc.getSchemaURI()))
            return false;

        Set existing = doc.getNameValueSet();
        for(Iterator i = existing.iterator(); i.hasNext(); ) {
            Map.Entry entry = (Map.Entry)i.next();
            final String name = (String)entry.getKey();
            String value = (String)entry.getValue();
            // album & artist were the corrupted fields ...
            if( name.equals(ALBUM_KEY) || name.equals(ARTIST_KEY) ) {
                if( value.length() == 30 ) {
                    // if there is a value in the 29th char, but not
                    // in the 28th, it's corrupted. 
                    if( value.charAt(29) != ' ' && value.charAt(28) == ' ' )
                        return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * Creates a new LimeXMLDocument without corruption.
     */
    public static LimeXMLDocument fixCorruption(LimeXMLDocument oldDoc) {
        Set existing = oldDoc.getNameValueSet();
        List info = new ArrayList(existing.size());
        for(Iterator i = existing.iterator(); i.hasNext(); ) {
            Map.Entry entry = (Map.Entry)i.next();
            final String name = (String)entry.getKey();
            String value = (String)entry.getValue();
            // album & artist were the corrupted fields ...
            if( name.equals(ALBUM_KEY) || name.equals(ARTIST_KEY) ) {
                if( value.length() == 30 ) {
                    // if there is a value in the 29th char, but not
                    // in the 28th, it's corrupted erase & trim.
                    if( value.charAt(29) != ' ' && value.charAt(28) == ' ' )
                        value = value.substring(0, 29).trim();
                }
            }
            info.add(new NameValue(name, value));
        }
        return new LimeXMLDocument(info, oldDoc.getSchemaURI());
    }

    public static boolean isNonLimeAudioField(String fieldName) {
        return !fieldName.equals(TRACK_KEY) &&
               !fieldName.equals(ARTIST_KEY) &&
               !fieldName.equals(ALBUM_KEY) &&
               !fieldName.equals(TITLE_KEY) &&
               !fieldName.equals(GENRE_KEY) &&
               !fieldName.equals(YEAR_KEY) &&
               !fieldName.equals(COMMENTS_KEY) &&
               !fieldName.equals(BITRATE_KEY) &&
               !fieldName.equals(SECONDS_KEY) &&
               !fieldName.equals(LICENSE_KEY) &&
               !fieldName.equals(PRICE_KEY) &&
               !fieldName.equals(LICENSE_TYPE_KEY)
               ;
    }
    
}
