/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
 */
package fr.gouv.culture.sdx.document;

import fr.gouv.culture.sdx.documentbase.DefaultIDGenerator;
import fr.gouv.culture.sdx.documentbase.IDGenerator;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.repository.Repository;
import fr.gouv.culture.sdx.repository.RepositoryConnection;
import fr.gouv.culture.sdx.utils.AbstractSdxObject;
import org.xml.sax.InputSource;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

/**
 * An abstract representation of a document.
 *
 * <p>
 * Implements all methods from the Document interface.
 */
public abstract class AbstractDocument extends AbstractSdxObject implements Document {


    /** Document's content origin is (still) unknown. */
    private final static int FROM_UNKNOWN = -1;

    /** Document's content comes from a file. */
    private final static int FROM_FILE = 0;

    /** Document's content comes from a URL. */
    private final static int FROM_URL = 1;

    /** Document's content comes from a byte array. */
    private final static int FROM_BYTE_ARRAY = 2;

    /** Document's content comes from a InputStream.*/
    private final static int FROM_INPUT_STREAM = 3;

    /** Where the document's content comes from. */
    private int origin = FROM_UNKNOWN;

    /** The document's content as a file. */
    private File cFile = null;

    /** The document's content as a URL. */
    private URL cURL = null;

    /** The document's content as an InputStream. */
    private InputStream cInputStream = null;

    /** The document's content as a byte array. */
    private byte[] cByteArray = null;

    /** The preferred filename.
     * The filename that will be used to save the document.
     * This filename should conform to platform specific
     * requirements.
     */
    private String preferredFilename;

    /**The id of the repository in which the document should be stored*/
    protected String storeRepo = "";

    /** The mime type of the document. */
    protected String mimeType;

    /**The id generator for the document*/
    protected IDGenerator idGenerator = new DefaultIDGenerator();//defaulted

    /**Prefix for generated id's*/
    protected String idPrefix = "";

    /**Suffix for generated id's*/
    protected String idSuffix = "";


