/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2005 Bull S.A.
 * Contact: jonas-team@objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * Initial developer(s): Brice Ruzand
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package emb.sample.session;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.ejb.FinderException;
import javax.ejb.RemoveException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.emb.Media;
import javax.emb.MediaEntityLocal;
import javax.emb.MediaEntityLocalHome;
import javax.emb.MediaException;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import emb.sample.MediaSampleException;

/**
 * This is an example of Session Bean, statefull, and synchronized.
 *
 * @author JOnAS team
 */
public class MediaSampleSessionBean implements SessionBean {

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 2549701184925498878L;

    // =======================================================================
    // Refs
    // =======================================================================

    /**
     * Media Enotity Bean Home reference
     */
    private static final String MEB_HOME_REF = "java:comp/env/ejb/emb/MediaEntity";

    /**
     * Reference on the env entry isCopyAllowed
     */
    public static final String IS_COPY_ALLOWED_REF = "java:comp/env/ejb/embSample/isCopyAllowed";

    /**
     * Reference on the env entry maxMediaPerSession
     */
    public static final String MAX_MEDIA_PER_SESSION_REF = "java:comp/env/ejb/embSample/maxMediaPerSession";

    // =======================================================================
    // Fields
    // =======================================================================

    /**
     * cache of the env value
     */
    private Boolean isCopyAllowed = null;

    /**
     * cache of the env value
     */
    private Integer maxMediaPerSession = null;

    /**
     * list containing primary key of all generated media, use to remove them
     */
    private final List mediaListAllCreated = new LinkedList();

    /**
     * list containing primary key of all media standart media that are beeing
     * showed
     */
    private final List mediaListStdShow = new LinkedList();

    /**
     * list containing primary key of all that have been upload by user
     */
    private final List mediaListUpload = new LinkedList();

    /**
     * Map containing name and primary key of some media
     */
    private final Map mediaRegistredName = new HashMap();

    // =======================================================================
    // Medthods
    // =======================================================================

    /**
     * There must be one ejbCreate() method per create() method on the Home
     * interface, and with the same signature.
     */
    public void ejbCreate() {

        // in case we are outside transactions
    }

    /**
     * Create a new MediaEntity from a stream
     *
     * @param contentStream content of the MediaEntity
     * @param name name of the MediaEntity
     * @param description description of the MediaEntity
     * @param status is ones of {@link MediaSampleSessionLocal#DONT_SHOW},
     *            {@link MediaSampleSessionLocal#SHOW_IN_LIST},
     *            {@link MediaSampleSessionLocal#USER_ADDED}
     * @param registeredName name to acces it with
     *            {@link #getMediaFromRegistredName(String)}
     * @return MediaEntity
     * @throws MediaSampleException if the mediaEntity cannot be create
     */
public MediaEntityLocal createMediaEntity(InputStream contentStream, String name, String description, byte status,
            String registeredName) throws MediaSampleException {

        // check maxmedia
        checkMaxMedia();

        MediaEntityLocal meb = null;
        try {

            meb = getMebHome().create();
            meb.setName(name);

            if (!meb.getFormat().isEmbedded()) {
                throw new MediaSampleException("A not embedded media have to be imported and not upload in order to import its children too.");
            }

            meb.setContent(contentStream);
            meb.setDescription(description);



            try {
                // publish proxy
                Media proxy = meb.getProxy();
                getMebHome().publishContent(proxy, MediaEntityLocalHome.TRANSFER_TYPE_BURST, null);
            } catch (MediaException e) {
                // do nothing
            }

            // store the key
            addMedia(meb, status);

            if (registeredName != null) {
                mediaRegistredName.put(registeredName, meb.getPrimaryKey());
            }

            return meb;

        } catch (Exception e) {
            if (meb != null) {
                try {
                    meb.remove();
                } catch (Throwable e1) {
                    // do nothing
                }
            }
            throw new MediaSampleException("Unable to create Media : " + name + " : " + e, e);
        }
    }
    /**
     * Create a new MediaEntity from an URL
     *
     * @param urlToImport of the media to import
     * @param description description of the MediaEntity
     * @param status is ones of {@link MediaSampleSessionLocal#DONT_SHOW},
     *            {@link MediaSampleSessionLocal#SHOW_IN_LIST},
     *            {@link MediaSampleSessionLocal#USER_ADDED}
     * @return MediaEntity
     * @throws MediaSampleException if the mediaEntity cannot be create
     */
    public MediaEntityLocal createMediaEntity(URL urlToImport, String description, byte status)
            throws MediaSampleException {

        // check maxmedia
        checkMaxMedia();

        MediaEntityLocal meb = null;
        try {

            MediaEntityLocal[] mebs = getMebHome().importMedia(new URL[] {urlToImport}, null);

            if (mebs.length > 0 && mebs[0] != null) {

                meb = mebs[0];
                meb.setDescription(description);

                try {
                    // publish proxy
                    Media proxy = meb.getProxy();
                    getMebHome().publishContent(proxy, MediaEntityLocalHome.TRANSFER_TYPE_BURST, null);
                } catch (MediaException e) {
                    // do nothing
                }

                // store the key
                addMedia(meb, status);

                return meb;
            }
            throw new MediaSampleException("Unable to import the media");
        } catch (Exception e) {
            if (meb != null) {
                // totdo use super remover
                try {
                    meb.remove();
                } catch (Throwable e1) {
                    // do nothing
                }
            }
            throw new MediaSampleException("Unable to import Media : " + urlToImport + " : " + e, e);
        }
    }

