/*******************************************************************************
 * Copyright (c) 2010 Neil Bartlett.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Neil Bartlett - initial API and implementation
 *******************************************************************************/
package bndtools.tasks;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.resource.Capability;

import aQute.lib.io.IO;
import bndtools.model.resolution.RequirementWrapper;

public class AnalyseBundleResolutionJob extends Job {

    private static final ILogger logger = Logger.getLogger(AnalyseBundleResolutionJob.class);

    private final Set<? extends CapReqLoader> loaders;

    private Map<String, List<RequirementWrapper>> requirements;
    private Map<String, List<Capability>> capabilities;

    public AnalyseBundleResolutionJob(String name, Set<? extends CapReqLoader> loaders) {
        super(name);
        this.loaders = loaders;
    }

    private static <K, V> void mergeMaps(Map<K, List<V>> from, Map<K, List<V>> into) {
        for (Entry<K, List<V>> entry : from.entrySet()) {
            K key = entry.getKey();

            List<V> list = into.get(key);
            if (list == null) {
                list = new ArrayList<V>();
                into.put(key, list);
            }

            list.addAll(entry.getValue());
        }
    }

    @Override
    protected IStatus run(IProgressMonitor monitor) {
        try {
            // Load all the capabilities and requirements
            Map<String, List<Capability>> allCaps = new HashMap<String, List<Capability>>();
            Map<String, List<RequirementWrapper>> allReqs = new HashMap<String, List<RequirementWrapper>>();
            for (CapReqLoader loader : loaders) {
                try {
                    Map<String, List<Capability>> caps = loader.loadCapabilities();
                    mergeMaps(caps, allCaps);

                    Map<String, List<RequirementWrapper>> reqs = loader.loadRequirements();
                    mergeMaps(reqs, allReqs);
                } catch (Exception e) {
                    logger.logError("Error in Bnd resolution analysis.", e);
                } finally {
                    IO.close(loader);
                }
            }

            // Check for resolved requirements
            for (String namespace : allReqs.keySet()) {
                List<RequirementWrapper> rws = allReqs.get(namespace);
                List<Capability> candidates = allCaps.get(namespace);

                if (candidates == null)
                    continue;

                for (RequirementWrapper rw : rws) {
                    String filterStr = rw.requirement.getDirectives()
                        .get("filter");
                    if (filterStr != null) {
                        aQute.lib.filter.Filter filter = new aQute.lib.filter.Filter(filterStr);
                        for (Capability cand : candidates) {
                            if (filter.matchMap(cand.getAttributes())) {
                                rw.resolved = true;
                                break;
                            }
                        }
                    }
                }
            }

            // Generate the final results
            // Set<File> resultFiles = builderMap.keySet();
            // resultFileArray = resultFiles.toArray(new File[0]);

            this.requirements = allReqs;
            this.capabilities = allCaps;

            // showResults(resultFileArray, importResults, exportResults);
            return Status.OK_STATUS;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Map<String, List<RequirementWrapper>> getRequirements() {
        return Collections.unmodifiableMap(requirements);
    }

    public Map<String, List<Capability>> getCapabilities() {
        return Collections.unmodifiableMap(capabilities);
    }
}
