/*
 * 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.maven.index;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

import org.apache.maven.index.context.IndexingContext;

/**
 * A default repository scanner for Maven 2 repository.
 *
 * @author Jason Van Zyl
 * @author Tamas Cservenak
 */
@Singleton
@Named
public class DefaultScanner implements Scanner {

    private final ArtifactContextProducer artifactContextProducer;

    @Inject
    public DefaultScanner(ArtifactContextProducer artifactContextProducer) {
        this.artifactContextProducer = artifactContextProducer;
    }

    public ScanningResult scan(ScanningRequest request) {
        request.getArtifactScanningListener().scanningStarted(request.getIndexingContext());

        ScanningResult result = new ScanningResult(request);

        scanDirectory(request.getStartingDirectory(), request);

        request.getArtifactScanningListener().scanningFinished(request.getIndexingContext(), result);

        return result;
    }

    private void scanDirectory(File dir, ScanningRequest request) {
        if (dir == null) {
            return;
        }

        File[] fileArray = dir.listFiles();

        if (fileArray != null) {
            Set<File> files = new TreeSet<>(new ScannerFileComparator());

            files.addAll(Arrays.asList(fileArray));

            for (File f : files) {
                if (f.getName().startsWith(".")) {
                    continue; // skip all hidden files and directories
                }

                if (f.isDirectory()) {
                    scanDirectory(f, request);
                }
                // else if ( !AbstractIndexCreator.isIndexable( f ) )
                // {
                // continue; // skip non-indexable files
                // }
                else {
                    processFile(f, request);
                }
            }
        }
    }

    private void processFile(File file, ScanningRequest request) {
        IndexingContext context = request.getIndexingContext();

        ArtifactContext ac = artifactContextProducer.getArtifactContext(context, file);

        if (ac != null) {
            request.getArtifactScanningListener().artifactDiscovered(ac);
        }
    }

    // ==

    /**
     * A special comparator to overcome some very bad limitations of nexus-indexer during scanning: using this
     * comparator, we force to "discover" POMs last, before the actual artifact file. The reason for this, is to
     * guarantee that scanner will provide only "best" informations 1st about same artifact, since the POM->artifact
     * direction of discovery is not trivial at all (pom read -> packaging -> extension -> artifact file). The artifact
     * -> POM direction is trivial.
     */
    private static class ScannerFileComparator implements Comparator<File> {
        public int compare(File o1, File o2) {
            if (o1.getName().endsWith(".pom") && !o2.getName().endsWith(".pom")) {
                // 1st is pom, 2nd is not
                return 1;
            } else if (!o1.getName().endsWith(".pom") && o2.getName().endsWith(".pom")) {
                // 2nd is pom, 1st is not
                return -1;
            } else {
                // both are "same" (pom or not pom)
                // Use reverse order so that timestamped snapshots
                // use latest - not first
                return o2.getName().compareTo(o1.getName());
            }
        }
    }
}
