/**
   * Organize all the data required for anonymization.
   *
   * @param cmds the complete set of scripts.
   * @param lkup the local lookup table.
   * @param inDS the input dataset.
   * @param outDS the dataset to be modified
   */
  public DICOMAnonymizerContext(
      Properties cmds, Properties lkup, IntegerTable intTable, Dataset inDS, Dataset outDS) {

    this.cmds = cmds;
    this.lkup = lkup;
    this.intTable = intTable;
    this.inDS = inDS;
    this.inFMI = inDS.getFileMetaInfo();
    this.outDS = outDS;

    // Build the index of private groups
    pgIndex = new PrivateGroupsIndex(inDS);

    // Make a table to hold locally defined names for private elements
    privateElementNames = new Hashtable<String, Integer>();

    // Set up the booleans to handle the global cases
    rpg = (cmds.getProperty("remove.privategroups") != null);
    rue = (cmds.getProperty("remove.unspecifiedelements") != null);
    rol = (cmds.getProperty("remove.overlays") != null);
    rc = (cmds.getProperty("remove.curves") != null);
    kspe = (cmds.getProperty("keep.safeprivateelements") != null);

    // Set up the keepGroups and the script Hashtable
    LinkedList<String> list = new LinkedList<String>();
    scriptTable = new Hashtable<Integer, String>();

    for (Enumeration it = cmds.keys(); it.hasMoreElements(); ) {
      String key = (String) it.nextElement();

      if (key.startsWith("set.[")) {
        int k = findClosingBracket(key, 4);
        if (k > 0) {
          int tag = getElementTag(key.substring(5, k));
          if (tag != 0) {
            // Store the script
            Integer tagInteger = new Integer(tag);
            scriptTable.put(tagInteger, cmds.getProperty(key));

            // If this is a private group element with a name, index the name.
            if ((tag & 0x10000) != 0) {
              String name = key.substring(k + 1).trim();
              if (!name.equals("")) privateElementNames.put(name, tagInteger);
            }
          }
        }
      } else if (key.startsWith("keep.group")) {
        list.add(key.substring("keep.group".length()).trim());
      }
    }

    // Convert the list to an int[]
    Iterator<String> iter = list.iterator();
    keepGroups = new int[list.size()];
    for (int i = 0; i < keepGroups.length; i++) {
      try {
        keepGroups[i] = Integer.parseInt(iter.next(), 16) << 16;
      } catch (Exception ex) {
        keepGroups[i] = 0;
      }
    }
    Arrays.sort(keepGroups);

    inStack = new LinkedList<Dataset>();
    outStack = new LinkedList<Dataset>();
  }
  /**
   * Convert an image to RGB.
   *
   * @param inFile the file to convert.
   * @param outFile the output file, which may be same as inFile.
   * @return the static status result
   */
  public static AnonymizerStatus convert(File inFile, File outFile) {

    long fileLength = inFile.length();
    logger.debug("Entering DICOMPaletteImageConverter.convert");
    logger.debug("File length       = " + fileLength);

    BufferedInputStream in = null;
    BufferedOutputStream out = null;
    File tempFile = null;
    byte[] buffer = new byte[4096];
    try {
      // Check that this is a known format.
      in = new BufferedInputStream(new FileInputStream(inFile));
      DcmParser parser = pFact.newDcmParser(in);
      FileFormat fileFormat = parser.detectFileFormat();
      if (fileFormat == null) {
        throw new IOException("Unrecognized file format: " + inFile);
      }

      // Get the dataset (excluding pixels) and leave the input stream open
      Dataset dataset = oFact.newDataset();
      parser.setDcmHandler(dataset.getDcmHandler());
      parser.parseDcmFile(fileFormat, Tags.PixelData);

      // Make sure this is an image
      if (parser.getReadTag() != Tags.PixelData) {
        close(in);
        return AnonymizerStatus.SKIP(inFile, "Not an image");
      }

      // Get the required parameters and make sure they are okay
      int numberOfFrames = getInt(dataset, Tags.NumberOfFrames, 1);
      int rows = getInt(dataset, Tags.Rows, 0);
      int columns = getInt(dataset, Tags.Columns, 0);
      String photometricInterpretation = getString(dataset, Tags.PhotometricInterpretation, "");
      if ((rows == 0) || (columns == 0)) {
        close(in);
        return AnonymizerStatus.SKIP(inFile, "Unable to get the rows and columns");
      }
      if (!photometricInterpretation.equals("PALETTE COLOR")) {
        close(in);
        return AnonymizerStatus.SKIP(
            inFile, "Unsupported PhotometricInterpretation: " + photometricInterpretation);
      }
      if (parser.getReadTag() != Tags.PixelData) {
        close(in);
        return AnonymizerStatus.SKIP(inFile, "No pixels");
      }

      // Get the encoding and set the parameters
      DcmDecodeParam fileParam = parser.getDcmDecodeParam();
      String fileEncodingUID = UIDs.ImplicitVRLittleEndian;
      FileMetaInfo fmi = dataset.getFileMetaInfo();
      if (fmi != null) fileEncodingUID = fmi.getTransferSyntaxUID();
      boolean isBigEndian = fileEncodingUID.equals(UIDs.ExplicitVRBigEndian);
      String encodingUID = UIDs.ExplicitVRLittleEndian;
      DcmEncodeParam encoding = (DcmEncodeParam) DcmDecodeParam.valueOf(encodingUID);
      boolean swap = (fileParam.byteOrder != encoding.byteOrder);

      if (encoding.encapsulated) {
        logger.debug("Encapsulated pixel data found");
        close(in);
        return AnonymizerStatus.SKIP(inFile, "Encapsulated pixel data not supported");
      }

      // Get the LUTs
      LUT red =
          new LUT(
              dataset.getInts(Tags.RedPaletteColorLUTDescriptor),
              dataset.getInts(Tags.RedPaletteColorLUTData));
      LUT green =
          new LUT(
              dataset.getInts(Tags.GreenPaletteColorLUTDescriptor),
              dataset.getInts(Tags.GreenPaletteColorLUTData));
      LUT blue =
          new LUT(
              dataset.getInts(Tags.BluePaletteColorLUTDescriptor),
              dataset.getInts(Tags.BluePaletteColorLUTData));

      // Set the PlanarConfiguration to 0
      dataset.putUS(Tags.PlanarConfiguration, 0);

      // Set the PhotometricInterpretation to RGB
      dataset.putCS(Tags.PhotometricInterpretation, "RGB");

      // Set the pixel parameters
      dataset.putUS(Tags.SamplesPerPixel, 3);
      dataset.putUS(Tags.BitsAllocated, 8);
      dataset.putUS(Tags.BitsStored, 8);
      dataset.putUS(Tags.HighBit, 7);

      // Remove the lookup tables and their descriptors
      dataset.remove(Tags.RedPaletteColorLUTDescriptor);
      dataset.remove(Tags.GreenPaletteColorLUTDescriptor);
      dataset.remove(Tags.BluePaletteColorLUTDescriptor);
      dataset.remove(Tags.RedPaletteColorLUTData);
      dataset.remove(Tags.GreenPaletteColorLUTData);
      dataset.remove(Tags.BluePaletteColorLUTData);

      // Save the dataset to a temporary file, and rename at the end.
      File tempDir = outFile.getParentFile();
      tempFile = File.createTempFile("DCMtemp-", ".anon", tempDir);
      out = new BufferedOutputStream(new FileOutputStream(tempFile));

      // Create and write the metainfo for the encoding we are using
      fmi = oFact.newFileMetaInfo(dataset, encodingUID);
      dataset.setFileMetaInfo(fmi);
      fmi.write(out);

      // Write the dataset as far as was parsed
      dataset.writeDataset(out, encoding);

      // Process the pixels
      int nPixels = numberOfFrames * rows * columns;
      int nPixelBytes = nPixels * 3 /*samplesPerPixel*/;
      int pad = nPixelBytes & 1;
      dataset.writeHeader(out, encoding, parser.getReadTag(), VRs.OB, nPixelBytes + pad);

      int pd;
      int b1, b2;
      int bytesPerFrame = rows * columns * 2;
      byte[] frameBytes = new byte[bytesPerFrame];
      for (int frame = 0; frame < numberOfFrames; frame++) {
        if (in.read(frameBytes, 0, frameBytes.length) != bytesPerFrame)
          throw new Exception("End of File");
        for (int p = 0; p < bytesPerFrame; ) {
          b1 = frameBytes[p++];
          b2 = frameBytes[p++];
          if (!swap) {
            pd = ((b2 & 0xff) << 8) | (b1 & 0xff);
          } else {
            pd = ((b1 & 0xff) << 8) | (b2 & 0xff);
          }
          out.write(red.get(pd));
          out.write(green.get(pd));
          out.write(blue.get(pd));
        }
      }
      if (pad != 0) out.write(0);
      logger.debug("Finished writing the pixels");

      // Skip everything after the pixels
      out.flush();
      out.close();
      in.close();
      outFile.delete();
      tempFile.renameTo(outFile);
      return AnonymizerStatus.OK(outFile, "");
    } catch (Exception e) {
      logger.debug("Exception while processing image.", e);

      // Close the input stream if it actually got opened.
      close(in);

      // Close the output stream if it actually got opened,
      // and delete the tempFile in case it is still there.
      try {
        if (out != null) {
          out.close();
          tempFile.delete();
        }
      } catch (Exception ex) {
        logger.warn("Unable to close the output stream.");
      }

      // Quarantine the object
      return AnonymizerStatus.QUARANTINE(inFile, e.getMessage());
    }
  }