    /** Opens a stream for reading the document's content. */
    public InputStream openStream() throws SDXException {
        switch (origin) {
            case FROM_FILE:
                try {
                    return new FileInputStream(cFile);
                } catch (FileNotFoundException e) {
                    String[] args = new String[2];
                    args[0] = getId();
                    args[1] = cFile.getAbsolutePath();
                    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_OPEN_STREAM, args, e);
                }
            case FROM_URL:
                try {
                    if (cURL != null)
                        return cURL.openStream();
                    else
                        return null;
                } catch (IOException e) {
                    String[] args = new String[2];
                    args[0] = getId();
                    args[1] = cURL.toExternalForm();
                    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_OPEN_STREAM, args, e);
                }
            case FROM_BYTE_ARRAY:
                return new ByteArrayInputStream(cByteArray);
            case FROM_INPUT_STREAM:
                return cInputStream;
            default:
                String[] args = new String[2];
                args[0] = getId();
                args[1] = "inputStream";
                throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_OPEN_STREAM, args, null);
        }
    }

    /**Returns a SAX InputSource for this document*/
    protected InputSource getInputSource() throws SDXException {
        InputSource source = null;
        switch (origin) {
            case FROM_FILE:
                try {
                    source = new InputSource(cFile.toURI().toURL().toString());
                } catch (MalformedURLException e) {
                    String[] args = new String[2];
                    args[0] = cFile.getAbsolutePath();
                    args[1] = e.getMessage();
                    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_INPUTSOURCE, args, e);
                }
                break;
            case FROM_URL:
                try {
                    source = new InputSource(cURL.toExternalForm());
                } catch (Exception e) {
                    String[] args = new String[2];
                    args[0] = cURL.toExternalForm();
                    args[1] = e.getMessage();
                    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_INPUTSOURCE, args, e);
                }
                break;
            case FROM_INPUT_STREAM:
                source = new InputSource(openStream());
                break;
            case FROM_BYTE_ARRAY:
                source = new InputSource(openStream());
                break;
            default:
                source = new InputSource(openStream());
                break;

        }
        return source;

    }

    /**Returns the mimeType field (A String) for this document*/
    public String getMimeType() {
        return mimeType;
    }

    /**Sets the mimeType field (A String) for this document*/
    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

    /** Sets the content from a memory representation of the raw data. */
    public void setContent(byte[] b) {
        cByteArray = b;
        resetContent(FROM_BYTE_ARRAY);
    }

    /** Sets the content from a file. */
    public void setContent(File f) {
        cFile = f;
        resetContent(FROM_FILE);
    }

    /** Sets the content from a URL. */
    public void setContent(URL u) {
        cURL = u;
        resetContent(FROM_URL);
    }

    public void setContent(InputStream is) {
        cInputStream = is;
        resetContent(FROM_INPUT_STREAM);
    }

    /** Saves the document within a repository.
     *
     * Use with caution, we will use this method in the future
     * but its ramifications are not known yet.
     * @param   r       The repository where to save the document.
     */
    //why is this needed here? i believe frederic may be able to answer this question.-rbp
    public void save(Repository r) throws SDXException {
        RepositoryConnection c = r.getConnection();
        r.add(this, c);
        c.commit();
        r.releaseConnection(c);
    }

    /** Unsets the contents object. */
    private void resetContent(int newOrigin) {
        switch (newOrigin) {
            case FROM_FILE:
                break;
            case FROM_URL:
                break;
            case FROM_BYTE_ARRAY:
                break;
            case FROM_INPUT_STREAM:
                break;
            default:
                cFile = null;
                cURL = null;
                cByteArray = null;
                cInputStream = null;
        }
        origin = newOrigin;
        setPreferredFilename();
    }

    /** Returns the length of the document. */
    public int getLength() {
        // CAN BE DIFFERENT THAN WHAT VALUE WILL BE RETURNED FROM STORAGE RESOURCE (Repository) BASED ON STORAGE ENCODING
        switch (origin) {
            case FROM_FILE:
                if (cFile != null)
                    return (int) cFile.length();
                else
                    return 0;
            case FROM_URL:
                if (cURL != null) {
                    int ret = 0;
                    try {
                        URLConnection conn = cURL.openConnection();
                        ret = conn.getContentLength();
                        conn = null;
                    } catch (IOException e) {
                        ret = 0;
                        String[] args = new String[2];
                        args[0] = cURL.toExternalForm();
                        args[1] = e.getMessage();
                        //we are going to log this but return zero
                        new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_LENGTH, args, e);
                        ret = 0;
                    }
                    return ret;
                } else
                    return 0;
            case FROM_BYTE_ARRAY:
                if (cByteArray != null)
                    return cByteArray.length;
                else
                    return 0;
            case FROM_INPUT_STREAM:
                if (cInputStream != null) {
                    try {
                        //this returns a int indicating the number of bytes
                        return cInputStream.available();
                    } catch (IOException e) {
                        String[] args = new String[2];
                        args[0] = cInputStream.toString();
                        args[1] = e.getMessage();
                        //return zero after logging the exception
                        new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_INPUTSOURCE, args, e);
                        return 0;
                    }

                } else
                    return 0;
            default:
                return 0;   // Should not happen...
                //Throw an exception there ? -pb
        }
    }

    /** Returns a preferred filename for this document. */
    public String getPreferredFilename() {
        return this.preferredFilename;
    }

    /**
     * Sets the preferred filename according to the source.
     */
    protected void setPreferredFilename() {
        String filename = null;
        switch (origin) {
            case FROM_FILE:
                if (cFile != null) filename = cFile.getName();
                break;
            case FROM_URL:
                if (cURL != null) filename = cURL.getFile().substring(cURL.getFile().lastIndexOf("/") + 1);
                break;
        }
        //TODO?:what about other cases
        setPreferredFilename(filename);
    }

    /** Returns a URL for this document, null if no URL can be given. */
    public URL getURL() throws SDXException {
        switch (origin) {
            case FROM_URL:
                return cURL;
            case FROM_FILE:
                if (cFile != null) {
                    try {
						if( cURL!=null ) return cURL;
                        else return cFile.toURI().toURL();
                    } catch (MalformedURLException e) {
                        //return null;
                        String[] args = new String[2];
                        args[0] = getId();
                        args[1] = cFile.getAbsolutePath();
                        throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_BUILD_DOC_URL, args, e);
                    }
                } else
                    return null;
            default:
                if (cURL != null) return cURL;
                return null;

        }
    }

    /** Sets the URL of this document. */
    public void setURL(URL url) {
        this.cURL = url;
    }

    /**
     * Sets the preferred filename.
     *
     * @param   filename        The filename (may be null).
     */
    public void setPreferredFilename(String filename) {
        this.preferredFilename = filename;
    }

    public void setIdGenerator(IDGenerator idGen) {
        setIdGenerator(idGen, null, null);
    }

    public void setIdGenerator(IDGenerator idGen, String idPrefix, String idSuffix) {
        this.idGenerator = idGen;
        this.idPrefix = idPrefix;
        this.idSuffix = idSuffix;
    }



    /**
     * Sets the URL according to the source.
     */
/*
	protected void setURL() throws SDXException {
	    switch (this.origin) {
		case FROM_FILE:
		    if (cFile != null)
			try {
			    setURL(cFile.toURL());
			} catch (MalformedURLException e) {
			    setURL(null);
			    //more detailed message is necessary here based upon the above code
			    //1) create "arg" based upon error, and add to "args"
			    //2) create create new error code
			    String[] args = new String[1];
			    args[0] = e.getMessage();
			    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GENERIC, args, e);
			}
		    else
			setURL(null);
		    break;
		case FROM_URL:
		    setURL(cURL);
		    break;
		default:
		    setURL(null);
		    break;
	    }
	}
 */

    /*Returns the id of the repository in which the document should be stored*/
    public String getRepositoryForStorage() {
        return this.storeRepo;
    }

    /*Sets the id of the repository in which the document should be stored*/
    public void setRepositoryForStorage(String repoId) {
        this.storeRepo = repoId;
    }

    protected String getClassNameSuffix() {
        return Document.CLASS_NAME_SUFFIX;
    }

    /* (non-Javadoc)
	 * @see fr.gouv.culture.sdx.utils.AbstractSdxObject#initToSax()
	 */
	protected boolean initToSax() {
		return true;
	}

    /**Init the LinkedHashMap _xmlizable_volatile_objects with the objects in order to describ them in XML
	 * Some objects need to be refresh each time a toSAX is called*/
	protected void initVolatileObjectsToSax() {}

}
