/*
 * 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.alert.utils;

import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.User;
import com.queplix.core.modules.alert.AlertBlock;
import com.queplix.core.utils.cache.Cache;
import com.queplix.core.utils.log.AbstractLogger;
import com.queplix.core.utils.log.Log;

import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * Generic implementation of alert selector.
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.2 $ $Date: 2005/09/27 12:08:49 $
 */

public class GenericAlertSelectorImpl
        implements AlertSelector {

    // ----------------------------------------------------- variables

    // Comporator.
    protected Comparator comparator = new AlertComparator();

    // Logger.
    protected AbstractLogger logger = Log.getLog(getClass());

    // Properties.
    protected Properties prop;

    // ----------------------------------------------------- public methods

    /*
     * No javadoc
     * @see AlertSelector#search
     */

    public SortedMap search(Cache cache, AlertSelectorCriteria criteria) {

        // Init.
        LogonSession ls = criteria.getLogonSession();
        boolean incoming = criteria.isIncomming();
        Long startAlertID = criteria.getStartID();
        Integer page = criteria.getPage();
        Integer pageSize = criteria.getPageSize();

        // Check if cache is empty - return NULL
        Object lastKey = cache.getLastKey();
        if(lastKey == null) {
            logger.INFO(
                    "Skip searching. Cannot find Last Key. Probably cache is empty.");
            return null;
        }

        logger.DEBUG("Last key: " + lastKey);

        // Prepare result map with default comporator
        SortedMap ret = new TreeMap(comparator);

        if(startAlertID != null) {
            long id = startAlertID.longValue();

            // Check if the last key in cache less or equals to Start Alert ID - return NULL
            if(id >= Long.parseLong("" + lastKey)) {
                logger.INFO("Skip searching. Start ID " + startAlertID + " >= "
                        + lastKey);
                return null;
            }

            // Get SortedMap with keys high than Start Alert ID
            SortedMap map = cache.tailMap(new Long(id + 1));
            if(map == null || map.isEmpty()) {
                logger.INFO("Skip searching. Got empty Map.");
                return null;
            }

            // ..and sort it in DESC order.
            ret.putAll(map);

        } else {

            // Get entire SortedMap and sort it in DESC order.
            Map map = cache.toMap();
            if(map == null || map.isEmpty()) {
                logger.INFO("Skip searching. Got empty Map.");
                return null;
            }
            ret.putAll(map);
        }

        //
        // Return only user's alerts.
        // @see SQLExecutorGenericImpl#doSelect
        //

        User user = ls.getUser();
//        boolean isAdmin = SecurityManager.isAdmin( user ); old sample
//        boolean isEmployee = ( user.getUserTypeID() == EmployeeUser.ID ); old sample
        boolean isAdmin = false;//todo change login
        boolean isEmployee = true;//todo change login
        List<Long> userGroups = user.getUserGroups();

        // .. user alert block for checking
        AlertBlock block = new AlertBlock(user.getUserID(),
                user.getAuthenticationType());

        // .. available more records flag
        boolean availableMore = false;

        // .. start and end positions in SortedMap
        int start = 0;
        int end = 0;
        if(page != null && pageSize != null) {
            start = page * pageSize;
            end = start + pageSize;
        }

        // .. current position in SortedMap
        int i = 0;

        for(Iterator it = ret.keySet().iterator(); it.hasNext();) {
            Object key = it.next();
            AlertVO alertVO = (AlertVO) ret.get(key);

            // 1. Check if alert deleted (has block) for current user
            if(alertVO.contains(block)) {
                // remove from the <code>ret</code>
                it.remove();
                continue;
            }

            if(incoming) {
                // 2. Check is user is Admin.
                // 3. Check own incoming alerts.
                // 4. Check own incoming workgroup alerts.
                // 5. Check 'To All' flag (only for Employee).
                if(!isAdmin &&
                        !alertVO.isOwner(user) &&
//                    !alertVO.isOwner( userGroups ) &&//todo change logic, move it back
                        !(isEmployee && alertVO.isToAllAlert())) {
                    // .. not found
                    // remove from the <code>ret</code>
                    it.remove();
                    continue;
                }

            } else {
                // 2. Check is user is Admin.
                // 3. Check own outgoing alerts
                if(!isAdmin && !alertVO.isSender(user)) {
                    // .. not found
                    // remove from the <code>ret</code>
                    it.remove();
                    continue;
                }
            }

            // Check start position
            if(start > i) {
                // remove from the <code>ret</code>
                it.remove();
                i++;
                continue;
            }

            // Check rows limit
            if(end > 0 && i >= end) {
                // mark that we have more records...
                availableMore = true;

                // remove the rest from the <code>ret</code>
                it.remove();
                i++;
                for(; it.hasNext(); it.next(), it.remove(), i++) {
                    ;
                }
                break;
            }

            i++;
        }

        if(availableMore) {
            // set "available more records" flag
            criteria.setAvailableMore(true);
        }

        // Ok.
        return ret;
    }

    /*
     * No javadoc
     * @see Cache#setProperties
     */
    public void setProperties(Properties prop) {
        this.prop = prop;
    }

    /**
     * Property getter by the <code>name</code>.
     *
     * @param name String
     * @return Object
     */
    public Object getProperty(String name) {
        return prop == null ? null:prop.get(name);
    }

    // ----------------------------------------------------- inner class

    /**
     * <p>Default Alert comporator. Sorting by Alert ID in DESC order.</p>
     */
    static class AlertComparator
            implements Comparator {

        /*
         * No javadoc
         * @see Comparator#compare
         */
        public int compare(Object o1, Object o2) {
            long l1 = ((Long) o1).longValue();
            long l2 = ((Long) o2).longValue();

            if(l1 > l2) {
                return -1;
            } else if(l1 < l2) {
                return 1;
            } else {
                return 0;
            }
        }

        /*
         * No javadoc
         * @see Comparator#equals
         */
        public boolean equals(Object that) {
            if(this == that) {
                return true;
            }
            return false;
        }
    }

}
