/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.modules.attachment.ejb;

import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.User;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.jeo.JEObjectHandler;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocal;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocalHome;
import com.queplix.core.modules.jeo.gen.AttachmentObject;
import com.queplix.core.modules.jeo.gen.AttachmentObjectHandler;
import com.queplix.core.modules.jeo.gen.AttachmentTempObject;
import com.queplix.core.modules.jeo.gen.AttachmentTempObjectHandler;
import com.queplix.core.utils.DateHelper;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.ejb.AbstractSessionEJB;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Attachement manager session EJB.
 *
 * @author [ALB] Baranov Andrey
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @version $Revision: 1.3 $ $Date: 2006/01/27 17:58:31 $
 */
public class AttachmentManagerEJB
        extends AbstractSessionEJB {

    // =============================================================== Constants

    // Empty numeric value.
    private static final byte EMPTY_NUMBER = StringHelper.EMPTY_NUMBER;

    // Empty attachments VO arrays.
    private static final AttachmentTempObject[] EMPTY_TEMP_ATTACH_ARRAY
            = new AttachmentTempObject[0];
    private static final AttachmentObject[] EMPTY_ATTACH_ARRAY
            = new AttachmentObject[0];

    // Empty attachments IDs array.
    private static final long[] EMPTY_ID_ARRAY = new long[0];

    // Max file extension length.
    private static final int EXT_LENGTH = 3;

    // ============================================================= API methods

    /**
     * Initializes bean.
     */
    public void ejbCreate() {
        INFO("Attachement EJB created - " + hashCode());
    }

    // ======================================== Temporary attachments management

    /**
     * Generates the unique process ID.
     *
     * @return attachment process ID
     */
    public long getUniqueProcessID() {
        long id = System.currentTimeMillis();
        INFO("Got unigue process ID = " + id);
        return id;
    }

    /**
     * Adds a new temporary attachement.
     *
     * @param processID current process ID
     * @param fileName  file name
     * @param fileType  file type
     * @param data      file data
     * @param ls        LogonSession object
     * @return temporary attachment ID
     */
    public long addTempAttachment(LogonSession ls, long processID,
                                  String fileName, String fileType,
                                  byte[] data) {
        return addTempAttachment(ls, processID, fileName, fileType, data,
                false);
    }

    /**
     * Adds a new temporary attachement.
     *
     * @param processID current process ID
     * @param fileName  file name
     * @param fileType  file type
     * @param data      file data
     * @param ls        LogonSession object
     * @param purge     whether to purge all existing attachments with the same process ID
     * @return temporary attachment ID
     */
    public long addTempAttachment(LogonSession ls,
                                  long processID,
                                  String fileName,
                                  String fileType,
                                  byte[] data,
                                  boolean purge) {

        // Initialization.
        long time = System.currentTimeMillis();
        User user = ls.getUser();
        JEOManagerLocal jeoManager = getJEOManager();

        // Get file extension.
        String fileExt = getFileExt(fileName, fileType);

        long tempAttachID = EMPTY_NUMBER;
        try {

            // If necessary, delete all existing attachments from the same process.
            if(purge) {
                List hnds = AttachmentTempObjectHandler.selectByProcessID(
                        jeoManager, ls, processID);
                if(hnds != null) {
                    DEBUG("Deleting " + hnds.size()
                            + " existing temp attachments...");
                    for(int i = 0; i < hnds.size(); i++) {
                        JEObjectHandler hnd = (JEObjectHandler) hnds.get(i);
                        hnd.remove();
                        hnd.commit();
                    }
                }
            }

            // Add a new temp attachement.
            JEObjectHandler hnd = jeoManager.create(ls,
                    AttachmentTempObjectHandler.class);
            AttachmentTempObject obj = (AttachmentTempObject) hnd.getJEObject();

            obj.setProcess_id(processID);
            obj.setFilename(fileName);
            obj.setFiletype(fileType);
            obj.setFileext(fileExt);
            obj.setData(data);
            obj.setUser_id(user.getUserID());
            obj.setUser_type_id(user.getAuthenticationType());
            obj.setCreated(DateHelper.getNowDate());

            int rows = hnd.commit();
            INFO("Temp Attachment commit: " + rows + " record(s)");
            if(rows == 0) {
                return EMPTY_NUMBER;
            }
            tempAttachID = obj.getTemp_attach_id();

        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Temp attachment added (ID = " + tempAttachID + ") at " + time
                    + " ms.");
        }
        return tempAttachID;

    }

    /**
     * Copies attachments with process ID <code>processID</code> to Temp
     * with new process ID <code>tempProcessID</code>.
     *
     * @param ls            LogonSession
     * @param processID     attachments process ID
     * @param tempProcessID temp attachments process ID
     * @return AttachmentTempObject[]
     */
    public AttachmentTempObject[] copyAttachmentsToTemp(
            LogonSession ls,
            long processID,
            long tempProcessID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        AttachmentObject[] attachObjs = getAttachments(ls, processID);
        int size = (attachObjs == null) ? 0:attachObjs.length;

        // Clone all attachment to temp under <code>tempProcessID</code> process ID.
        List list = new ArrayList();
        try {
            for(int i = 0; i < size; i++) {
                AttachmentObject attachObj = attachObjs[i];

                // Add a new temp attachement.
                JEObjectHandler tmpAttachHnd = jeoManager.create(ls,
                        AttachmentTempObjectHandler.class);
                AttachmentTempObject tmpAttachObj
                        = (AttachmentTempObject) tmpAttachHnd.getJEObject();

                tmpAttachObj.setProcess_id(new Long(tempProcessID));
                tmpAttachObj.setFilename(attachObj.getFilename());
                tmpAttachObj.setFiletype(attachObj.getFiletype());
                tmpAttachObj.setFileext(attachObj.getFileext());
                tmpAttachObj.setData(attachObj.getData());
                tmpAttachObj.setUser_id(attachObj.getUser_id());
                tmpAttachObj.setCreated(attachObj.getCreated());

                tmpAttachHnd.commit();

                list.add(tmpAttachObj);
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Attachments to Temp copied (from process ID = " + processID +
                    " to temp process ID = " + tempProcessID + ") at " + time
                    + " ms.");
        }

        return (list.size() == 0)
                ? EMPTY_TEMP_ATTACH_ARRAY
                :(AttachmentTempObject[]) list.toArray(
                        new AttachmentTempObject[0]);

    }

    /**
     * Gets the temporary attachement by its ID.
     *
     * @param tempAttachID temporary attachment ID
     * @param ls           LogonSession object
     * @return temporary attachment JEO object, or <b>null</b> if not found
     */
    public AttachmentTempObject getTempAttachment(LogonSession ls,
                                                  long tempAttachID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Get the temp attachment.
        AttachmentTempObject obj = null;
        try {
            JEObjectHandler hnd = AttachmentTempObjectHandler.selectByPkey(
                    jeoManager, ls, tempAttachID);
            if(hnd != null) {
                obj = (AttachmentTempObject) hnd.getJEObject();
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Got temp attachment (ID = " + tempAttachID + ") at " + time
                    + " ms.");
        }
        return obj;

    }

    /**
     * Gets all the temporary attachements spawned by the same process.
     *
     * @param processID process ID
     * @param ls        LogonSession object
     * @return array of temporary attachment value objects (might be empty)
     */
    public AttachmentTempObject[] getTempAttachments(LogonSession ls,
                                                     long processID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Get temp attachments.
        List list = new ArrayList();
        try {
            List hnds = AttachmentTempObjectHandler.selectByProcessID(
                    jeoManager, ls, processID);
            if(hnds != null) {
                for(int i = 0; i < hnds.size(); i++) {
                    AttachmentTempObjectHandler hnd
                            = (AttachmentTempObjectHandler) hnds.get(i);
                    AttachmentTempObject obj = (AttachmentTempObject) hnd
                            .getJEObject();
                    DEBUG("Got temp attachment '" + obj.getFilename() + "'");
                    list.add(obj);
                }
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Got temp attachments (" + list.size()
                    + ") by the process (process ID = " + processID + ") at "
                    + time + " ms.");
        }

        return (list.size() == 0)
                ? EMPTY_TEMP_ATTACH_ARRAY
                :(AttachmentTempObject[]) list.toArray(
                        new AttachmentTempObject[0]);
    }

    /**
     * Deletes the temporary attachement.
     *
     * @param tempAttachID temporary attachment ID
     * @param ls           LogonSession object
     */
    public void deleteTempAttachment(LogonSession ls, long tempAttachID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Delete the temp attachment.
        try {
            JEObjectHandler hnd = AttachmentTempObjectHandler.selectByPkey(
                    jeoManager, ls, tempAttachID);
            if(hnd != null) {
                hnd.remove();
                hnd.commit();
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Temp attachment deleted (ID = " + tempAttachID + ") at "
                    + time + " ms.");
        }

    }

    /**
     * Deletes the temporary attachements.
     *
     * @param tempAttachIDs temporary attachment IDs array
     * @param ls            LogonSession object
     */
    public void deleteTempAttachments(LogonSession ls, long[] tempAttachIDs) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();
        int attachCount = 0;

        // Delete the temp attachments.
        DEBUG("Deleting attachments: " + StringHelper.toString(tempAttachIDs));
        for(int i = 0; i < tempAttachIDs.length; i++) {
            try {
                JEObjectHandler hnd = AttachmentTempObjectHandler.selectByPkey(
                        jeoManager, ls, tempAttachIDs[i]);
                if(hnd != null) {
                    hnd.remove();
                    hnd.commit();
                    attachCount++;
                }
            } catch (EQLException ex) {
                throwException(ex);
            }
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Deleted " + attachCount + " temp attachment(s) at " + time
                    + " ms.");
        }

    }

    /**
     * Deletes all the temporary attachements spawned by the same process.
     *
     * @param processID process ID
     * @param ls        LogonSession object
     */
    public void deleteTempAttachments(LogonSession ls, long processID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Delete the temp attachments.
        try {
            List hnds = AttachmentTempObjectHandler.selectByProcessID(
                    jeoManager, ls, processID);
            if(hnds != null) {
                DEBUG("Found " + hnds.size() + " temp attachments...");
                for(int i = 0; i < hnds.size(); i++) {
                    JEObjectHandler hnd = (JEObjectHandler) hnds.get(i);
                    hnd.remove();
                    hnd.commit();
                }
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Process temp attachment deleted (process ID = " + processID
                    + ") at " + time + " ms.");
        }

    }

    // ========================================== Regular attachments management

    /**
     * Makes a new regular attachment from the temporary one.
     * The temporary attachment is deleted after use.
     *
     * @param ls           LogonSession object
     * @param tempAttachID temporary attachment ID
     * @return ID new attachment ID
     */
    public long addAttachment(LogonSession ls, long tempAttachID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        long attachID = EMPTY_NUMBER;
        try {

            // Get the temp attachement.
            JEObjectHandler tmpHnd = AttachmentTempObjectHandler.selectByPkey(
                    jeoManager, ls, tempAttachID);
            if(tmpHnd == null) {
                WARN("Can't get temporary attachment by ID = " + tempAttachID);
                return attachID;
            }
            AttachmentTempObject tmpObj = (AttachmentTempObject) tmpHnd
                    .getJEObject();

            // Create a new regular attachement.
            JEObjectHandler attHnd = jeoManager.create(ls,
                    AttachmentObjectHandler.class);
            AttachmentObject attObj = (AttachmentObject) attHnd.getJEObject();

            attObj.setProcess_id(tmpObj.getProcess_id());
            attObj.setFilename(tmpObj.getFilename());
            attObj.setFiletype(tmpObj.getFiletype());
            attObj.setFileext(tmpObj.getFileext());
            attObj.setCreated(tmpObj.getCreated());
            attObj.setData(tmpObj.getData());
            attObj.setUser_id(tmpObj.getUser_id());

            attHnd.commit();
            attachID = attObj.getAttachment_id().longValue();

            // Delete the temp attachment used.
            tmpHnd.remove();
            tmpHnd.commit();

        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Attachment added (ID = " + attachID + ") at " + time
                    + " ms.");
        }
        return attachID;

    }

    /**
     * Makes new regular attachments from all temporary ones spawned by
     * the same process. The temporary attachments are deleted after use.
     *
     * @param ls        LogonSession object
     * @param processID process ID to filter temporary attachments
     * @return new attachments IDs array (might be empty)
     */
    public HashMap addAttachments(LogonSession ls, long processID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        long[] attachIDs = EMPTY_ID_ARRAY;
        HashMap resultList = new HashMap();
        try {

            // Get the temp attachements by the process ID.
            List tmpHnds = AttachmentTempObjectHandler.selectByProcessID(
                    jeoManager, ls, processID);
            if(tmpHnds == null || tmpHnds.isEmpty()) {
                WARN("No temporary attachments found for process ID = "
                        + processID);
                return resultList;
            }
            int size = tmpHnds.size();
            DEBUG("Found " + size + " temp attachments...");
            attachIDs = new long[size];

            for(int i = 0; i < size; i++) {

                // Get the next temp attachement.
                AttachmentTempObjectHandler tmpHnd
                        = (AttachmentTempObjectHandler) tmpHnds.get(i);
                AttachmentTempObject tmpObj = (AttachmentTempObject) tmpHnd
                        .getJEObject();

                // Create a new attachement from the temp one.
                JEObjectHandler attHnd = jeoManager.create(ls,
                        AttachmentObjectHandler.class);
                AttachmentObject attObj = (AttachmentObject) attHnd
                        .getJEObject();

                attObj.setProcess_id(tmpObj.getProcess_id());
                attObj.setFilename(tmpObj.getFilename());
                attObj.setFiletype(tmpObj.getFiletype());
                attObj.setFileext(tmpObj.getFileext());
                attObj.setCreated(tmpObj.getCreated());
                attObj.setData(tmpObj.getData());
                attObj.setUser_id(tmpObj.getUser_id());

                attHnd.commit();
                attachIDs[i] = attObj.getAttachment_id().longValue();
                resultList.put(attObj.getAttachment_id(), tmpObj.getFilename());
            } // for( int i = 0; i < size; i++ )

        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Process attachment added (process ID = " + processID + ") at "
                    + time + " ms.");
        }
        return resultList;
    } // addAttachments( LogonSession, long ) : HashMap

    /**
     * Gets the attachement by its ID.
     *
     * @param ls       LogonSession object
     * @param attachID attachment ID
     * @return attachment value object, or <b>null</b> if not found
     */
    public AttachmentObject getAttachment(LogonSession ls, long attachID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Get the attachment.
        AttachmentObject obj = null;
        try {
            JEObjectHandler hnd = AttachmentObjectHandler.selectByPkey(
                    jeoManager, ls, attachID);
            if(hnd != null) {
                obj = (AttachmentObject) hnd.getJEObject();
            } else {
                INFO("No attachments found for ID = " + attachID);
                return obj;
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Got attachment (ID = " + attachID + ") at " + time + " ms.");
        }
        return obj;

    }

    /**
     * Gets the attachement by the Process Id and file name.
     *
     * @param ls        LogonSession object
     * @param processID process ID
     * @param fileName  attachment file name
     * @return attachment value object, or <b>null</b> if not found
     */
    public AttachmentObject getAttachment(LogonSession ls, long processID,
                                          String fileName) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Get the attachment.
        AttachmentObject obj = null;
        try {
            JEObjectHandler hnd = AttachmentObjectHandler.selectByProcessFName(
                    jeoManager, ls, processID, fileName);
            if(hnd != null) {
                obj = (AttachmentObject) hnd.getJEObject();
            } else {
                INFO("No attachments found for ProcessID = " + processID +
                        " and file name = " + fileName);
                return obj;
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Got attachment (ProcessID = " + processID
                    + " and File Name = " + fileName + ") at " + time + " ms.");
        }
        return obj;

    }

    /**
     * Gets all the attachements spawned by the same process.
     *
     * @param processID process ID
     * @param ls        LogonSession object
     * @return array of attachment value objects (might be empty)
     */
    public AttachmentObject[] getAttachments(LogonSession ls, long processID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Get attachments.
        ArrayList list = new ArrayList();
        try {
            List hnds = AttachmentObjectHandler.selectByProcessID(jeoManager,
                    ls, processID);
            if(hnds != null) {
                for(int i = 0; i < hnds.size(); i++) {
                    AttachmentObjectHandler hnd = (AttachmentObjectHandler) hnds
                            .get(i);
                    AttachmentObject obj = (AttachmentObject) hnd.getJEObject();
                    DEBUG("Got attachment '" + obj.getFilename() + "'");
                    list.add(obj);
                }
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Got attachments by the process (process ID = " + processID
                    + ") at " + time + " ms.");
        }
        return (list.size() == 0)
                ? EMPTY_ATTACH_ARRAY
                :(AttachmentObject[]) list.toArray(new AttachmentObject[0]);

    }

    /**
     * Deletes the attachement.
     *
     * @param attachID attachment ID
     * @param ls       LogonSession object
     */
    public void deleteAttachment(LogonSession ls, long attachID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Delete the temp attachment.
        try {
            JEObjectHandler hnd = AttachmentObjectHandler.selectByPkey(
                    jeoManager, ls, attachID);
            if(hnd != null) {
                hnd.remove();
                hnd.commit();
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Attachment deleted (ID = " + attachID + ") at " + time
                    + " ms.");
        }

    }

    /**
     * Deletes all attachements spawned by the same process.
     *
     * @param processID process ID
     * @param ls        LogonSession object
     */
    public void deleteAttachments(LogonSession ls, long processID) {

        // Initialization.
        long time = System.currentTimeMillis();
        JEOManagerLocal jeoManager = getJEOManager();

        // Delete the attachments.
        try {
            List hnds = AttachmentObjectHandler.selectByProcessID(jeoManager,
                    ls, processID);
            if(hnds != null) {
                DEBUG("Found " + hnds.size() + " attachments...");
                for(int i = 0; i < hnds.size(); i++) {
                    JEObjectHandler hnd = (JEObjectHandler) hnds.get(i);
                    hnd.remove();
                    hnd.commit();
                }
            }
        } catch (EQLException ex) {
            throwException(ex);
        }

        // Ok.
        if(getLogger().isInfoEnabled()) {
            time = System.currentTimeMillis() - time;
            INFO("Process attachment deleted (process ID = " + processID
                    + ") at " + time + " ms.");
        }

    }

    // ========================================================= Private methods

    // Gets file extension.

    private String getFileExt(String fileName, String fileType) {

        /** @todo detect file extension by the file type */

        String fileExt = null;
        int pos = fileName.lastIndexOf(".");
        if(pos >= 0 && pos < (fileName.length() - 1)) {
            fileExt = fileName.substring(pos + 1).toLowerCase();
            if(fileExt.length() > EXT_LENGTH) {
                fileExt = null;
            }
        }

        return fileExt;
    }

    // Gets the JEO Manager EJB local interface.
    private JEOManagerLocal getJEOManager() {
        return (JEOManagerLocal) getLocalObject(JNDINames.JEOManager,
                JEOManagerLocalHome.class);
    }

}
