/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ghidra.bsfv;

import java.awt.*;

import javax.swing.*;

import ghidra.app.decompiler.DecompilerHighlightService;
import ghidra.app.decompiler.DecompilerHighlighter;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.table.*;

/**
 * This class is the {@link ComponentProviderAdapter} for the BSim feature table.
 */
public class BsfvTableProvider extends ComponentProviderAdapter {

	private BSimFeatureVisualizerPlugin plugin;
	private JComponent component;
	private GhidraTable bsimFeatureTable;
	private GhidraThreadedTablePanel<BsfvRowObject> bsimFeatureTablePanel;
	private BsfvTableModel model;
	private GhidraTableFilterPanel<BsfvRowObject> tableFilterPanel;
	private JPanel panel;
	private Component table;
	private ClearDecompilerHighlightsAction clearingAction;

	/**
	 * Creates a provider for the BSim feature table.
	 * @param plugin owning plugin
	 */
	public BsfvTableProvider(BSimFeatureVisualizerPlugin plugin) {
		super(plugin.getTool(), "BSimFeatureVisualizer", plugin.getName());
		this.plugin = plugin;
		component = build();
		setTransient();
		tool.addComponentProvider(this, true);
		setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
		HighlightAndGraphAction highlightAndGraphAction = new HighlightAndGraphAction(this, plugin);
		tool.addLocalAction(this, highlightAndGraphAction);
		clearingAction = new ClearDecompilerHighlightsAction(plugin);
		tool.addLocalAction(this, clearingAction);
	}

	@Override
	public JComponent getComponent() {
		return component;
	}

	/**
	 * Removes any decompiler highlights generated by this plugin, reloads the table, and sets the 
	 * title.
	 */
	public void reload() {
		if (isVisible()) {
			clearingAction.clearHighlights();
			model.reload(plugin.getCurrentProgram());
			setTitle(getTitleText());
		}
	}

	/**
	 * Reloads the table and sets the title
	 * @param program new program
	 */
	public void programOpened(Program program) {
		if (isVisible()) {
			model.reload(program);
			setTitle(getTitleText());
		}
	}

	/**
	 * Clears any decompiler highlights associated with this plugin, empties the table, and resets
	 * the table title.
	 */
	public void programDeactivated() {
		clearingAction.clearHighlights();
		model.reload(null);
		setTitle(getTitleText());

	}

	/**
	 * Removes {@code this} from the tool.
	 */
	void dispose() {
		tool.removeComponentProvider(this);
	}

	@Override
	public void componentShown() {
		model.reload(plugin.getCurrentProgram());
	}

	@Override
	public void componentHidden() {
		clearingAction.clearHighlights();
	}

	/**
	 * Returns the underlying {@link GhidraTable} of the BSim feature table
	 * @return table
	 */
	GhidraTable getTable() {
		return bsimFeatureTable;
	}

	/**
	 * Returns the BSim feature table model
	 * @return table model
	 */
	BsfvTableModel getModel() {
		return model;
	}

	private String getTitleText() {
		String title = "BSim Feature Visualizer";
		if (plugin.getCurrentProgram() == null) {
			return title;
		}
		ProgramLocation loc = plugin.getProgramLocation();
		if (loc == null) {
			return title;  //can happen when switching between tabbed programs in the listing
		}
		Function func =
			plugin.getCurrentProgram().getFunctionManager().getFunctionContaining(loc.getAddress());
		if (func == null) {
			return title;
		}
		return title + ": " + func.getName();
	}

	private JComponent build() {
		panel = new JPanel(new BorderLayout());
		table = buildTablePanel();
		panel.add(table, BorderLayout.CENTER);
		return panel;
	}

	private Component buildTablePanel() {

		model = new BsfvTableModel(plugin, plugin.getCurrentProgram());
		bsimFeatureTablePanel = new GhidraThreadedTablePanel<>(model, 1000);
		bsimFeatureTable = bsimFeatureTablePanel.getTable();
		bsimFeatureTable.setName("BSim Features");

		bsimFeatureTable.installNavigation(tool);
		bsimFeatureTable.setNavigateOnSelectionEnabled(true);
		bsimFeatureTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
		bsimFeatureTable.setPreferredScrollableViewportSize(new Dimension(900, 300));
		bsimFeatureTable.setRowSelectionAllowed(true);
		bsimFeatureTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		bsimFeatureTable.getSelectionModel().addListSelectionListener(e -> {
			if (!plugin.getHighlightByRow()) {
				return;
			}
			DecompilerHighlightService service = plugin.getDecompilerHighlightService();
			if (service == null) {
				return;
			}
			int selectedRow = bsimFeatureTable.getSelectedRow();
			if (selectedRow == -1) {
				return;
			}
			BsfvRowObject row = model.getRowObject(selectedRow);
			BsfvTokenHighlightMatcher tokenMatcher =
				new BsfvTokenHighlightMatcher(row, model.getHighFunction(), plugin);
			DecompilerHighlighter highlighter = service.createHighlighter(
				HighlightAndGraphAction.BSIM_FEATURE_HIGHLIGHTER_NAME, tokenMatcher);
			highlighter.applyHighlights();
		});

		model.addTableModelListener(e -> {
			int rowCount = model.getRowCount();
			int unfilteredCount = model.getUnfilteredRowCount();

			StringBuilder buffy = new StringBuilder();

			buffy.append(rowCount).append(" items");
			if (rowCount != unfilteredCount) {
				buffy.append(" (of ").append(unfilteredCount).append(" )");
			}

			setSubTitle(buffy.toString());
		});

		tableFilterPanel = new GhidraTableFilterPanel<>(bsimFeatureTable, model);

		JPanel container = new JPanel(new BorderLayout());
		container.add(bsimFeatureTablePanel, BorderLayout.CENTER);
		container.add(tableFilterPanel, BorderLayout.SOUTH);
		return container;
	}

}
