public static ForensicReport getReport(String urlHash, String mongoHostIP)
     throws UnknownHostException {
   MongoClient mongoclient = new MongoClient(mongoHostIP, 27017);
   Morphia morphia = new Morphia();
   morphia.map(ForensicReport.class).map(dqReport.class);
   Datastore ds = new Morphia().createDatastore(mongoclient, "ForensicDatabase");
   ds.ensureCaps();
   ForensicReport report = ds.get(ForensicReport.class, urlHash);
   if (report != null) {
     JsonParser parser = new JsonParser();
     JsonObject tmpJson = parser.parse(report.metadataStringReport).getAsJsonObject();
     GsonBuilder builder = new GsonBuilder();
     report.metadataObjectReport = builder.create().fromJson(tmpJson, Object.class);
   }
   mongoclient.close();
   return report;
 }
  public static String downloadURL(String urlIn, String folderOut, String mongoHostIP)
      throws IOException {
    System.out.println("downloadURL");
    String imgHash = null;
    byte[] data = null;

    // connect to URL and get input stream
    URL imageURL = new URL(urlIn);
    File localDir = new File(folderOut);
    localDir.mkdir();

    InputStream inputStream = null;
    URLConnection urlConnection = null;
    int noOfBytes = 0;
    byte[] byteChunk = new byte[4096];
    ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
    urlConnection = imageURL.openConnection();
    urlConnection.addRequestProperty(
        "User-Agent",
        "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
    urlConnection.connect();
    inputStream = urlConnection.getInputStream();
    while ((noOfBytes = inputStream.read(byteChunk)) > 0) {
      byteOutputStream.write(byteChunk, 0, noOfBytes);
    }
    // hash creation from image file
    try {
      System.out.println("Start MD5 Digest");
      data = byteOutputStream.toByteArray();
      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(data);
      byte[] hash = md.digest();
      imgHash = String.format("%032x", new java.math.BigInteger(1, hash));
      System.out.println("Hash : " + imgHash);
    } catch (NoSuchAlgorithmException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    MongoClient mongoclient = new MongoClient(mongoHostIP, 27017);
    // System.out.println("mongoHostIP :: " + mongoHostIP);
    Morphia morphia = new Morphia();
    morphia.map(ForensicReport.class).map(dqReport.class);
    Datastore ds = new Morphia().createDatastore(mongoclient, "ForensicDatabase");
    ds.ensureCaps();

    String baseFolder = folderOut + imgHash + "/";
    ForensicReport report = ds.get(ForensicReport.class, imgHash);
    // check if hash exist
    if (report != null) {
      System.out.println("Exists");
    } else {
      // if hash does not exist in database, then download the image
      report = new ForensicReport();
      report.id = imgHash;
      try {
        File writeFolder = new File(baseFolder);
        if (!writeFolder.exists()) writeFolder.mkdirs();
        File imageFile = new File(baseFolder, "Raw");
        OutputStream outputStream = new FileOutputStream(imageFile);

        byteOutputStream.writeTo(outputStream);
        outputStream.close();
        BufferedImage downloadedImage = ImageIO.read(imageFile);
        ImageIO.write(downloadedImage, "JPEG", new File(baseFolder, "Display.jpg"));
        // store in database image information
        report.sourceImage = baseFolder + "Raw";
        report.displayImage = baseFolder + "Display.jpg";
        report.sourceURL = urlIn;
        report.status = "Downloaded";
        ds.save(report);
      } catch (Exception e) {
        e.printStackTrace();
        mongoclient.close();
        return "URL_ERROR";
      }
    }
    mongoclient.close();
    System.out.println("Downloaded.");
    return imgHash;
  }
  public static String reportCalculation(
      String urlHash,
      String mongoHostIP,
      String folderOut,
      int maxGhostImageSmallDimension,
      int numGhostThreads,
      int numberOfThreads,
      long computationTimeoutLimit)
      throws UnknownHostException {
    String outMessage = "COMPLETEDSUCCESSFULLY";

    ExecutorService threadpool = Executors.newFixedThreadPool(numberOfThreads);
    MongoClient mongoclient = new MongoClient(mongoHostIP, 27017);
    Morphia morphia = new Morphia();
    morphia.map(ForensicReport.class).map(dqReport.class);
    Datastore ds = new Morphia().createDatastore(mongoclient, "ForensicDatabase");
    ds.ensureCaps();
    String baseFolder = folderOut + urlHash + "/";
    ForensicReport report = ds.get(ForensicReport.class, urlHash);

    if (report == null) {
      mongoclient.close();
      return "HASHNOTFOUND";
    }
    if (report.status.equalsIgnoreCase("Processing")) {
      mongoclient.close();
      return "ALREADYPROCESSING";
    }
    if (report.status.equalsIgnoreCase("Done")) {
      mongoclient.close();
      return "PROCESSINGALREADYCOMPLETE";
    }
    System.out.println("start processing");
    report.status = "Processing";
    dqReport dqReport = new dqReport();
    ELAReport elaReport = new ELAReport();
    GhostReport ghostReport = new GhostReport();
    DWNoiseReport dwNoiseReport = new DWNoiseReport();
    BlockingReport blockingReport = new BlockingReport();
    MedianNoiseReport medianNoiseReport = new MedianNoiseReport();
    GPSReport gpsReport = new GPSReport();
    GridsNormalReport gridsReport = new GridsNormalReport();
    GridsInversedReport gridsInversedReport = new GridsInversedReport();

    File dqOutputfile = new File(baseFolder, "DQOutput.png");
    File dwNoiseOutputfile = new File(baseFolder, "DWNoiseOutput.png");
    File ghostOutputfile;
    File elaOutputfile = new File(baseFolder, "ELAOutput.png");
    File blkOutputfile = new File(baseFolder, "BLKOutput.png");
    File medianNoiseOutputFile = new File(baseFolder, "MedianNoiseOutput.png");
    File gridsOutputFile = new File(baseFolder, "GridsOutput.png");
    File gridsInversedOutputFile = new File(baseFolder, "GridsInversedOutput.png");
    try {
      if (ImageIO.read(new File(report.sourceImage)).getColorModel().hasAlpha()) {
        System.out.println(
            "If image has an alpha channel, then assume transparent PNG -No point in processing it");
        // If image has an alpha channel, then assume transparent PNG -No point in processing it
        BufferedImage transparentPNGNotAccepted = ArtificialImages.transparentPNGNotAccepted();
        ImageIO.write(transparentPNGNotAccepted, "png", dqOutputfile);
        System.out.println(
            " dqOutputfile.getCanonicalPath() 1 :: " + dqOutputfile.getCanonicalPath());
        dqReport.map = dqOutputfile.getCanonicalPath();
        dqReport.completed = true;
        report.dqReport = dqReport;
        ImageIO.write(transparentPNGNotAccepted, "png", dwNoiseOutputfile);
        System.out.println(
            " dwNoiseOutputfile.getCanonicalPath() :: " + dwNoiseOutputfile.getCanonicalPath());
        dwNoiseReport.map = dwNoiseOutputfile.getCanonicalPath();
        dwNoiseReport.completed = true;
        report.dwNoiseReport = dwNoiseReport;
        ghostOutputfile = new File(baseFolder, "GhostOutput" + String.format("%02d", 0) + ".png");
        ImageIO.write(transparentPNGNotAccepted, "png", ghostOutputfile);
        ghostReport.maps.add(ghostOutputfile.getCanonicalPath());
        System.out.println(
            " ghostOutputfile.getCanonicalPath() :: " + ghostOutputfile.getCanonicalPath());
        ghostReport.differences.add((float) 0.0);
        ghostReport.qualities.add(0);
        ghostReport.minValues.add((float) 0.0);
        ghostReport.maxValues.add((float) 0.0);
        ghostReport.completed = true;
        report.ghostReport = ghostReport;
        ImageIO.write(transparentPNGNotAccepted, "png", elaOutputfile);
        System.out.println(
            " elaOutputfile.getCanonicalPath() :: " + elaOutputfile.getCanonicalPath());
        elaReport.map = elaOutputfile.getCanonicalPath();
        elaReport.completed = true;
        report.elaReport = elaReport;
        ImageIO.write(transparentPNGNotAccepted, "png", blkOutputfile);
        blockingReport.map = blkOutputfile.getCanonicalPath();
        System.out.println(
            " blkOutputfile.getCanonicalPath() :: " + blkOutputfile.getCanonicalPath());
        blockingReport.completed = true;
        report.blockingReport = blockingReport;
        ImageIO.write(transparentPNGNotAccepted, "png", medianNoiseOutputFile);
        medianNoiseReport.map = medianNoiseOutputFile.getCanonicalPath();
        System.out.println(
            " medianNoiseOutputFile.getCanonicalPath() :: "
                + medianNoiseOutputFile.getCanonicalPath());
        medianNoiseReport.completed = true;
        report.medianNoiseReport = medianNoiseReport;
        ImageIO.write(transparentPNGNotAccepted, "png", gridsOutputFile);
        gridsReport.map = gridsOutputFile.getCanonicalPath();
        System.out.println(
            " gridsOutputFile.getCanonicalPath() :: " + gridsOutputFile.getCanonicalPath());
        ImageIO.write(transparentPNGNotAccepted, "png", gridsInversedOutputFile);
        gridsInversedReport.map = gridsInversedOutputFile.getCanonicalPath();
        System.out.println(
            " gridsInversedOutputFile.getCanonicalPath() :: "
                + gridsInversedOutputFile.getCanonicalPath());
        gridsReport.completed = true;
        gridsInversedReport.completed = true;
        report.gridsReport = gridsReport;
        report.gridsInversedReport = gridsInversedReport;
        ds.save(report);
      } else {

        Boolean dqSaved = false,
            noiseDWSaved = false,
            ghostSaved = false,
            elaSaved = false,
            blkSaved = false,
            medianNoiseSaved = false,
            gridsSaved = false;
        DQThread dqThread = new DQThread(report.sourceImage, dqOutputfile);
        Future dqFuture = threadpool.submit(dqThread);
        noiseDWThread noiseDWThread = new noiseDWThread(report.sourceImage, dwNoiseOutputfile);
        Future noiseDWFuture = threadpool.submit(noiseDWThread);
        GhostThread ghostThread =
            new GhostThread(
                report.sourceImage, baseFolder, maxGhostImageSmallDimension, numGhostThreads);
        Future ghostFuture = threadpool.submit(ghostThread);
        ELAThread elaThread = new ELAThread(report.sourceImage, elaOutputfile);
        Future elaFuture = threadpool.submit(elaThread);
        BLKThread blkThread = new BLKThread(report.sourceImage, blkOutputfile);
        Future blkFuture = threadpool.submit(blkThread);
        MedianNoiseThread medianNoiseThread =
            new MedianNoiseThread(report.sourceImage, medianNoiseOutputFile);
        Future medianNoiseFuture = threadpool.submit(medianNoiseThread);
        GridsThread gridsThread =
            new GridsThread(report.sourceImage, gridsOutputFile, gridsInversedOutputFile);
        Future gridsFuture = threadpool.submit(gridsThread);
        //  GridsInversedThread gridsInversedThread = new
        // GridsInversedThread(report.sourceImage,gridsInversedOutputFile);
        //  Future gridsInversedFuture = threadpool.submit(gridsInversedThread);

        Long startTime = System.currentTimeMillis();
        MetadataExtractor metaExtractor;
        metaExtractor = new MetadataExtractor(report.sourceImage);
        JsonObject metadataReport = metaExtractor.metadataReport;
        metadataReport.addProperty("completed", true);
        report.metadataStringReport = metadataReport.toString();
        ds.save(report);

        GPSExtractor gpsExtractor = new GPSExtractor(metadataReport);
        gpsReport.completed = true;
        gpsReport.exists = gpsExtractor.exists;
        gpsReport.latitude = gpsExtractor.latitude;
        gpsReport.longitude = gpsExtractor.longitude;
        report.gpsReport = gpsReport;
        ds.save(report);

        ThumbnailReport thumbnail = new ThumbnailReport();
        ThumbnailExtractor thumbnailExtractor;
        thumbnailExtractor = new ThumbnailExtractor(report.sourceImage);
        thumbnail.numberOfThumbnails = thumbnailExtractor.numberOfThumbnails;
        File thumbFile = null;
        for (int thumbInd = 0; thumbInd < thumbnailExtractor.numberOfThumbnails; thumbInd++) {
          thumbFile = new File(baseFolder, "Thumbnail" + String.valueOf(thumbInd) + ".png");
          ImageIO.write(thumbnailExtractor.thumbnails.get(thumbInd), "png", thumbFile);
          thumbnail.thumbnailList.add(thumbFile.getCanonicalPath());
        }

        report.thumbnailReport = thumbnail;
        ds.save(report);

        while (!dqFuture.isDone()
            | !noiseDWFuture.isDone()
            | !ghostFuture.isDone()
            | !elaFuture.isDone()
            | !blkFuture.isDone()
            | !medianNoiseFuture.isDone()
            | !gridsFuture.isDone()) {
          Thread.sleep(100); // sleep for 1 millisecond before checking again
          if (dqFuture.isDone() & !dqSaved) {
            report.dqReport = (dqReport) dqFuture.get();
            dqSaved = true;
            ds.save(report);
            System.out.println("dqReport Done");
          }
          if (noiseDWFuture.isDone() & !noiseDWSaved) {
            report.dwNoiseReport = (DWNoiseReport) noiseDWFuture.get();
            noiseDWSaved = true;
            ds.save(report);
            System.out.println("DWNoiseReport Done");
          }
          if (ghostFuture.isDone() & !ghostSaved) {
            report.ghostReport = (GhostReport) ghostFuture.get();
            ghostSaved = true;
            ds.save(report);
            System.out.println("Ghost Done");
          }
          if (elaFuture.isDone() & !elaSaved) {
            report.elaReport = (ELAReport) elaFuture.get();
            elaSaved = true;
            ds.save(report);
            System.out.println("elaReport Done");
          }
          if (blkFuture.isDone() & !blkSaved) {
            report.blockingReport = (BlockingReport) blkFuture.get();
            blkSaved = true;
            ds.save(report);
            System.out.println("blockingReport Done");
          }
          if (medianNoiseFuture.isDone() & !medianNoiseSaved) {
            report.medianNoiseReport = (MedianNoiseReport) medianNoiseFuture.get();
            medianNoiseSaved = true;
            ds.save(report);
            System.out.println("Median Noise Done");
          }
          if (gridsFuture.isDone() & !gridsSaved) {
            GridsBothReport gridsBothReport = (GridsBothReport) gridsFuture.get();
            report.gridsReport = gridsBothReport.gridsNormalReport;
            report.gridsReport.completed = true;
            report.gridsInversedReport = gridsBothReport.gridsInversedReport;
            report.gridsInversedReport.completed = true;
            gridsSaved = true;
            ds.save(report);
            System.out.println("GridsReport Done");
          }
          if ((System.currentTimeMillis() - startTime) > computationTimeoutLimit) {
            System.out.println("Computation timed out");
            outMessage = "TIMEDOUT";
            // kill if timeout
            dqFuture.cancel(true);
            noiseDWFuture.cancel(true);
            ghostFuture.cancel(true);
            blkFuture.cancel(true);
            medianNoiseFuture.cancel(true);
            elaFuture.cancel(true);
            gridsFuture.cancel(true);
            break;
          }
        }
        threadpool.shutdown();
      }
    } catch (Exception e) {
      threadpool.shutdown();
      e.printStackTrace();
    }
    report.status = "Done";
    ds.save(report);
    System.out.println("Will now close mongodb connection");
    mongoclient.close();
    return outMessage;
  }