package com.limegroup.gnutella.updates;

import java.io.IOException;
import java.io.StringReader;

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.limegroup.gnutella.Assert;
import com.limegroup.gnutella.settings.ApplicationSettings;
import com.limegroup.gnutella.xml.LimeXMLUtils;

public class UpdateFileParser {
    
    //initilaize this once per class. 
    private static DOMParser parser = new DOMParser();
    
    /**
     * For the first release the only value we need is the new version.
     * As we add more data to the update file, we can have the structure be a 
     * hashmap, and add getter and setter methods.
     */
    private String newVersion=null;
    
    private String updateMessage=null;

    private boolean usingLocale = true;

    private long timestamp;

    public UpdateFileParser(String xml) throws SAXException, IOException {
        if(xml==null || xml.equals(""))
            throw new SAXException("xml is null or empty string");
        timestamp = -1l;
        InputSource inputSource = new InputSource(new StringReader(xml));
        Document d = null;
        synchronized(this.parser) {
            parser.parse(inputSource);
            d = parser.getDocument();
        }
        if(d==null)//problems parsing?
            throw new SAXException("document is null");
        populateValues(d);
    }
    
    private void populateValues(Document doc) throws IOException {
        Element docElement = doc.getDocumentElement();
        //Note: We are assuming that the XML structure will have no attributes.
        //only child elements. We can make this assumption because we are the
        //XML is generated right here in house at LimeWire.
        NodeList children = docElement.getChildNodes();
        int len = children.getLength();
        for(int i=0; i<len; i++) { //parse the nodes.
            Node node = children.item(i);
            String name = node.getNodeName().toLowerCase().trim();
            if(name.equals("version")) 
                newVersion = LimeXMLUtils.getText(node.getChildNodes());
            else if(name.equals("message"))
                updateMessage = getLocaleSpecificMessage(node);
            else if(name.equals("timestamp")) {
                try {
                    timestamp = 
                    Long.parseLong(LimeXMLUtils.getText(node.getChildNodes()));
                } catch (NumberFormatException nfx) {
                    throw new IOException();
                }
            }
        }
    }
    
    /**
     * Looks at the child nodes of node, and tries to find the value of the
     * message based on the language specified in limewire.props
     * If there is no string for the message in that langauge, returns the
     * string in English.
     * <p>
     * If we were not able to find the string as per the language preference,
     * we set the value of usingLocale to false. 
     */
    private String getLocaleSpecificMessage(Node node) {
        String locale = ApplicationSettings.LANGUAGE.getValue().toLowerCase();
        String defaultMessage=null;
        String localeMessage=null;
        NodeList children = node.getChildNodes();
        int len = children.getLength();
        for(int i=0 ; i<len ; i++) {
            Node n = children.item(i);
            String name = n.getNodeName().toLowerCase().trim();
            if(name.equals("en"))
                defaultMessage = LimeXMLUtils.getText(n.getChildNodes());
            else if(name.equals(locale)) 
                localeMessage = LimeXMLUtils.getText(n.getChildNodes());
        }
        Assert.that(defaultMessage!=null,"bad xml file signed by LimeWire");
        //check if we should send back en or locale
        if(locale.equals("en"))
            return defaultMessage;
        if(localeMessage!=null)  //we have a proper string to return
            return localeMessage;
        usingLocale = false;
        return defaultMessage;        
    }

    /**
     * @return the value of new version we parsed out of XML. Can return null.
     */ 
    public String getVersion() {
        return newVersion;
    }
    
    public long getTimestamp() {
        return timestamp;
    }

    /**
     * @return true if the message was picked up as per the locale, else false
     */
    public boolean usesLocale() {
        return usingLocale;
    }
    
    /**
     * @return the message to show the user.
     */
    public String getMessage() {
        return updateMessage;
    }
}
