/*
 * 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.qwoss.notification;

import com.queplix.core.error.ErrorHelper;
import com.queplix.core.error.GenericSystemException;
import com.queplix.core.integrator.security.AccessRightsManager;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.SecurityHelper;
import com.queplix.core.integrator.security.User;
import com.queplix.core.integrator.security.WorkGroup;
import com.queplix.core.jxb.entity.Entity;
import com.queplix.core.modules.attachment.ejb.AttachmentManagerLocal;
import com.queplix.core.modules.attachment.ejb.AttachmentManagerLocalHome;
import com.queplix.core.modules.eql.CompoundKey;
import com.queplix.core.modules.eqlext.error.FileManagerException;
import com.queplix.core.modules.eqlext.error.FormTransformException;
import com.queplix.core.modules.eqlext.utils.FileManager;
import com.queplix.core.modules.eqlext.utils.FormsManager;
import com.queplix.core.modules.eqlext.utils.images.FileUploadManager;
import com.queplix.core.modules.inbox.InboxHelper;
import com.queplix.core.modules.inbox.InboxMessage;
import com.queplix.core.modules.jeo.gen.UserObject;
import com.queplix.core.modules.mail.Attachment;
import com.queplix.core.modules.mail.MailAddress;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.ejb.AbstractSessionEJB;
import com.queplix.qwoss.inbox.ejb.InboxManagerLocal;
import com.queplix.qwoss.inbox.ejb.InboxManagerLocalHome;
import com.queplix.qwoss.utils.CustomJNDINames;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;

import javax.mail.MessagingException;
import java.util.List;

/**
 * <p/>
 * Notification manager stateful session EJB
 * </p>
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.7.2.4 $ $Date: 2006/01/24 20:33:36 $
 */

