/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.content.packager;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;

import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.crosswalk.CrosswalkException;
import org.dspace.content.crosswalk.MetadataValidationException;
import org.dspace.core.Context;
import org.dspace.core.Constants;
import org.dspace.app.mediafilter.MediaFilter;
import org.dspace.core.factory.CoreServiceFactory;
import org.dspace.core.service.PluginService;

import org.jdom.Element;

/**
 * Packager plugin to ingest a
 * METS (Metadata Encoding and Transmission Standard) package
 * that conforms to the DSpace METS SIP (Submission Information Package) Profile.
 * See <a href="http://www.loc.gov/standards/mets/">http://www.loc.gov/standards/mets/</a>
 * for more information on METS, and
 * <a href="http://www.dspace.org/standards/METS/SIP/profilev0p9p1/metssipv0p9p1.pdf">
 * http://www.dspace.org/standards/METS/SIP/profilev0p9p1/metssipv0p9p1.pdf</a>
 * (or a similar file in the /standards/METS/SIP resource hierarchy)
 * for more information about the DSpace METS SIP profile.
 *
 * @author Larry Stone
 * @author Tim Donohue
 * @version $Revision$
 * @see org.dspace.content.packager.METSManifest
 * @see AbstractMETSIngester
 * @see AbstractPackageIngester
 * @see PackageIngester
 */
