/*-
 * Copyright (c) 2014 Red Hat, Inc.
 *
 * Licensed 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.fedoraproject.xmvn.resolver.impl;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.fedoraproject.xmvn.metadata.PackageMetadata;
import org.fedoraproject.xmvn.metadata.io.stax.MetadataStaxReader;

/**
 * @author Mikolaj Izdebski
 */
class MetadataReader
{
    private final Logger logger = LoggerFactory.getLogger( MetadataReader.class );

    private final ThreadPoolExecutor executor;

    static class DaemonFactory
        implements ThreadFactory
    {
        @Override
        public Thread newThread( Runnable runnable )
        {
            Thread thread = new Thread( runnable );
            thread.setName( MetadataReader.class.getCanonicalName() + ".worker" );
            thread.setDaemon( true );
            return thread;
        }
    }

    public MetadataReader()
    {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
        int nThread = 2 * Math.min( Math.max( Runtime.getRuntime().availableProcessors(), 1 ), 8 );
        executor = new ThreadPoolExecutor( nThread, nThread, 1, TimeUnit.MINUTES, queue, new DaemonFactory() );
    }

    public List<PackageMetadata> readMetadata( List<String> depmapLocations )
    {
        Map<Path, Future<PackageMetadata>> futures = new LinkedHashMap<>();

        for ( String pathString : depmapLocations )
        {
            Path path = Paths.get( pathString );

            if ( Files.isDirectory( path ) )
            {
                String flist[] = path.toFile().list();
                if ( flist != null )
                {
                    Arrays.sort( flist );
                    for ( String fragFilename : flist )
                    {
                        Path xmlPath = path.resolve( fragFilename );
                        futures.put( xmlPath, executor.submit( new Task( xmlPath ) ) );
                    }
                }
            }
            else
            {
                futures.put( path, executor.submit( new Task( path ) ) );
            }
        }

        try
        {
            List<PackageMetadata> result = new ArrayList<>();

            for ( Entry<Path, Future<PackageMetadata>> entry : futures.entrySet() )
            {
                Path path = entry.getKey();
                Future<PackageMetadata> future = entry.getValue();

                try
                {
                    PackageMetadata metadata = future.get();
                    result.add( metadata );
                    logger.debug( "Read metadata for {} artifacts from file {}", metadata.getArtifacts().size(), path );
                }
                catch ( ExecutionException e )
                {
                    // Ignore. Failure to read PackageMetadata of a single package should not break the whole system
                    logger.debug( "Skipping metadata file {}", path, e );
                }
            }

            return result;
        }
        catch ( InterruptedException e )
        {
            logger.debug( "Metadata reader thread was interrupted" );
            throw new RuntimeException( e );
        }
    }

    class Task
        implements Callable<PackageMetadata>
    {
        private final Path path;

        public Task( Path path )
        {
            this.path = path;
        }

        @Override
        public PackageMetadata call()
            throws Exception
        {
            try (InputStream fis = Files.newInputStream( path ))
            {
                try (BufferedInputStream bis = new BufferedInputStream( fis, 128 ))
                {
                    try (InputStream is = isCompressed( bis ) ? new GZIPInputStream( bis ) : bis)
                    {
                        MetadataStaxReader reader = new MetadataStaxReader();
                        return reader.read( is );
                    }
                }
            }
        }

        private boolean isCompressed( BufferedInputStream bis )
            throws IOException
        {
            try
            {
                bis.mark( 2 );
                DataInputStream ois = new DataInputStream( bis );
                int magic = Short.reverseBytes( ois.readShort() ) & 0xFFFF;
                return magic == GZIPInputStream.GZIP_MAGIC;
            }
            catch ( EOFException e )
            {
                return false;
            }
            finally
            {
                bis.reset();
            }
        }
    }
}

@@ -1476,16 +1476,73 @@
 ctory;%0A%0A
+import org.fedoraproject.xmvn.metadata.ArtifactMetadata;%0A
 import o
@@ -3875,24 +3875,102 @@
 metadata );%0A
+%0A                    if ( logger.isDebugEnabled() )%0A                    %7B%0A    
             
@@ -3992,20 +3992,22 @@
 debug( %22
-Read
+Adding
  metadat
@@ -4013,78 +4013,215 @@
 ta f
-or %7B%7D artifacts from file %7B%7D%22, metadata.getArtifacts().size(), path );
+rom file %7B%7D%22, path );%0A%0A                        for ( ArtifactMetadata artifact : metadata.getArtifacts() )%0A                            logger.debug( %22Added metadada for %7B%7D%22, artifact );%0A                    %7D
 %0A   

Improve MetadataReader logging