    /**
     * Create a new MediaEntity from a Media
     *
     * @param media to copy
     * @return MediaEntity
     * @throws MediaSampleException if the mediaEntity cannot be create
     */
    public MediaEntityLocal copyMediaEntity(Media media) throws MediaSampleException {

        if (!getCopyAllowed() && media instanceof MediaEntityLocal) {
            return (MediaEntityLocal) media;
        }

        // check maxmedia
        checkMaxMedia();

        try {

            MediaEntityLocal meb = getMebHome().create();

            // store the key
            addMedia(meb, MediaSampleSessionLocal.DONT_SHOW);

            meb.setName(media.getName());
            meb.setContent(media.getContent());
            meb.setMimeType(media.getMimeType());
            meb.setChildren(meb.getChildren());

            return meb;

        } catch (Exception e) {
            throw new MediaSampleException("Unable to copy Media", e);
        }
    }

    /**
     * Get media list (only status {@link MediaSampleSessionLocal#SHOW_IN_LIST},
     * {@link MediaSampleSessionLocal#USER_ADDED}
     *
     * @return MediaEntity array
     * @throws MediaSampleException if media cannot be retrive
     */

    public MediaEntityLocal[] getMediaList() throws MediaSampleException {

        try {
            List mebs = new ArrayList(mediaListUpload.size() + mediaListStdShow.size());
            for (Iterator i = mediaListUpload.iterator(); i.hasNext();) {
                String primaryKey = (String) i.next();
                try {
                    MediaEntityLocal meb = getMebHome().findByPrimaryKey(primaryKey);
                    mebs.add(meb);
                } catch (FinderException e) {
                    // do nothing
                }
            }
            for (Iterator i = mediaListStdShow.iterator(); i.hasNext();) {
                String primaryKey = (String) i.next();
                try {
                    MediaEntityLocal meb = getMebHome().findByPrimaryKey(primaryKey);
                    mebs.add(meb);
                } catch (FinderException e) {
                    // do nothing
                }
            }

            return (MediaEntityLocal[]) mebs.toArray(new MediaEntityLocal[mebs.size()]);

        } catch (NamingException e) {
            throw new MediaSampleException("Unable to get MediaList due to " + e, e);
        }

    }

    /**
     * Get media from its premary key
     *
     * @param primaryKey the primary key of the wanted media
     * @return MediaEntity
     * @throws MediaSampleException if the mediaEntity cannot be retrive
     */
    public MediaEntityLocal getMediaFromPrimaryKey(String primaryKey) throws MediaSampleException {
        try {
            MediaEntityLocal meb = getMebHome().findByPrimaryKey(primaryKey);
            return meb;
        } catch (FinderException e) {
            throw new MediaSampleException("Media with this primary key does not exist : " + primaryKey, e);
        } catch (NamingException e) {
            throw new MediaSampleException("Unable to get Media due to " + e, e);
        }
    }

    /**
     * Get media from registered name
     *
     * @param registeredName the registeredName of the wanted media
     * @return MediaEntity
     * @throws MediaSampleException if the mediaEntity cannot be retrive
     */
    public MediaEntityLocal getMediaFromRegistredName(String registeredName) throws MediaSampleException {

        String primaryKey = (String) mediaRegistredName.get(registeredName);
        if (primaryKey != null) {
            return getMediaFromPrimaryKey(primaryKey);
        }
        throw new MediaSampleException("unable to find Media with registered name : " + registeredName);

    }

    /**
     * Remove default media to reinit the database, except uploaded media
     */
    public void removeDefaultMedia() {

        // copy the primary list
        String[] primaryKeyToRemove = (String[]) mediaListAllCreated.toArray(new String[mediaListAllCreated.size()]);

        // remove al creating media during this session
        for (int i = 0; i < primaryKeyToRemove.length; i++) {

            if (!mediaListUpload.contains(primaryKeyToRemove[i])) {

                try {
                    MediaEntityLocal meb = getMediaFromPrimaryKey(primaryKeyToRemove[i]);
                    removeMedia(meb);
                } catch (MediaSampleException e) {
                    // do nothing
                }
            }
        }
    }

    // =======================================================================
    // Internals
    // =======================================================================

