package imsdebug;

import edu.umiacs.ace.exception.StatusCode;
import edu.umiacs.ace.ims.api.IMSService;
import edu.umiacs.ace.ims.api.RequestBatchCallback;
import edu.umiacs.ace.ims.api.TokenRequestBatch;
import edu.umiacs.ace.ims.ws.TokenRequest;
import edu.umiacs.ace.ims.ws.TokenResponse;
import edu.umiacs.ace.util.HashValue;
import edu.umiacs.io.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.List;

/**
 * Class demonstrating how to register items within the IMS.
 * 
 * Usage: BatchSample <directory>
 * 
 * @author toaster
 */
public class BatchSample
{

    private static final String IMS_HOST = "ims.umiacs.umd.edu";
    private static final int IMS_PORT = 8080;
    private long numTotalRequests = 0;
    private long numTotalResponses = 0;
    private long numTotalErrors = 0;
    private MessageDigest digest;
    private byte[] buffer = new byte[4096];

    public static void main(String[] args) throws Exception
    {

        String dirName = args[0];

        new BatchTest().run(dirName);
    }

    /**
     * Register all the files in a directory.
     * 
     * @param dirName directory to scan 
     * 
     * @throws java.lang.Exception since this is just example code
     */
    public void run(String dirName) throws Exception
    {
        // Create digest service to use for local file digesting
        digest = MessageDigest.getInstance("SHA-256");
        
        // Create connection to IMS
        IMSService ims = IMSService.connect(IMS_HOST, IMS_PORT);
        
        // Create registration service with 1000 files/batch max and 5s delay max
        TokenRequestBatch batch = ims.createImmediateTokenRequestBatch(
                "SHA-256-0", new BatchCallback(), 1000, 5000);
        
        // process all files in the directory
        processDirectory(batch, new File(dirName));
        
        // block until all outstanding requests have been serviced
        batch.close();
        System.out.println("\nComplete: " + numTotalRequests + " request(s), " +
                numTotalResponses + " response(s), " +
                numTotalErrors + " error(s)");

    }

    /**
     * Register all files in the current directory and recurse to any child
     * directories
     * 
     * @param batch batch processor used to register files
     * 
     * @param dir directory to scan for items
     */
    private void processDirectory(TokenRequestBatch batch, File dir)
    {
        System.out.println("Directory: " + dir);
        File[] files = dir.listFiles();
        for ( File file : files )
        {
            if ( file.isDirectory() )
            {
                processDirectory(batch, file);
            }
            else
            {
                processFile(batch, file);
            }
        }
    }

    /**
     * Read in a file, calculate its digest and add it to the current batch
     * 
     * @param batch
     * @param file
     */
    @SuppressWarnings("empty-statement")
    private void processFile(TokenRequestBatch batch, File file)
    {
        numTotalRequests++;
        System.out.println("File: " + file + ", " + file.length());
        DigestInputStream dis = null;
        try
        {
            // reset digest and prepare to read file
            digest.reset();
            dis = new DigestInputStream(new FileInputStream(file), digest);

            // read file, updating digest and it occurs
            while ( dis.read(buffer) >= 0 )
            {
                ;
            }
            byte[] hashValue = digest.digest();
            dis.close();

            // create token request and add it to the current open batch
            // results will be returned via callback registered to batch
            TokenRequest request = new TokenRequest();
            request.setHashValue(HashValue.asHexString(hashValue));
            request.setName(file.getPath());
            batch.add(request);

        }
        catch ( IOException ie )
        {
            System.out.println(file.getPath() + ": IOException: " +
                    ie.getMessage());
            numTotalErrors++;
        }
        catch ( InterruptedException ie )
        {
            Thread.currentThread().interrupt();
        }
        finally
        {
            IO.release(dis);
        }
    }

    /**
     * Simple callback for the token request batch
     */
    class BatchCallback implements RequestBatchCallback
    {

        /**
         * A response was received for a batch of requests. Just becuase this was
         * received, doesn't mean the token is OK. You should check to make sure
         * that the resulting status code is StatusCode.SUCCESS. The result may have
         * mixed successful and unsuccessful responses.
         * 
         * @param requests requests that were sent in the batch.
         * @param responses responses received.
         */
        public void tokensReceived(List<TokenRequest> requests,
                List<TokenResponse> responses)
        {
            int numResponses = 0;
            int numErrors = 0;

            for ( TokenResponse response : responses )
            {
                if ( response.getStatusCode() != StatusCode.SUCCESS )
                {
                    System.out.println("Unsuccessful request: " +
                            "name: " + response.getName() + ", status code: " +
                            response.getStatusCode());
                    numErrors++;
                    numTotalErrors++;
                }
                else
                {
                    System.out.println("Suffessful request: " +
                            "name: " + response.getName() + ", status code: " +
                            response.getStatusCode());
                    numResponses++;
                    numTotalResponses++;
                }
            }
            System.out.println("" + requests.size() + " request(s), " +
                    numResponses + " response(s), " +
                    numErrors + " error(s)");
        }

        /**
         * Exception occurred when attempting to contact the IMS. Most likely this
         * is a network-related error. Any item in this
         * batch was not registered, however the thread is still running and
         * future add attempts may work assuming the underlying connectivity 
         * issue is resolved
         * 
         * @param requests list of requests that was being transmitted when the exception occurred.
         * @param t
         */
        public void exceptionThrown(List<TokenRequest> requests, Throwable t)
        {
            System.out.println("" + requests.size() +
                    " error(s) on exception: " + t.getMessage());
        }

        /**
         * Uncaught throwable occured in the token registion thread. This should
         * never happen and may indicate out of memory or other really bad things.
         * After this exception, the request thread has died and no future requests 
         * will return.
         * 
         * @param t
         */
        public void unexpectedException(Throwable t)
        {
            System.out.println("*** ERROR: " + t.getMessage());
            t.printStackTrace();
        }
    }
    
}