/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (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.apache.org/licenses/LICENSE-2.0
 *
 *  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 org.apache.ivy.core.module.id;

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

import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
import org.apache.ivy.plugins.matcher.MapMatcher;
import org.apache.ivy.plugins.matcher.PatternMatcher;

/**
 * This class targets to speed up lookup for exact pattern matcher by keys, which are created with
 * (organization, module) information. When exact pattern matcher is added, the key is created from
 * matcher's attributes. When matcher is looked up against specific module, the key is recreated
 * from module's attributes.
 * <p>
 * </p>
 * The lookup doesn't target to speed up lookup for non exact pattern matcher. All non exact
 * matchers are placed in non-keyed collection.
 * <p>
 * </p>
 * At lookup for matchers against specific module, all non exact pattern matchers are iterated to
 * match with module attributes, and exact pattern matchers binding to the same key will also
 * iterated to match with module attributes.
 * <p>
 * </p>
 * If there are much more exact pattern matchers than non exact pattern matchers, the matcher lookup
 * speed can benefit from this class significantly. A quick example could be user declares lots of
 * dependencyOverrides which are typically exact pattern matchers.
 * <p>
 * </p>
 * If there are balanced exact and non exact pattern matchers, the matcher lookup speed doesn't hurt
 * by this class.
 * <p>
 * </p>
 */
public class MatcherLookup {

    //private static final String FORMAT = "{org:%s, module:%s}";

    private static final String DEFAULT = "{org:" + "default" + ", module:" + "default" + "}";

    private Map/* <String, List<MapMatcher>> */lookup = new HashMap();

    private List/* <MapMatcher> */non_exact_matchers = new ArrayList();

    /**
     * Add matcher.
     * 
     * If matcher is exact pattern matcher, it will be associated with a key and placed in keyed
     * collection.
     * 
     * If matcher is not exact pattern matcher, it will be placed into non-keyed collection
     * 
     * @param matcher
     */
    public void add(MapMatcher matcher) {
        if (!(matcher.getPatternMatcher() instanceof ExactPatternMatcher)) {
            non_exact_matchers.add(matcher);
            return;
        }
        Object key = key(matcher.getAttributes());
        List exact_matchers = (List) lookup.get(key);
        if (exact_matchers == null) {
            exact_matchers = new ArrayList();
            lookup.put(key, exact_matchers);
        }
        exact_matchers.add(matcher);
    }

    /**
     * Get a list of matchers which can apply to module with specified attributes
     * 
     * @param attrs
     *            A map of attributes that matcher should match.
     * 
     * @return list A list of candidate matchers that matches specified attributes
     */
    public List get(Map attrs) {
        List matchers = new ArrayList();
        // Step 1: find matchers from non_exact_matchers list
        if (!non_exact_matchers.isEmpty()) {
            for (Iterator iter = non_exact_matchers.iterator(); iter.hasNext();) {
                MapMatcher matcher = (MapMatcher) iter.next();
                if (matcher.matches(attrs)) {
                    matchers.add(matcher);
                }
            }
        }
        // Step 2: find matchers from exact_matchers list of key
        Object key = key(attrs);
        List exact_matchers = (List) lookup.get(key);
        if (exact_matchers != null) {
            for (Iterator iter = exact_matchers.iterator(); iter.hasNext();) {
                MapMatcher matcher = (MapMatcher) iter.next();
                if (matcher.matches(attrs)) {
                    matchers.add(matcher);
                }
            }
        }
        // Step 3: (iff key != DEFAULT) find matchers from exact_matchers of DEFAULT
        if (key != DEFAULT) {
            List default_exact_matchers = (List) lookup.get(DEFAULT);
            if (default_exact_matchers != null) {
                for (Iterator iter = default_exact_matchers.iterator(); iter.hasNext();) {
                    MapMatcher matcher = (MapMatcher) iter.next();
                    if (matcher.matches(attrs)) {
                        matchers.add(matcher);
                    }
                }
            }
        }
        return matchers;
    }

    /**
     * Create a key from specified attributes
     * 
     * @param attrs
     *            A map of attributes
     * @return key object
     */
    private Object key(Map attrs) {
        Object org = attrs.get(IvyPatternHelper.ORGANISATION_KEY);
        Object module = attrs.get(IvyPatternHelper.MODULE_KEY);
        if (org == null || PatternMatcher.ANY_EXPRESSION.equals(org) || module == null
                || PatternMatcher.ANY_EXPRESSION.equals(module)) {
            return DEFAULT;
        }
        return "{org:" + org + ", module:" + module + "}";
    }

}
