/*
 * 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.netbeans.modules.editor.mimelookup;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.spi.editor.mimelookup.MimeDataProvider;
import org.netbeans.spi.editor.mimelookup.MimeLookupInitializer;
import org.openide.util.*;
import org.openide.util.Lookup.Template;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;


/**
 *
 * @author vita
 */
@SuppressWarnings("deprecation")
public final class MimePathLookup extends ProxyLookup implements LookupListener {

    private static final Logger LOG = Logger.getLogger(MimePathLookup.class.getName());
    private static final RequestProcessor WORKER = new RequestProcessor("MimePathLookupFiring", 1);

    private final MimePath mimePath;
    private final boolean mimePathBanned;
    private final Lookup.Result<MimeDataProvider> dataProviders;
    private final Lookup.Result<MimeLookupInitializer> mimeInitializers; // This is supported for backwards compatibility only.
    private boolean initialized = false;

    /** Creates a new instance of MimePathLookup */
    public MimePathLookup(MimePath mimePath) {
        super();

        if (mimePath == null) {
            throw new NullPointerException("Mime path can't be null."); //NOI18N
        }

        this.mimePath = mimePath;
        this.mimePathBanned = mimePath.size() > 0 && mimePath.getMimeType(0).contains("text/base"); //NOI18N
        
        class R implements Runnable {
            Lookup.Result<MimeDataProvider> dataProviders;
            Lookup.Result<MimeLookupInitializer> mimeInitializers;
            
            public void run() {
                dataProviders = Lookup.getDefault().lookup(new Lookup.Template<MimeDataProvider>(MimeDataProvider.class));
                dataProviders.addLookupListener(WeakListeners.create(LookupListener.class, MimePathLookup.this, dataProviders));

                mimeInitializers = Lookup.getDefault().lookup(new Lookup.Template<MimeLookupInitializer>(MimeLookupInitializer.class));
                mimeInitializers.addLookupListener(WeakListeners.create(LookupListener.class, MimePathLookup.this, mimeInitializers));
            }
        }
        R r = new R();
        Lookups.executeWith(null, r);
        this.dataProviders = r.dataProviders;
        this.mimeInitializers = r.mimeInitializers;
    }

    @Override
    protected void beforeLookup(Template<?> template) {
        synchronized (this) {
            if (!initialized) {
                initialized = true;
                rebuild();
            }
        }
    }


    public MimePath getMimePath() {
        return mimePath;
    }

    private void rebuild() {
        ArrayList<Lookup> lookups = new ArrayList<Lookup>();

        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("Rebuilding MimeLookup for '" + mimePath.getPath() + "'..."); //NOI18N
        }

        // Add lookups from MimeDataProviders
        for (MimeDataProvider provider : dataProviders.allInstances()) {
            if (mimePathBanned && !isDefaultProvider(provider)) {
                continue;
            }
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("- Querying MimeDataProvider(" + mimePath.getPath() + "): " + provider); //NOI18N
            }
            Lookup mimePathLookup = provider.getLookup(mimePath);
            if (mimePathLookup != null) {
                lookups.add(mimePathLookup);
            }
        }

        // XXX: This hack here is to make GSF and Schliemann frameworks work.
        // Basically we should somehow enforce the composition of lookups
        // for MimeDataProviders too. But some providers such as the one from
        // editor/mimelookup/impl do the composition in their own way. So we
        // will probably have to extend the SPI somehow to accomodate both simple
        // providers and the composing ones.
        // See also http://www.netbeans.org/issues/show_bug.cgi?id=118099

        // Add lookups from deprecated MimeLookupInitializers
        List<MimePath> paths = mimePath.getIncludedPaths();
        for(MimePath mp : paths) {
            Collection<? extends MimeLookupInitializer> initializers = mimeInitializers.allInstances();

            for(int i = 0; i < mp.size(); i++) {
                Collection<MimeLookupInitializer> children = new ArrayList<MimeLookupInitializer>(initializers.size());

                for(MimeLookupInitializer mli : initializers) {
                    children.addAll(mli.child(mp.getMimeType(i)).allInstances());
                }

                initializers = children;
            }

            for(MimeLookupInitializer mli : initializers) {
                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("- Querying MimeLookupInitializer(" + mp.getPath() + "): " + mli); //NOI18N
                }
                Lookup mimePathLookup = mli.lookup();
                if (mimePathLookup != null) {
                    lookups.add(mimePathLookup);
                }
            }
        }

        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("MimeLookup for '" + mimePath.getPath() + "' rebuilt."); //NOI18N
        }

        setLookups(WORKER, lookups.toArray(new Lookup[lookups.size()])); // NOI18N
    }

    private boolean isDefaultProvider(MimeDataProvider provider) {
        return provider.getClass().getName().equals("org.netbeans.modules.editor.mimelookup.impl.DefaultMimeDataProvider"); //NOI18N
    }

    //-------------------------------------------------------------
    // LookupListener implementation
    //-------------------------------------------------------------

    public synchronized void resultChanged(LookupEvent ev) {
        rebuild();
    }

}