public class DSpaceMETSIngester
       extends AbstractMETSIngester
{
    // first part of required mets@PROFILE value
    protected static final String PROFILE_START = "DSpace METS SIP Profile";

    // just check the profile name.
    @Override
    void checkManifest(METSManifest manifest)
        throws MetadataValidationException
    {
        String profile = manifest.getProfile();
        if (profile == null)
        {
            throw new MetadataValidationException("Cannot accept METS with no PROFILE attribute!");
        }
        else if (!profile.startsWith(PROFILE_START))
        {
            throw new MetadataValidationException("METS has unacceptable PROFILE value, profile=" + profile);
        }
    }

    /**
     * Choose DMD section(s) to crosswalk.
     * <p>
     * The algorithm is:<br>
     * 1. Use whatever the <code>dmd</code> parameter specifies as the primary DMD.<br>
     * 2. If (1) is unspecified, find MODS (preferably) or DC as primary DMD.<br>
     * 3. If (1) or (2) succeeds, crosswalk it and ignore all other DMDs with
     *    same GROUPID<br>
     * 4. Crosswalk remaining DMDs not eliminated already.
     * @throws CrosswalkException if crosswalk error
     * @throws PackageValidationException if validation error
     * @throws IOException if IO error
     * @throws SQLException if database error
     * @throws AuthorizeException if authorization error
     */
    @Override
    public void crosswalkObjectDmd(Context context, DSpaceObject dso,
                              METSManifest manifest,
                              MdrefManager callback,
                              Element dmds[], PackageParameters params)
        throws CrosswalkException, PackageValidationException,
               AuthorizeException, SQLException, IOException
    {
        int found = -1;

        // Check to see what dmdSec the user specified in the 'dmd' parameter
        String userDmd = null;
        if (params != null)
        {
            userDmd = params.getProperty("dmd");
        }
        if (userDmd != null && userDmd.length() > 0)
        {
            for (int i = 0; i < dmds.length; ++i)
            {
                if (userDmd.equalsIgnoreCase(manifest.getMdType(dmds[i])))
                {
                    found = i;
                }
            }
        }

        // MODS is preferred, if nothing specified by user
        if (found == -1)
        {
            for (int i = 0; i < dmds.length; ++i)
            {
                //NOTE: METS standard actually says this should be MODS (all uppercase). But,
                // just in case, we're going to be a bit more forgiving.
                if ("MODS".equalsIgnoreCase(manifest.getMdType(dmds[i])))
                {
                    found = i;
                }
            }
        }

        // DC acceptable if no MODS
        if (found == -1)
        {
            for (int i = 0; i < dmds.length; ++i)
            {
                //NOTE: METS standard actually says this should be DC (all uppercase). But,
                // just in case, we're going to be a bit more forgiving.
                if ("DC".equalsIgnoreCase(manifest.getMdType(dmds[i])))
                {
                    found = i;
                }
            }
        }

        String groupID = null;
        if (found >= 0)
        {
            manifest.crosswalkItemDmd(context, params, dso, dmds[found], callback);
            groupID = dmds[found].getAttributeValue("GROUPID");

            if (groupID != null)
            {
                for (int i = 0; i < dmds.length; ++i)
                {
                    String g = dmds[i].getAttributeValue("GROUPID");
                    if (g != null && !g.equals(groupID))
                    {
                        manifest.crosswalkItemDmd(context, params, dso, dmds[i], callback);
                    }
                }
            }
        }
        else
        {
            // otherwise take the first.  Don't xwalk more than one because
            // each xwalk _adds_ metadata, and could add duplicate fields.
            if (dmds.length > 0)
            {
                manifest.crosswalkItemDmd(context, params, dso, dmds[0], callback);
            }
        }
    }


    /**
     * Policy:  For DSpace deposit license, take deposit license
     * supplied by explicit argument first, else use collection's
     * default deposit license.
     * For Creative Commons, look for a rightsMd containing a CC license.
     * @throws PackageValidationException if validation error
     * @throws IOException if IO error
     * @throws SQLException if database error
     * @throws AuthorizeException if authorization error
     */
    @Override
    public void addLicense(Context context, Item item, String license,
                                    Collection collection, PackageParameters params)
        throws PackageValidationException,
               AuthorizeException, SQLException, IOException
    {
        if (PackageUtils.findDepositLicense(context, item) == null)
        {
            PackageUtils.addDepositLicense(context, license, item, collection);
        }
    }

    @Override
    public void finishObject(Context context, DSpaceObject dso,
                             PackageParameters params)
        throws PackageValidationException, CrosswalkException,
         AuthorizeException, SQLException, IOException
    {
        // nothing to do.
    }

    @Override
    public int getObjectType(METSManifest manifest)
        throws PackageValidationException
    {
        return Constants.ITEM;
    }

    // return name of derived file as if MediaFilter created it, or null
    // only needed when importing a SIP without canonical DSpace derived file naming.
    private String makeDerivedFilename(String bundleName, String origName)
    {
        PluginService pluginService = CoreServiceFactory.getInstance().getPluginService();

        // get the MediaFilter that would create this bundle:
        String mfNames[] = pluginService.getAllPluginNames(MediaFilter.class);

        for (int i = 0; i < mfNames.length; ++i)
        {
            MediaFilter mf = (MediaFilter)pluginService.getNamedPlugin(MediaFilter.class, mfNames[i]);
            if (bundleName.equals(mf.getBundleName()))
            {
                return mf.getFilteredName(origName);
            }
        }

        return null;
    }

    /**
     * Take a second pass over files to correct names of derived files
     * (e.g. thumbnails, extracted text) to what DSpace expects:
     * @throws MetadataValidationException if validation error
     * @throws IOException if IO error
     * @throws SQLException if database error
     * @throws AuthorizeException if authorization error
     */
    @Override
    public void finishBitstream(Context context,
                                                Bitstream bs,
                                                Element mfile,
                                                METSManifest manifest,
                                                PackageParameters params)
        throws MetadataValidationException, SQLException, AuthorizeException, IOException
    {
        String bundleName = METSManifest.getBundleName(mfile);
        if (!bundleName.equals(Constants.CONTENT_BUNDLE_NAME))
        {
            String opath = manifest.getOriginalFilePath(mfile);
            if (opath != null)
            {
                // String ofileId = origFile.getAttributeValue("ID");
                // Bitstream obs = (Bitstream)fileIdToBitstream.get(ofileId);

                String newName = makeDerivedFilename(bundleName, opath);

                if (newName != null)
                {
                    //String mfileId = mfile.getAttributeValue("ID");
                    //Bitstream bs = (Bitstream)fileIdToBitstream.get(mfileId);
                    bs.setName(context, newName);
                    bitstreamService.update(context, bs);
                }
            }
        }
    }

    @Override
    public String getConfigurationName()
    {
        return "dspaceSIP";
    }


    public boolean probe(Context context, InputStream in, PackageParameters params)
    {
        throw new UnsupportedOperationException("PDF package ingester does not implement probe()");
    }

    /**
     * Returns a user help string which should describe the
     * additional valid command-line options that this packager
     * implementation will accept when using the <code>-o</code> or
     * <code>--option</code> flags with the Packager script.
     *
     * @return a string describing additional command-line options available
     * with this packager
     */
    @Override
    public String getParameterHelp()
    {
        String parentHelp = super.getParameterHelp();

        //Return superclass help info, plus the extra parameter/option that this class supports
        return parentHelp +
                "\n\n" +
                "* dmd=[dmdSecType]      " +
                   "Type of the METS <dmdSec> which should be used for primary item metadata (defaults to MODS, then DC)";
    }
}
