/* ###
 * 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 docking;

import javax.swing.Icon;

import docking.action.*;
import docking.actions.AutoGeneratedDockingAction;
import ghidra.util.HelpLocation;
import resources.Icons;

/**
 * Action for showing components.  If the component is hidden it will be made visible.
 * If it is tabbed, it will become the top tab. In all cases it will receive focus.
 */
class ShowComponentAction extends DockingAction
		implements AutoGeneratedDockingAction, Comparable<ShowComponentAction> {
	private static final int MAX_LENGTH = 40;

	protected static final Icon EMPTY_ICON = Icons.EMPTY_ICON;
	protected static final String MENU_WINDOW = "&" + DockingWindowManager.COMPONENT_MENU_NAME;

	protected DockingWindowManager winMgr;
	private ComponentPlaceholder info;
	private String title;

	private static String truncateTitleAsNeeded(String title) {
		if (title.length() <= MAX_LENGTH) {
			return title;
		}

		return title.substring(0, MAX_LENGTH - 3) + "...";
	}

	protected ShowComponentAction(DockingWindowManager winMgr, String name, String subMenuName) {
		super(truncateTitleAsNeeded(name), DockingWindowManager.DOCKING_WINDOWS_OWNER);
		this.winMgr = winMgr;
	}

	ShowComponentAction(DockingWindowManager winMgr, ComponentPlaceholder placeholder,
			String subMenuName, boolean isTransient) {
		super(placeholder.getProvider().getName(), DockingWindowManager.DOCKING_WINDOWS_OWNER,
			createKeyBindingType(isTransient, placeholder));

		this.info = placeholder;
		this.winMgr = winMgr;
		this.title = truncateTitleAsNeeded(placeholder.getTitle());

		Icon icon = placeholder.getIcon();
		if (icon == null) {
			icon = EMPTY_ICON;
		}

		String[] menuPath = subMenuName != null
				? new String[] { MENU_WINDOW, subMenuName, "temporary_placeholder" }
				: new String[] { MENU_WINDOW, "temporary_placeholder" };
		MenuData menuData = new MenuData(menuPath, icon, "Permanent");
		// defer setting the menu name until after the ctor so we can specify it using
		// MenuData.setMenuItemNamePlain(), which won't parse the string for '&'s.
		menuData.setMenuItemNamePlain(subMenuName != null ? placeholder.getFullTitle() : title);
		setMenuBarData(menuData);

		// keybinding data used to show the binding in the menu
		ComponentProvider provider = placeholder.getProvider();
		synchronizeKeyBinding(provider);

		// Use provider Help for this action		
		HelpLocation helpLocation = provider.getHelpLocation();
		if (helpLocation != null) {
			setHelpLocation(helpLocation);
		}
		else {
			// This action only exists as a convenience for users to show a provider from the menu.
			// There is no need for this action itself to report errors if no help exists.
			markHelpUnnecessary();
		}
	}

	/**
	 * Ensures that the given provider's key binding matches this class's key binding
	 * @param provider the provider
	 */
	private void synchronizeKeyBinding(ComponentProvider provider) {

		if (!getKeyBindingType().supportsKeyBindings()) {
			return;
		}

		DockingActionIf action = provider.getShowProviderAction();
		KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
		setKeyBindingData(defaultBinding);

		KeyBindingData kbData = action.getKeyBindingData();
		if (kbData != null) {
			setUnvalidatedKeyBindingData(kbData);
		}
	}

	private static KeyBindingType createKeyBindingType(boolean isTransient,
			ComponentPlaceholder placeholder) {

		if (isTransient) {
			return KeyBindingType.UNSUPPORTED; // temporary window
		}

		// 'info' is null when this action is used to 'show all' instances of a given provider
		return placeholder == null ? KeyBindingType.UNSUPPORTED : KeyBindingType.SHARED;
	}

	@Override
	public void actionPerformed(ActionContext context) {
		winMgr.showComponent(info, true, true, true);
	}

	@Override
	public boolean isEnabledForContext(ActionContext context) {
		return true;
	}

	// this compare method must take into account that there is sorting amongst top-level menus
	// and sub-menus, while understanding that they will all end up in one datastructure 
	@Override
	public int compareTo(ShowComponentAction other) {
		String[] myMenuPath = getMenuBarData().getMenuPath();
		String[] otherMenuPath = other.getMenuBarData().getMenuPath();

		// compare each path downward until they are no longer equal
		int loopLength = Math.min(myMenuPath.length, otherMenuPath.length);
		for (int i = 0; i < loopLength; i++) {
			int result = myMenuPath[i].compareTo(otherMenuPath[i]);
			if (result != 0) {
				return result;
			}
		}

		// return the smaller path first (arbitrary)
		return myMenuPath.length - otherMenuPath.length;
	}

	@Override
	public String getHelpInfo() {
		if (info == null) {
			return super.getHelpInfo();
		}

		StringBuilder buffy = new StringBuilder(super.getHelpInfo());

		ComponentProvider provider = info.getProvider();
		Class<? extends ComponentProvider> clazz = provider.getClass();
		String className = clazz.getName();
		String filename = className.substring(className.lastIndexOf('.') + 1);

		DockingActionIf showAction = provider.getShowProviderAction();
		String realInception = showAction.getInceptionInformation();
		buffy.append("           ").append(realInception).append("\n    ");

		buffy.append("    ").append("PROVIDER:    ").append(filename).append(' ');
		buffy.append('(').append(provider.getOwner()).append(")");
		buffy.append("\n    ");

		return buffy.toString();
	}
}
