/*******************************************************************************
 * Copyright (c) 2004, 2018 Red Hat, Inc. and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Thavidu Ranatunga (IBM) - derived and modified from
 *        org.eclipse.linuxtools.oprofile.launch.configuration.OprofileEventConfigTab
 *******************************************************************************/
package org.eclipse.linuxtools.internal.perf.launch;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.linuxtools.internal.perf.PerfCore;
import org.eclipse.linuxtools.internal.perf.PerfPlugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

public class PerfEventsTab extends AbstractLaunchConfigurationTab {

    private static final String EMPTY_STRING = ""; //$NON-NLS-1$

    // checkbox for selecting default event
    protected Button chkDefaultEvent;

    // the event tabs within the tab folder
    protected TabItem[] eventTabItems;

    // the table within the corresponding event tab
    protected Table[] eventTable;

    protected TabFolder tabFolder;
    private int rawTabIndex = 0;
    private int bpTabIndex = 0;
    protected Text rawText;
    protected Text bpText;
    private Composite top;
    private IProject previousProject = null;

    @Override
    public Image getImage() {
        return PerfPlugin.getImageDescriptor("icons/event.gif").createImage(); //$NON-NLS-1$
    }

    @Override
    public void createControl(Composite parent) {
        Composite top = new Composite(parent, SWT.NONE);
        setControl(top);
        top.setLayout(new GridLayout());
        this.top = top;
    }

    private void createEventTabs(Composite top, ILaunchConfiguration config){
        //Maybe not the best place to load the event list but we'll see.
        Map<String,List<String>> events = PerfCore.getEventList(config);

        // the special counters should be last
        ArrayList<String> tmpTabNames = new ArrayList<>(events.keySet());
        final List<String> SPECIAL_EVENTS = Arrays.asList(new String[] {
                PerfPlugin.STRINGS_HWBREAKPOINTS,
                PerfPlugin.STRINGS_RAWHWEvents });
        tmpTabNames.removeAll(SPECIAL_EVENTS);
        tmpTabNames.addAll(SPECIAL_EVENTS);

        String [] tabNames = tmpTabNames.toArray(new String [0]);
        eventTabItems = new TabItem[tabNames.length];
        eventTable = new Table[tabNames.length];

        tabFolder = new TabFolder(top, SWT.NONE);
        tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // Initialize each tab.
        for (int i = 0; i < tabNames.length; i++) {

            eventTabItems[i] = new TabItem(tabFolder, SWT.NONE);
            eventTabItems[i].setText(tabNames[i]);

            // These are for the two special tabs for custom events.
            if (tabNames[i].equals(PerfPlugin.STRINGS_HWBREAKPOINTS)
                    || tabNames[i].equals(PerfPlugin.STRINGS_RAWHWEvents)) {

                // Composite to contain it all
                Composite c = new Composite(tabFolder, SWT.NONE);
                c.setLayout(new GridLayout(2, false));

                // A list to check off existing custom events (or show the new ones added)
                Table table = new Table(c, SWT.CHECK | SWT.MULTI);
                eventTable[i] = table;
                table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
				table.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
					updateLaunchConfigurationDialog();
				}));

