/** update the visual representation of the selection state of this DrawableView */
 protected synchronized void updateSelectionState() {
   final boolean selected = globalSelectionModel.isSelected(getFileID());
   Platform.runLater(
       () -> {
         setBorder(selected ? SELECTED_BORDER : UNSELECTED_BORDER);
       });
 }
  @SuppressWarnings("deprecation")
  protected void initialize() {
    followUpToggle.setOnAction(
        (ActionEvent t) -> {
          if (followUpToggle.isSelected() == true) {
            globalSelectionModel.clearAndSelect(fileID);
            try {
              AddDrawableTagAction.getInstance().addTag(TagUtils.getFollowUpTagName(), "");
            } catch (TskCoreException ex) {
              LOGGER.log(Level.SEVERE, "Failed to add follow up tag.  Could not load TagName.", ex);
            }
          } else {
            // TODO: convert this to an action!
            final ImageGalleryController controller = ImageGalleryController.getDefault();
            try {
              // remove file from old category group
              controller
                  .getGroupManager()
                  .removeFromGroup(
                      new GroupKey<TagName>(DrawableAttribute.TAGS, TagUtils.getFollowUpTagName()),
                      fileID);

              List<ContentTag> contentTagsByContent =
                  Case.getCurrentCase()
                      .getServices()
                      .getTagsManager()
                      .getContentTagsByContent(file);
              for (ContentTag ct : contentTagsByContent) {
                if (ct.getName()
                    .getDisplayName()
                    .equals(TagUtils.getFollowUpTagName().getDisplayName())) {
                  Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(ct);
                }
              }
              IngestServices.getInstance()
                  .fireModuleDataEvent(
                      new ModuleDataEvent(
                          "TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); // NON-NLS
              controller
                  .getGroupManager()
                  .handleFileUpdate(
                      FileUpdateEvent.newUpdateEvent(
                          Collections.singleton(fileID), DrawableAttribute.TAGS));
            } catch (TskCoreException ex) {
              LOGGER.log(Level.SEVERE, "Failed to delete follow up tag.", ex);
            }
          }
        });
  }
/**
 * An abstract base class for {@link DrawableTile} and {@link SlideShowView}, since they share a
 * similar node tree and many behaviors, other implementers of {@link DrawableView}s should
 * implement the interface directly
 */
public abstract class DrawableViewBase extends AnchorPane implements DrawableView {

  private static final Logger LOGGER = Logger.getLogger(DrawableViewBase.class.getName());

  private static final Border UNSELECTED_BORDER =
      new Border(
          new BorderStroke(
              Color.GRAY, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3)));

  private static final Border SELECTED_BORDER =
      new Border(
          new BorderStroke(
              Color.BLUE, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3)));

  // TODO: do this in CSS? -jm
  protected static final Image videoIcon =
      new Image("org/sleuthkit/autopsy/imagegallery/images/video-file.png");
  protected static final Image hashHitIcon =
      new Image("org/sleuthkit/autopsy/imagegallery/images/hashset_hits.png");
  protected static final Image followUpIcon =
      new Image("org/sleuthkit/autopsy/imagegallery/images/flag_red.png");
  protected static final Image followUpGray =
      new Image("org/sleuthkit/autopsy/imagegallery/images/flag_gray.png");

  protected static final FileIDSelectionModel globalSelectionModel =
      FileIDSelectionModel.getInstance();

  /** displays the icon representing video files */
  @FXML protected ImageView fileTypeImageView;

  /** displays the icon representing hash hits */
  @FXML protected ImageView hashHitImageView;

  /** displays the icon representing follow up tag */
  @FXML protected ImageView followUpImageView;

  @FXML protected ToggleButton followUpToggle;

  /** the label that shows the name of the represented file */
  @FXML protected Label nameLabel;

  @FXML protected BorderPane imageBorder;

  private static ContextMenu contextMenu;

  private DrawableFile<?> file;

  private Long fileID;

  /** the groupPane this {@link DrawableViewBase} is embedded in */
  private final GroupPane groupPane;

  private boolean registered = false;

  GroupPane getGroupPane() {
    return groupPane;
  }

  protected DrawableViewBase(GroupPane groupPane) {
    this.groupPane = groupPane;
    globalSelectionModel
        .getSelected()
        .addListener(
            (Observable observable) -> {
              updateSelectionState();
            });

    // set up mouse listener
    // TODO: split this between DrawableTile and SingleDrawableViewBase
    addEventFilter(
        MouseEvent.MOUSE_CLICKED,
        new EventHandler<MouseEvent>() {

          @Override
          public void handle(MouseEvent t) {

            switch (t.getButton()) {
              case PRIMARY:
                if (t.getClickCount() == 1) {
                  if (t.isControlDown()) {
                    globalSelectionModel.toggleSelection(fileID);
                  } else {
                    groupPane.makeSelection(t.isShiftDown(), fileID);
                  }
                } else if (t.getClickCount() > 1) {
                  groupPane.activateSlideShowViewer(fileID);
                }
                break;
              case SECONDARY:
                if (t.getClickCount() == 1) {
                  if (globalSelectionModel.isSelected(fileID) == false) {
                    groupPane.makeSelection(false, fileID);
                  }
                }

                if (contextMenu != null) {
                  contextMenu.hide();
                }
                final ContextMenu groupContextMenu = groupPane.getContextMenu();
                if (groupContextMenu != null) {
                  groupContextMenu.hide();
                }
                contextMenu = buildContextMenu();
                contextMenu.show(DrawableViewBase.this, t.getScreenX(), t.getScreenY());

                break;
            }
            t.consume();
          }

          private ContextMenu buildContextMenu() {
            final ArrayList<MenuItem> menuItems = new ArrayList<>();

            menuItems.add(CategorizeAction.getPopupMenu());

            menuItems.add(AddDrawableTagAction.getInstance().getPopupMenu());

            final MenuItem extractMenuItem = new MenuItem("Extract File(s)");
            extractMenuItem.setOnAction(
                (ActionEvent t) -> {
                  SwingUtilities.invokeLater(
                      () -> {
                        TopComponent etc =
                            WindowManager.getDefault()
                                .findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
                        ExtractAction.getInstance()
                            .actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
                      });
                });
            menuItems.add(extractMenuItem);

            MenuItem contentViewer = new MenuItem("Show Content Viewer");
            contentViewer.setOnAction(
                (ActionEvent t) -> {
                  SwingUtilities.invokeLater(
                      () -> {
                        new NewWindowViewAction(
                                "Show Content Viewer", new FileNode(getFile().getAbstractFile()))
                            .actionPerformed(null);
                      });
                });
            menuItems.add(contentViewer);

            MenuItem externalViewer = new MenuItem("Open in External Viewer");
            final ExternalViewerAction externalViewerAction =
                new ExternalViewerAction(
                    "Open in External Viewer", new FileNode(getFile().getAbstractFile()));

            externalViewer.setDisable(externalViewerAction.isEnabled() == false);
            externalViewer.setOnAction(
                (ActionEvent t) -> {
                  SwingUtilities.invokeLater(
                      () -> {
                        externalViewerAction.actionPerformed(null);
                      });
                });
            menuItems.add(externalViewer);

            Collection<? extends ContextMenuActionsProvider> menuProviders =
                Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);

            for (ContextMenuActionsProvider provider : menuProviders) {
              for (final Action act : provider.getActions()) {
                if (act instanceof Presenter.Popup) {
                  Presenter.Popup aact = (Presenter.Popup) act;
                  menuItems.add(SwingMenuItemAdapter.create(aact.getPopupPresenter()));
                }
              }
            }

            ContextMenu contextMenu = new ContextMenu(menuItems.toArray(new MenuItem[] {}));
            contextMenu.setAutoHide(true);
            return contextMenu;
          }
        });
  }

  @ThreadConfined(type = ThreadType.UI)
  protected abstract void clearContent();

  protected abstract void disposeContent();

  protected abstract Runnable getContentUpdateRunnable();

  protected abstract String getTextForLabel();

  @SuppressWarnings("deprecation")
  protected void initialize() {
    followUpToggle.setOnAction(
        (ActionEvent t) -> {
          if (followUpToggle.isSelected() == true) {
            globalSelectionModel.clearAndSelect(fileID);
            try {
              AddDrawableTagAction.getInstance().addTag(TagUtils.getFollowUpTagName(), "");
            } catch (TskCoreException ex) {
              LOGGER.log(Level.SEVERE, "Failed to add follow up tag.  Could not load TagName.", ex);
            }
          } else {
            // TODO: convert this to an action!
            final ImageGalleryController controller = ImageGalleryController.getDefault();
            try {
              // remove file from old category group
              controller
                  .getGroupManager()
                  .removeFromGroup(
                      new GroupKey<TagName>(DrawableAttribute.TAGS, TagUtils.getFollowUpTagName()),
                      fileID);

              List<ContentTag> contentTagsByContent =
                  Case.getCurrentCase()
                      .getServices()
                      .getTagsManager()
                      .getContentTagsByContent(file);
              for (ContentTag ct : contentTagsByContent) {
                if (ct.getName()
                    .getDisplayName()
                    .equals(TagUtils.getFollowUpTagName().getDisplayName())) {
                  Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(ct);
                }
              }
              IngestServices.getInstance()
                  .fireModuleDataEvent(
                      new ModuleDataEvent(
                          "TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); // NON-NLS
              controller
                  .getGroupManager()
                  .handleFileUpdate(
                      FileUpdateEvent.newUpdateEvent(
                          Collections.singleton(fileID), DrawableAttribute.TAGS));
            } catch (TskCoreException ex) {
              LOGGER.log(Level.SEVERE, "Failed to delete follow up tag.", ex);
            }
          }
        });
  }

  @Override
  public DrawableFile<?> getFile() {
    if (fileID != null) {
      if (file == null || file.getId() != fileID) {
        try {
          file = ImageGalleryController.getDefault().getFileFromId(fileID);
        } catch (TskCoreException ex) {
          LOGGER.log(Level.WARNING, "failed to get DrawableFile for obj_id" + fileID, ex);
          file = null;
        }
      }
      return file;
    } else {
      return null;
    }
  }

  protected boolean hasFollowUp() throws TskCoreException {
    String followUpTagName = TagUtils.getFollowUpTagName().getDisplayName();
    Collection<TagName> tagNames = DrawableAttribute.TAGS.getValue(getFile());
    return tagNames.stream().anyMatch((tn) -> tn.getDisplayName().equals(followUpTagName));
  }

  @Override
  public synchronized Long getFileID() {
    return fileID;
  }

  @Override
  public synchronized void handleTagsChanged(Collection<Long> ids) {
    if (fileID != null && ids.contains(fileID)) {
      updateFollowUpIcon();
    }
  }

  protected synchronized void updateFollowUpIcon() {
    if (file != null) {
      try {
        boolean hasFollowUp = hasFollowUp();
        Platform.runLater(
            () -> {
              followUpImageView.setImage(hasFollowUp ? followUpIcon : followUpGray);
              followUpToggle.setSelected(hasFollowUp);
            });
      } catch (TskCoreException ex) {
        LOGGER.log(Level.SEVERE, "Failed to get follow up status for file.", ex);
      }
    }
  }

  @Override
  public synchronized void setFile(final Long fileID) {
    if (Objects.equals(fileID, this.fileID) == false) {
      this.fileID = fileID;
      disposeContent();

      if (this.fileID == null || Case.isCaseOpen() == false) {
        if (registered == true) {
          ImageGalleryController.getDefault().getCategoryManager().unregisterListener(this);
          TagUtils.unregisterListener(this);
          registered = false;
        }
        file = null;
        Platform.runLater(
            () -> {
              clearContent();
            });
      } else {
        if (registered == false) {
          ImageGalleryController.getDefault().getCategoryManager().registerListener(this);
          TagUtils.registerListener(this);
          registered = true;
        }
        file = null;
        getFile();
        updateSelectionState();
        updateCategoryBorder();
        updateFollowUpIcon();
        updateUI();
        Platform.runLater(getContentUpdateRunnable());
      }
    }
  }

  private void updateUI() {
    final boolean isVideo = getFile().isVideo();
    final boolean hasHashSetHits = hasHashHit();
    final String text = getTextForLabel();

    Platform.runLater(
        () -> {
          fileTypeImageView.setImage(isVideo ? videoIcon : null);
          hashHitImageView.setImage(hasHashSetHits ? hashHitIcon : null);
          nameLabel.setText(text);
          nameLabel.setTooltip(new Tooltip(text));
        });
  }

  /** update the visual representation of the selection state of this DrawableView */
  protected synchronized void updateSelectionState() {
    final boolean selected = globalSelectionModel.isSelected(getFileID());
    Platform.runLater(
        () -> {
          setBorder(selected ? SELECTED_BORDER : UNSELECTED_BORDER);
        });
  }

  @Override
  public Region getCategoryBorderRegion() {
    return imageBorder;
  }

  @Subscribe
  @Override
  public synchronized void handleCategoryChanged(CategoryChangeEvent evt) {
    if (evt.getIds().contains(getFileID())) {
      updateCategoryBorder();
    }
  }
}
  protected DrawableViewBase(GroupPane groupPane) {
    this.groupPane = groupPane;
    globalSelectionModel
        .getSelected()
        .addListener(
            (Observable observable) -> {
              updateSelectionState();
            });

    // set up mouse listener
    // TODO: split this between DrawableTile and SingleDrawableViewBase
    addEventFilter(
        MouseEvent.MOUSE_CLICKED,
        new EventHandler<MouseEvent>() {

          @Override
          public void handle(MouseEvent t) {

            switch (t.getButton()) {
              case PRIMARY:
                if (t.getClickCount() == 1) {
                  if (t.isControlDown()) {
                    globalSelectionModel.toggleSelection(fileID);
                  } else {
                    groupPane.makeSelection(t.isShiftDown(), fileID);
                  }
                } else if (t.getClickCount() > 1) {
                  groupPane.activateSlideShowViewer(fileID);
                }
                break;
              case SECONDARY:
                if (t.getClickCount() == 1) {
                  if (globalSelectionModel.isSelected(fileID) == false) {
                    groupPane.makeSelection(false, fileID);
                  }
                }

                if (contextMenu != null) {
                  contextMenu.hide();
                }
                final ContextMenu groupContextMenu = groupPane.getContextMenu();
                if (groupContextMenu != null) {
                  groupContextMenu.hide();
                }
                contextMenu = buildContextMenu();
                contextMenu.show(DrawableViewBase.this, t.getScreenX(), t.getScreenY());

                break;
            }
            t.consume();
          }

          private ContextMenu buildContextMenu() {
            final ArrayList<MenuItem> menuItems = new ArrayList<>();

            menuItems.add(CategorizeAction.getPopupMenu());

            menuItems.add(AddDrawableTagAction.getInstance().getPopupMenu());

            final MenuItem extractMenuItem = new MenuItem("Extract File(s)");
            extractMenuItem.setOnAction(
                (ActionEvent t) -> {
                  SwingUtilities.invokeLater(
                      () -> {
                        TopComponent etc =
                            WindowManager.getDefault()
                                .findTopComponent(ImageGalleryTopComponent.PREFERRED_ID);
                        ExtractAction.getInstance()
                            .actionPerformed(new java.awt.event.ActionEvent(etc, 0, null));
                      });
                });
            menuItems.add(extractMenuItem);

            MenuItem contentViewer = new MenuItem("Show Content Viewer");
            contentViewer.setOnAction(
                (ActionEvent t) -> {
                  SwingUtilities.invokeLater(
                      () -> {
                        new NewWindowViewAction(
                                "Show Content Viewer", new FileNode(getFile().getAbstractFile()))
                            .actionPerformed(null);
                      });
                });
            menuItems.add(contentViewer);

            MenuItem externalViewer = new MenuItem("Open in External Viewer");
            final ExternalViewerAction externalViewerAction =
                new ExternalViewerAction(
                    "Open in External Viewer", new FileNode(getFile().getAbstractFile()));

            externalViewer.setDisable(externalViewerAction.isEnabled() == false);
            externalViewer.setOnAction(
                (ActionEvent t) -> {
                  SwingUtilities.invokeLater(
                      () -> {
                        externalViewerAction.actionPerformed(null);
                      });
                });
            menuItems.add(externalViewer);

            Collection<? extends ContextMenuActionsProvider> menuProviders =
                Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);

            for (ContextMenuActionsProvider provider : menuProviders) {
              for (final Action act : provider.getActions()) {
                if (act instanceof Presenter.Popup) {
                  Presenter.Popup aact = (Presenter.Popup) act;
                  menuItems.add(SwingMenuItemAdapter.create(aact.getPopupPresenter()));
                }
              }
            }

            ContextMenu contextMenu = new ContextMenu(menuItems.toArray(new MenuItem[] {}));
            contextMenu.setAutoHide(true);
            return contextMenu;
          }
        });
  }