    /**
     * Add meb to the right list
     *
     * @param meb the media to add
     * @param status where the medi is added, is ones of {@link #DONT_SHOW},
     *            {@link #SHOW_IN_LIST}, {@link #USER_ADDED}
     */
    private synchronized void addMedia(MediaEntityLocal meb, byte status) {

        mediaListAllCreated.add(meb.getPrimaryKey());
        if (status == MediaSampleSessionLocal.SHOW_IN_LIST) {
            mediaListStdShow.add(meb.getPrimaryKey());
        } else if (status == MediaSampleSessionLocal.USER_ADDED) {
            mediaListUpload.add(meb.getPrimaryKey());
        }

    }

    /**
     * Remove media from all media list
     *
     * @param meb the media to add
     */
    private synchronized void removeMedia(MediaEntityLocal meb) {

        recusiveRemoveMedia(meb);

        mediaListAllCreated.remove(meb.getPrimaryKey());
        mediaListStdShow.remove(meb.getPrimaryKey());
        mediaListUpload.remove(meb.getPrimaryKey());

    }

    /**
     * Remove recurcively all preview version and children
     *
     * @param meb the media entity bean
     */
    private void recusiveRemoveMedia(MediaEntityLocal meb) {
        if (meb != null) {

            // recurcive call for children

            MediaEntityLocal[] children = meb.getChildren();
            for (int i = 0; i < children.length; i++) {
                recusiveRemoveMedia(children[i]);
            }

            // recurcive call for preview version
            recusiveRemoveMedia(meb.getPreviousVersion());

            try {
                meb.remove();
            } catch (RemoveException e) {
                // do nothing
            }
        }
    }

    /**
     * Get the iscopy allow
     *
     * @return if copy is allow
     */
    private boolean getCopyAllowed() {

        Boolean cache = isCopyAllowed;

        if (cache == null) {
            try {
                cache = (Boolean) (new InitialContext()).lookup(IS_COPY_ALLOWED_REF);
                isCopyAllowed = cache;

            } catch (NamingException e) {
                return true;
            }
        }
        return cache.booleanValue();
    }

    /**
     * Get MaxMediaPerSession
     *
     * @return maxmedia per session
     */
    private int getMaxMediaPerSession() {

        Integer cache = maxMediaPerSession;

        if (cache == null) {
            try {
                cache = (Integer) (new InitialContext()).lookup(MAX_MEDIA_PER_SESSION_REF);
                maxMediaPerSession = cache;

            } catch (NamingException e) {
                return 0;
            }
        }
        return cache.intValue();
    }

    /**
     * test if MaxMediaPerSession has been reach
     *
     * @throws MediaSampleException if MaxMediaPerSession has been reach
     */
    private void checkMaxMedia() throws MediaSampleException {

        if (!(getMaxMediaPerSession() == 0)) {
            if (mediaListAllCreated.size() >= getMaxMediaPerSession() - 1) {
                throw new MediaSampleException(
                        "You have reach the maximum number per session. You cannot create more media.");
            }
        }

    }

    // =======================================================================
    // TOOLS
    // =======================================================================

    /**
     * cached Home
     */
    private MediaEntityLocalHome mebHome = null;

    /**
     * Provide a cached Home access
     *
     * @return MediaEntityLocalHome
     * @throws NamingException in case MediaEntityLocalHome is not found
     */
    private MediaEntityLocalHome getMebHome() throws NamingException {
        if (mebHome == null) {
            mebHome = (MediaEntityLocalHome) new InitialContext().lookup(MEB_HOME_REF);
        }
        return mebHome;
    }

    // =======================================================================
    // EJB contract
    // =======================================================================

    /**
     * Session Context
     */
    private SessionContext mySessionCtx;

    /**
     * getSessionContext
     *
     * @return SessionContext
     */
    public SessionContext getSessionContext() {
        return mySessionCtx;
    }

    /**
     * @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
     * @inheritDoc
     */
    public void setSessionContext(SessionContext ctx) {
        mySessionCtx = ctx;
    }

    /**
     * @see javax.ejb.SessionBean#ejbActivate()
     * @inheritDoc
     */
    public void ejbActivate() {
        // do nothing
    }

    /**
     * @see javax.ejb.SessionBean#ejbPassivate()
     * @inheritDoc
     */
    public void ejbPassivate() {
        // do nothing
    }

    /**
     * @see javax.ejb.SessionBean#ejbRemove()
     * @inheritDoc
     */
    public void ejbRemove() {

        // copy the primary list
        String[] primaryKeyToRemove = (String[]) mediaListAllCreated.toArray(new String[mediaListAllCreated.size()]);

        // remove al creating media during this session
        for (int i = 0; i < primaryKeyToRemove.length; i++) {
            try {
                MediaEntityLocal meb = getMediaFromPrimaryKey(primaryKeyToRemove[i]);
                removeMedia(meb);
            } catch (MediaSampleException e) {
                // do nothing
            }
        }

    }

}