                // Right side to enter new events and delete old ones
                Composite right = new Composite(c, SWT.NONE);
                right.setLayout(new GridLayout(2,false));
                right.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, true));

                // for adding
                Label l = new Label(right, SWT.NONE);
                l.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false,2,1));
                Text t = new Text(right, SWT.SINGLE | SWT.BORDER);
                t.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
                if (tabNames[i].equals(PerfPlugin.STRINGS_HWBREAKPOINTS)) {
                    bpTabIndex = i;
                    bpText = t;
                    l.setText(Messages.PerfEventsTab_HardwareBreakpoint);
                }
                if (tabNames[i].equals(PerfPlugin.STRINGS_RAWHWEvents)) {
                    rawTabIndex = i;
                    rawText = t;
                    l.setText(Messages.PerfEventsTab_RawRegisterEncoding);
                }

                Button b = new Button(right, SWT.PUSH);
                b.setText(Messages.PerfEventsTab_Add);
                b.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
				b.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
					int index = tabFolder.getSelectionIndex();
					if (rawTabIndex == index) {
						new TableItem(eventTable[index], SWT.NONE).setText(rawText.getText());
					} else if (bpTabIndex == index) {
						new TableItem(eventTable[index], SWT.NONE).setText(bpText.getText());
					}
					updateLaunchConfigurationDialog();
				}));
                l = new Label(right, SWT.NONE);
                l.setForeground(new Color(right.getDisplay(), 100,100,100));
                if (tabNames[i].equals(PerfPlugin.STRINGS_HWBREAKPOINTS)) {
                    l.setText(Messages.PerfEventsTab_ForExample);
                }
                if (tabNames[i].equals(PerfPlugin.STRINGS_RAWHWEvents)) {
                    l.setText(Messages.PerfEventsTab_ForExampleR1A8);
                }
                l.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false,2,1));

                // spacer label.
                l = new Label(right, SWT.NONE);
                l.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true,2,1));

                // for removing
                b = new Button(right, SWT.PUSH);
                b.setText(Messages.PerfEventsTab_RemoveSelectedEvents);
                b.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false,2,1));
				b.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
					eventTable[tabFolder.getSelectionIndex()]
							.remove(eventTable[tabFolder.getSelectionIndex()].getSelectionIndices());
					updateLaunchConfigurationDialog();
				}));
                l = new Label(right, SWT.NONE);
                l.setForeground(new Color(right.getDisplay(), 100,100,100));
                l.setText(Messages.PerfEventsTab_Note);
                l.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false,2,1));

                eventTabItems[i].setControl(c);

            } else {
                // This loads all the events 'perf list' gives into their respective tabs.
                Table table = new Table(tabFolder, SWT.CHECK);
                eventTable[i] = table;

                List<String> eventList = events.get(tabNames[i]);
                for (String event : eventList) {
                    TableItem item = new TableItem(table, SWT.NONE);
                    item.setText(event);
                }

				table.addSelectionListener(
						SelectionListener.widgetSelectedAdapter(e -> updateLaunchConfigurationDialog()));

                eventTabItems[i].setControl(table);
            }
        }
    }

    private void refreshDefaultEnabled() {
        boolean state = !chkDefaultEvent.getSelection();
        for (Table tab : eventTable) {
            tab.setEnabled(state);
        }
    }

    @Override
    public String getName() {
        return Messages.PerfEventsTab_Events;
    }

    @Override
    public void initializeFrom(ILaunchConfiguration config) {
        IProject project = getProject(config);

        try {
            if(previousProject == null || (previousProject != null && !previousProject.equals(project))){
                Control[] children = top.getChildren();

                for (Control control : children) {
                    control.dispose();
                }

                createVerticalSpacer(top, 1);

                // Default event checkbox
                chkDefaultEvent = new Button(top, SWT.CHECK);
                chkDefaultEvent.setText("Default Event"); //$NON-NLS-1$
                chkDefaultEvent.setLayoutData(new GridData());
				chkDefaultEvent.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
					refreshDefaultEnabled();
					updateLaunchConfigurationDialog();
				}));

                createEventTabs(top, config);
            }

            // restore whether things are default event/enabled or not.
            chkDefaultEvent.setSelection(config.getAttribute(PerfPlugin.ATTR_DefaultEvent, PerfPlugin.ATTR_DefaultEvent_default));
            refreshDefaultEnabled();

            // restore custom hw breakpoints
            List<?> hwbps = config.getAttribute(PerfPlugin.ATTR_HwBreakpointEvents, PerfPlugin.ATTR_HwBreakpointEvents_default);
            if (hwbps != null) {
                for (int i = 0; i < eventTable.length; i++) {
                    if (eventTabItems[i].getText().equals(PerfPlugin.STRINGS_HWBREAKPOINTS)) {
                        eventTable[i].removeAll();
                        for (Object e : hwbps) {
                            TableItem x = new TableItem(eventTable[i], SWT.NONE);
                            x.setText((String)e);
                        }
                    }
                }
            }

            // restore custom raw hw events
            List<?> rawhe = config.getAttribute(PerfPlugin.ATTR_RawHwEvents, PerfPlugin.ATTR_RawHwEvents_default);
            if (rawhe != null) {
                for (int i = 0; i < eventTable.length; i++) {
                    if (eventTabItems[i].getText().equals(PerfPlugin.STRINGS_RAWHWEvents)) {
                        eventTable[i].removeAll();
                        for (Object e : rawhe) {
                            TableItem x = new TableItem(eventTable[i], SWT.NONE);
                            x.setText((String)e);
                        }
                    }
                }
            }

            // tick all the boxes that are checked
            List<?> selectedEvents = config.getAttribute(PerfPlugin.ATTR_SelectedEvents, PerfPlugin.ATTR_SelectedEvents_default);

            if(selectedEvents != null){
                for (int i = 0; i < eventTable.length; i++) {
                    for(TableItem event : eventTable[i].getItems()) {
                        if(selectedEvents.contains(event.getText())){
                            event.setChecked(true);
                        } else {
                            event.setChecked(false);
                        }
                    }
                }
            }
            previousProject = project;
            } catch (CoreException e) {
                e.printStackTrace();
            }
    }

    @Override
    public void performApply(ILaunchConfigurationWorkingCopy wconfig) {
        //Store default event checkbox
        wconfig.setAttribute(PerfPlugin.ATTR_DefaultEvent, chkDefaultEvent.getSelection());

        //Store which events are selected
        ArrayList<String> selectedEvents = new ArrayList<>();
        for (int i = 0; i < eventTable.length; i++) {
            for(TableItem x : eventTable[i].getItems()) {
                if (x.getChecked())
                    selectedEvents.add(x.getText());
            }
        }

        if (selectedEvents.size() == 0) {
            wconfig.setAttribute(PerfPlugin.ATTR_SelectedEvents, (String) null);
        } else {
            wconfig.setAttribute(PerfPlugin.ATTR_SelectedEvents, selectedEvents);
        }

        //Flag for multiple events
        if ((chkDefaultEvent.getSelection() == false) && !selectedEvents.isEmpty()) {
            wconfig.setAttribute(PerfPlugin.ATTR_MultipleEvents, true);
        } else {
            wconfig.setAttribute(PerfPlugin.ATTR_MultipleEvents, false);
        }
        if (selectedEvents.size() <= 0) {
            //If they unticked the default box but didn't select any events revert to default.
            wconfig.setAttribute(PerfPlugin.ATTR_DefaultEvent, true);
        }

        //Store any custom HW BreakPoints they added (even if unchecked).
        ArrayList<String> hwbps = new ArrayList<>();
        for (int i = 0; i < eventTable.length; i++) {
            if (eventTabItems[i].getText().equals(PerfPlugin.STRINGS_HWBREAKPOINTS)) {
                for(TableItem x : eventTable[i].getItems()) {
                    hwbps.add(x.getText());
                }
            }
        }
        if (hwbps.size() == 0) {
            hwbps = null; // to match with default value.
        }
        wconfig.setAttribute(PerfPlugin.ATTR_HwBreakpointEvents, hwbps);

        //Store any custom Raw HW Events they added (even if unchecked).
        ArrayList<String> rawhwe = new ArrayList<>();
        for (int i = 0; i < eventTable.length; i++) {
            if (eventTabItems[i].getText().equals(PerfPlugin.STRINGS_RAWHWEvents)) {
                for(TableItem x : eventTable[i].getItems()) {
                    rawhwe.add(x.getText());
                }
            }
        }
        if (rawhwe.size() == 0) {
            rawhwe = null; //to match with default value.
        }
        wconfig.setAttribute(PerfPlugin.ATTR_RawHwEvents, rawhwe);
    }

    @Override
    public void setDefaults(ILaunchConfigurationWorkingCopy wconfig) {
        wconfig.setAttribute(PerfPlugin.ATTR_DefaultEvent, PerfPlugin.ATTR_DefaultEvent_default);
        wconfig.setAttribute(PerfPlugin.ATTR_MultipleEvents, PerfPlugin.ATTR_MultipleEvents_default);
        wconfig.setAttribute(PerfPlugin.ATTR_SelectedEvents, PerfPlugin.ATTR_SelectedEvents_default);
        wconfig.setAttribute(PerfPlugin.ATTR_HwBreakpointEvents, PerfPlugin.ATTR_HwBreakpointEvents_default);
        wconfig.setAttribute(PerfPlugin.ATTR_RawHwEvents, PerfPlugin.ATTR_RawHwEvents_default);
    }

    private IProject getProject(ILaunchConfiguration config){
        String name = null;
        try {
            name = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, EMPTY_STRING);
        } catch (CoreException e) {
            return null;
        }
        if (name.isEmpty()){
            return null;
        }

        return ResourcesPlugin.getWorkspace().getRoot().getProject(name);
    }

}