public class NotificationManagerEJB
        extends AbstractSessionEJB {

    // base attributes
    private LogonSession ls;
    private Entity entity;
    private CompoundKey key;

    // notification attributes
    private String subject;
    private String message;

    // specials...
    private String translatedSubject;
    private String translatedMessage;
    private boolean isTranslated = false;

    /**
     * Initialize bean
     *
     * @param ls     LogonSession object
     * @param entity changed entity
     * @param key    changed record compound primary key
     */
    public void ejbCreate(LogonSession ls, Entity entity, CompoundKey key) {

        INFO("NotificationManagerEJB create - " + hashCode());
        if(getLogger().isDebugEnabled()) {
            DEBUG("Notification parameters:");
            DEBUG("   entity: " + entity.getName());
            DEBUG("   key: " + key);
        }

        if(ls == null) {
            throw new IllegalStateException("Logon session is NULL");
        }
        if(entity == null) {
            throw new IllegalStateException("Entity is NULL");
        }
        if(key == null) {
            throw new IllegalStateException("Key is NULL");
        }

        this.ls = ls;
        this.entity = entity;
        this.key = key;
    }

    // ----------------------------------------------------- setters

    public void setSubject(String subject) {
        this.subject = subject;
        isTranslated = false;
    }

    public void setMessage(String message) {
        this.message = message;
        isTranslated = false;
    }

    /**
     * Default notification
     *
     * @param user User object
     */
    public void notify(User user) {
        notify(user, NotificationHelper.ALERT_METHOD);
    }

    /**
     * Notify via alert
     *
     * @param user User object
     */
    public void notifyByAlert(User user) {

        long time = System.currentTimeMillis();

        INFO("Try to send alert notification to user '" + user + "'");
        InboxManagerLocal inboxLocal = getInboxManagerLocal();
        InboxMessage mailMessage = buildAlertInboxMessage(AccessRightsManager
                .getSystemLogonSession().getUser().getUserID(),
                user.getUserID());
        inboxLocal.sendAlertMessage(ls, mailMessage);

        INFO("Send notification to user via alert - ok. Time(ms)="
                + (System.currentTimeMillis() - time));
    }

    /**
     * Notify via E-mail
     *
     * @param user User object
     */
    public void notifyByMail(User user) {

        long time = System.currentTimeMillis();
        DEBUG("Try to send mail notification to user '" + user + "'");

        try {
            InboxManagerLocal inboxLocal = getInboxManagerLocal();
            MailAddress[] to = new MailAddress[]{new MailAddress(user)};
            InboxMessage mailMessage = buildInboxMessage(to);

            // Send message!
            inboxLocal.sendEmailMessage(ls, mailMessage, null);

            DEBUG("Send notification to user via e-mail - ok. Time(ms)="
                    + (System.currentTimeMillis() - time) +
             "\nSend notification to address " + mailMessage.getTo()
                    + " from address " + mailMessage.getFrom());

        } catch (MessagingException mex) {
            WARN("Send notification to user via e-mail - fail. Problem:\n"
                    + mex.getMessage() + "\nTime(ms)="
                    + (System.currentTimeMillis() - time));

        } catch (Throwable t) {
            ErrorHelper.throwSystemException(t, this);
        }
    }

    /**
     * Notify via E-mail
     *
     * @param email String
     */
    public void notifyByMail(String email) {
        long time = System.currentTimeMillis();

        DEBUG("Try to send mail notification to addressee '" + email + "'");

        try {
            InboxManagerLocal inboxLocal = getInboxManagerLocal();
            MailAddress[] to = MailAddress.parse(email);
            InboxMessage mailMessage = buildInboxMessage(to);

            // Send message!
            inboxLocal.sendEmailMessage(ls, mailMessage, null);

                DEBUG("Send notification to addressee via e-mail - ok. Time(ms)="
                        + (System.currentTimeMillis() - time) +
                "\n Send notification to address " + mailMessage.getTo()
                        + " from address " + mailMessage.getFrom());
        } catch (MessagingException mex) {
            WARN("Send notification to addressee via e-mail. Problem:\n"
                    + mex.getMessage() + "\nTime(ms)="
                    + (System.currentTimeMillis() - time));

        } catch (Throwable t) {
            ErrorHelper.throwSystemException(t, this);
        }
    }

    /**
     * Default notification
     *
     * @param group    Group object
     * @param tier     group tier
     * @param userList List of User objects (optional)
     */
    public void notify(WorkGroup group, Integer tier, List userList) {
        // send notification to memebers of this group

        int notifyMethod = group.getNotifyMethod();
        if(notifyMethod == WorkGroup.NOTIFY_METHOD_OWNER) {

            // Notify workgroup members
            if(userList == null) {
                userList = SecurityHelper.loadUsers(ls, group, tier);
            }
            int size = (userList == null) ? 0:userList.size();
            for(int i = 0; i < size; i++) {

                UserObject userObj = (UserObject) userList.get(i);
                User user = new User();
                user.setUserID(userObj.getPkey());
                user.setUserType(userObj.getUser_type());
                user.setLoginName(userObj.getLoginname());
                user.setFullName(userObj.getFullname());
                user.setEmail(userObj.getEmail());
                user.setPasswordDigest(userObj.getPassword());

                Integer userGroupNotifyMethod =
                        SecurityHelper.loadUserGroupNotifyMethod(ls, user,
                                group.getGroupID());
                if(userGroupNotifyMethod == null) {
                    // default notification
                    notify(user);
                } else {
                    // notification using group
                    notify(user, userGroupNotifyMethod);
                }
            }
        } else {
            // Notify workgroup
            notify(group, tier, notifyMethod);
        }
    }

    /**
     * Notify via alert
     *
     * @param group Group object
     * @param tier  group tier
     */
    public void notifyByAlert(WorkGroup group, Integer tier) {

        long time = System.currentTimeMillis();

        INFO("Try to send alert notification to group '" + group
                + "' with tier '" + tier + "'");

        List userList = SecurityHelper.loadUsers(ls, group, tier);
        int size = (userList == null) ? 0:userList.size();
        InboxManagerLocal inboxLocal = getInboxManagerLocal();
        for(int i = 0; i < size; i++) {
            UserObject userObj = (UserObject) userList.get(i);
            InboxMessage mailMessage = buildAlertInboxMessage(
                    AccessRightsManager.getSystemLogonSession()
                            .getUser().getUserID(), userObj.getPkey());
            inboxLocal.sendAlertMessage(ls, mailMessage);
        }
        INFO("Send notification to group via alert - ok. Time(ms)="
                + (System.currentTimeMillis() - time));
    }

    /**
     * Notify via E-mail
     *
     * @param group Group object
     */
    public void notifyByMail(WorkGroup group) {
        long time = System.currentTimeMillis();
        DEBUG("Try to send mail notification to group '" + group + "'");

        try {
            InboxManagerLocal inboxLocal = getInboxManagerLocal();
            MailAddress[] to = new MailAddress[]{new MailAddress(group)};
            InboxMessage mailMessage = buildInboxMessage(to);

            // Send!
            inboxLocal.sendEmailMessage(ls, mailMessage, null);

            DEBUG("Send notification to group via e-mail - ok. Time(ms)="
                    + (System.currentTimeMillis() - time) +
            "\nSend notification to address " + mailMessage.getTo()
                    + " from address " + mailMessage.getFrom());

        } catch (MessagingException mex) {
            WARN("Send notification to group via e-mail - fail. Problem:\n"
                    + mex.getMessage() + "\nTime(ms)="
                    + (System.currentTimeMillis() - time));

        } catch (Throwable t) {
            ErrorHelper.throwSystemException(t, this);
        }
    }

    /**
     * User notification
     */
    private void notify(User user, int notifyMethod) {

        // Checks email. If NULL - send alerts
        if(notifyMethod == User.NOTIFY_METHOD_EMAIL
                && user.getEmail() != null) {
            notifyByMail(user);
        } else {
            notifyByAlert(user);
        }
    }

    /**
     * Workgroup notification
     */
    private void notify(WorkGroup group, Integer tier, int notifyMethod) {

        // Checks email. If NULL - send alerts
        if(notifyMethod == WorkGroup.NOTIFY_METHOD_EMAIL
                && group.getEmail() != null) {
            notifyByMail(group);
        } else {
            notifyByAlert(group, tier);
        }
    }

    /**
     * Get translated subject
     */
    private String getTranslatedSubject() {
        doTranslatePatterns();
        return translatedSubject;
    }

    /**
     * Get translated message
     */
    private String getTranslatedMessage() {
        doTranslatePatterns();
        return translatedMessage;
    }

    /**
     * Do translate patterns
     */
    private synchronized void doTranslatePatterns() {
        if(!isTranslated) {
            FormsManager formsManager = new FormsManager(entity.getName(), key);
            try {
                translatedSubject = formsManager.translateBody(subject, false);
                translatedMessage = formsManager.translateBody(message, false);
            } catch (FormTransformException ex) {
                ERROR("Transformation error: " + ex.getMessage());
            }
            isTranslated = true;
        }
    }

    /**
     * Constrcuts InboxMessage.
     * @param to
     * @return
     */
    private InboxMessage buildInboxMessage(MailAddress[] to) {

        InboxMessage mailMessage = new InboxMessage(to, new MailAddress(
                InboxHelper.getDefSender()), getTranslatedSubject(),
                getTranslatedMessage());
        mailMessage.setFrom(new MailAddress(InboxHelper.getDefSender()));

        // Add linked object.
        Long callID = new Long((String) key.getKey(0));
        Integer callType = null;
        if(key.size() == 2) {
            callType = new Integer((String) key.getKey(1));
        } else if(key.size() != 1) {
            throw new IllegalStateException(
                    "Wrong compound key. Only Call ID and Call Type supported!");
        }
        mailMessage.setObjectInfo(callID, callType);

        addAttachments(mailMessage);

        return mailMessage;

    }

    /**
     * Adds attachemnts to the email.
     * @param mailMessage
     */
    private void addAttachments(InboxMessage mailMessage) {
        String body = mailMessage.getBody();

        if(!StringHelper.isHTML(body)) {
            return;
        }
        RE imgTag;
        RE src;
        try {
            imgTag = new RE("<img[^>]{1,}attachment=\"notAttachment\"[^>.]{0,}>",
                    RE.MATCH_CASEINDEPENDENT);
            src = new RE("src=\".+\"", RE.MATCH_CASEINDEPENDENT);
        } catch (RESyntaxException e) {
                throw new GenericSystemException(e);
        }

        boolean isAttachment = false;

        int curPos = 0;
        while(imgTag.match(body, curPos)) {
            int startPos = imgTag.getParenStart(0);
            int endPos = imgTag.getParenEnd(0);
            String tag = body.substring(startPos, endPos);
            if(src.match(tag)) {
                int sPos = src.getParenStart(0);
                int ePos = src.getParenEnd(0);
                String srcText = tag.substring(sPos, ePos);
                sPos = srcText.indexOf('"');
                ePos = srcText.indexOf('"', sPos + 1);
                String filePath = srcText.substring(sPos + 1, ePos);
                createAttachment(filePath, mailMessage);
                isAttachment = true;
            }
            curPos = endPos;
        }

        // set process id
        if(isAttachment) {
            AttachmentManagerLocal attachMgr = (AttachmentManagerLocal)
                    getLocalObject(CustomJNDINames.AttachmentManager,
                            AttachmentManagerLocalHome.class);
            mailMessage.setProcessId(attachMgr.getUniqueProcessID());
        } // if (isAttachment)
    }

    private void createAttachment(String filePath, InboxMessage mailMessage) {
        String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
        byte[] fileData = getFileData(null, fileName);
        Attachment attachment = new Attachment(fileName, fileData);
        mailMessage.addAttachments(attachment);
    }

    private byte[] getFileData(String id, String filepath)
            throws FileManagerException {
        FileUploadManager manager = new FileUploadManager(ls);
        byte[] data = null;
        try {
            FileManager.FileInfo fi;
            if(!StringHelper.isEmpty(id)) {
                fi = manager.loadFile(Long.parseLong(id));
            } else {
                fi = FileManager.loadFile(filepath);
            }
            if(fi.getData() != null) {
                data = fi.getData();
            }
        } catch (FileManagerException ex) {
            //todo review it, what should we do here
        }
        return data;
    }

    private InboxMessage buildAlertInboxMessage(Long senderId,
                                                Long recipientId) {

        // Add linked object.
        Long callID = new Long((String) key.getKey(0));
        Integer callType = null;
        if(key.size() == 2) {
            callType = new Integer((String) key.getKey(1));
        } else if(key.size() != 1) {
            throw new IllegalStateException(
                    "Wrong compound key. Only Call ID and Call Type supported!");
        }

        return new InboxMessage(senderId, recipientId, null,
                getTranslatedSubject(), getTranslatedMessage(), callType,
                callID);

    }

    /**
     * @return InboxManagerLocal local interface
     */
    protected InboxManagerLocal getInboxManagerLocal() {
        return (InboxManagerLocal) getLocalObject(CustomJNDINames.InboxManager,
                InboxManagerLocalHome.class);
    }
}
