/** LSB first. Labels may be null, a String, or a String[2] with (low label,high label) values. */
  @Nullable
  protected String getBitFlagDescription(final int tagType, @NotNull final Object... labels) {
    Integer value = _directory.getInteger(tagType);

    if (value == null) return null;

    List<String> parts = new ArrayList<String>();

    int bitIndex = 0;
    while (labels.length > bitIndex) {
      Object labelObj = labels[bitIndex];
      if (labelObj != null) {
        boolean isBitSet = (value & 1) == 1;
        if (labelObj instanceof String[]) {
          String[] labelPair = (String[]) labelObj;
          assert (labelPair.length == 2);
          parts.add(labelPair[isBitSet ? 1 : 0]);
        } else if (isBitSet && labelObj instanceof String) {
          parts.add((String) labelObj);
        }
      }
      value >>= 1;
      bitIndex++;
    }

    return StringUtil.join(parts, ", ");
  }
  /**
   * An application entry point. Takes the name of one or more files as arguments and prints the
   * contents of all metadata directories to <code>System.out</code>.
   *
   * <p>If <code>-thumb</code> is passed, then any thumbnail data will be written to a file with
   * name of the input file having <code>.thumb.jpg</code> appended.
   *
   * <p>If <code>-markdown</code> is passed, then output will be in markdown format.
   *
   * <p>If <code>-hex</code> is passed, then the ID of each tag will be displayed in hexadecimal.
   *
   * @param args the command line arguments
   */
  public static void main(@NotNull String[] args) throws MetadataException, IOException {
    Collection<String> argList = new ArrayList<String>(Arrays.asList(args));
    boolean thumbRequested = argList.remove("-thumb");
    boolean markdownFormat = argList.remove("-markdown");
    boolean showHex = argList.remove("-hex");

    if (argList.size() < 1) {
      String version = ImageMetadataReader.class.getPackage().getImplementationVersion();
      System.out.println("metadata-extractor version " + version);
      System.out.println();
      System.out.println(
          String.format(
              "Usage: java -jar metadata-extractor-%s.jar <filename> [<filename>] [-thumb] [-markdown] [-hex]",
              version == null ? "a.b.c" : version));
      System.exit(1);
    }

    for (String filePath : argList) {
      long startTime = System.nanoTime();
      File file = new File(filePath);

      if (!markdownFormat && argList.size() > 1)
        System.out.printf("\n***** PROCESSING: %s\n%n", filePath);

      Metadata metadata = null;
      try {
        metadata = ImageMetadataReader.readMetadata(file);
      } catch (Exception e) {
        e.printStackTrace(System.err);
        System.exit(1);
      }
      long took = System.nanoTime() - startTime;
      if (!markdownFormat)
        System.out.printf(
            "Processed %.3f MB file in %.2f ms%n%n",
            file.length() / (1024d * 1024), took / 1000000d);

      if (markdownFormat) {
        String fileName = file.getName();
        String urlName = StringUtil.urlEncode(filePath);
        ExifIFD0Directory exifIFD0Directory =
            metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
        String make =
            exifIFD0Directory == null
                ? ""
                : exifIFD0Directory.getString(ExifIFD0Directory.TAG_MAKE);
        String model =
            exifIFD0Directory == null
                ? ""
                : exifIFD0Directory.getString(ExifIFD0Directory.TAG_MODEL);
        System.out.println();
        System.out.println("---");
        System.out.println();
        System.out.printf("# %s - %s%n", make, model);
        System.out.println();
        System.out.printf(
            "<a href=\"https://raw.githubusercontent.com/drewnoakes/metadata-extractor-images/master/%s\">%n",
            urlName);
        System.out.printf(
            "<img src=\"https://raw.githubusercontent.com/drewnoakes/metadata-extractor-images/master/%s\" width=\"300\"/><br/>%n",
            urlName);
        System.out.println(fileName);
        System.out.println("</a>");
        System.out.println();
        System.out.println("Directory | Tag Id | Tag Name | Extracted Value");
        System.out.println(":--------:|-------:|----------|----------------");
      }

      // iterate over the metadata and print to System.out
      for (Directory directory : metadata.getDirectories()) {
        String directoryName = directory.getName();
        for (Tag tag : directory.getTags()) {
          String tagName = tag.getTagName();
          String description = tag.getDescription();

          // truncate the description if it's too long
          if (description != null && description.length() > 1024) {
            description = description.substring(0, 1024) + "...";
          }

          if (markdownFormat) {
            System.out.printf(
                "%s|0x%s|%s|%s%n",
                directoryName, Integer.toHexString(tag.getTagType()), tagName, description);
          } else {
            // simple formatting
            if (showHex) {
              System.out.printf(
                  "[%s - %s] %s = %s%n", directoryName, tag.getTagTypeHex(), tagName, description);
            } else {
              System.out.printf("[%s] %s = %s%n", directoryName, tagName, description);
            }
          }
        }

        // print out any errors
        for (String error : directory.getErrors()) System.err.println("ERROR: " + error);
      }

      if (args.length > 1 && thumbRequested) {
        ExifThumbnailDirectory directory =
            metadata.getFirstDirectoryOfType(ExifThumbnailDirectory.class);
        if (directory != null && directory.hasThumbnailData()) {
          System.out.println("Writing thumbnail...");
          directory.writeThumbnail(args[0].trim() + ".thumb.jpg");
        } else {
          System.out.println("No thumbnail data exists in this image");
        }
      }
    }
  }