//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   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 de.ugoe.cs.autoquest.ui.swt;

import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.autoquest.usability.UsabilitySmell;
import de.ugoe.cs.autoquest.usability.UsabilitySmell.ManualLabel;
import de.ugoe.cs.autoquest.usability.UsabilitySmellIntensity;
import de.ugoe.cs.autoquest.usability.UsabilityEvaluationResult;

/**
 * <p>
 * a dialog to inspect the results of a usability evaluation
 * TODO update comments
 * </p>
 * 
 * @author Patrick Harms
 */
public class ShowUsabilityEvaluationResultDialog extends Dialog {

    /** the main shell */
    protected Shell shell;

    /** the table containing all smells */
    private Tree smellList;

    /** the description label of a selected smell */
    private StyledText description;
    
    /** the tree of involved GUI elements of a specific smell on the right */
    private Tree involvedTargetsTree;

    /** the table containing the involved tasks of a displayed smell */
    private Tree involvedTasks;
    
    /** the radio button group for assessing if a smell is true positive or not */
    private Group assessmentRadioButtons;
    
    /** the list of all different tags given for smells */
    private List<String> existingTags = new LinkedList<>();

    /** the parent of all check boxes for tag representation */
    private Composite tagCheckBoxParent;

    /** the displayed usability evaluation result */
    private UsabilityEvaluationResult usabilityEvalResult;
    
    /** task tree dialog to show task details */
    private ShowTaskTreeDialog showTaskTreeDialog;

    /**
     * creates the dialog
     */
    public ShowUsabilityEvaluationResultDialog(Shell                     parent,
                                               int                       style,
                                               UsabilityEvaluationResult usabilityEvalResult,
                                               String                    dataSetName)
    {
        super(parent, style);
        setText("Usability Evaluation Result " + dataSetName);
        this.usabilityEvalResult = usabilityEvalResult;
        
        for (UsabilitySmell smell : usabilityEvalResult.getAllSmells()) {
            for (String tag : smell.getTags()) {
                if (!existingTags.contains(tag)) {
                    existingTags.add(tag);
                }
            }
        }
        
        Collections.sort(existingTags);
    }

    /**
     * displays the dialog
     */
    public void open() {
        showTaskTreeDialog = new ShowTaskTreeDialog
            (super.getParent(), SWT.NONE, usabilityEvalResult.getTaskModel(),
             "task details of usability smells");

        createContents();
        shell.open();
        shell.layout();
        
        shell.addShellListener(new ShellAdapter() {
            @Override
            public void shellClosed(ShellEvent e) {
                showTaskTreeDialog.dispose();
            }
        });
        
        Display display = getParent().getDisplay();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * creates the two views, one on the task instances on the left, on on the task models on the
     * right. Also adds a selection adapter to the task instances so that for a selected task
     * instance always the respective model is presented.
     */
    private void createContents() {
        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER);
        GraphicsDevice gd =
            GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();

        shell.setSize(gd.getDisplayMode().getWidth(), gd.getDisplayMode().getHeight());
        shell.setText(getText());

        shell.setLayout(new GridLayout(1, false));

        SashForm mainSashForm = new SashForm(shell, SWT.HORIZONTAL);
        mainSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        
        smellList = new Tree(mainSashForm, SWT.BORDER | SWT.SINGLE | SWT.VIRTUAL);
        smellList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        smellList.setHeaderVisible(true);
        //smellList.setLinesVisible(true);

        TreeColumn treeColumn = new TreeColumn(smellList, SWT.HORIZONTAL);
        treeColumn.setWidth(300);
        treeColumn.setText("smells");

        buildSmellTree();
        
        smellList.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                TreeItem[] selectedItems = smellList.getSelection();
                if ((selectedItems.length == 1) &&
                    (selectedItems[0].getData() instanceof UsabilitySmell))
                {
                    displaySmellDetails((UsabilitySmell) selectedItems[0].getData());
                }
                else {
                    clearSmellDetails();
                }
                
                VisualizationUtils.updateColumnWidths(smellList);
            }
        });
        
        
        SashForm detailsSashForm = new SashForm(mainSashForm, SWT.VERTICAL);
        detailsSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        
        Composite descriptionComposite = new Composite(detailsSashForm, SWT.NONE);
        descriptionComposite.setLayout(new GridLayout());

        description = new StyledText(descriptionComposite, SWT.READ_ONLY | SWT.BORDER | SWT.WRAP);
        description.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

        SashForm detailsTreesSashForm = new SashForm(detailsSashForm, SWT.HORIZONTAL);
        detailsTreesSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        
        involvedTasks = VisualizationUtils.createTaskDetailsTree
            (detailsTreesSashForm, "involved tasks", usabilityEvalResult.getTaskModel());
        
        VisualizationUtils.addItemSpecificContextMenu
            (involvedTasks, ITask.class, "show details", new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e) {
                showTaskTreeDialog.open((ITask) involvedTasks.getSelection()[0].getData());
            }
        });
        
        VisualizationUtils.addExpansionListener(involvedTasks, new Listener() {
            public void handleEvent(final Event event) {
                ensureChildren((TreeItem) event.item);
                ((TreeItem) event.item).setExpanded(true);
            }
        });
        
        involvedTargetsTree =
            VisualizationUtils.createTargetsTree(detailsTreesSashForm, "involved GUI elements");
        
        VisualizationUtils.addInvolvedTargetsHighlighting(involvedTasks, involvedTargetsTree);
        
        final Composite detailsAssessmentComposite = new Composite(descriptionComposite, SWT.NONE);
        detailsAssessmentComposite.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false, 1, 1));
        detailsAssessmentComposite.setLayout(new GridLayout(5, false));
        
        assessmentRadioButtons = new Group(detailsAssessmentComposite, SWT.NONE);
        assessmentRadioButtons.setLayout(new RowLayout(SWT.HORIZONTAL));
        
        final Button unassessedRadioButton = new Button(assessmentRadioButtons, SWT.RADIO);
        unassessedRadioButton.setText("unassessed");
        unassessedRadioButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                TreeItem[] selectedItems = smellList.getSelection();
                if ((selectedItems.length == 1) &&
                    (selectedItems[0].getData() instanceof UsabilitySmell))
                {
                    UsabilitySmell smell = (UsabilitySmell) selectedItems[0].getData();
                    if (unassessedRadioButton.getSelection()) {
                        smell.setManualLabel(ManualLabel.UNCHECKED);
                    }
                }
            }
        });
        
        final Button falsePositiveRadioButton = new Button(assessmentRadioButtons, SWT.RADIO);
        falsePositiveRadioButton.setText("false positive");
        falsePositiveRadioButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                TreeItem[] selectedItems = smellList.getSelection();
                if ((selectedItems.length == 1) &&
                    (selectedItems[0].getData() instanceof UsabilitySmell))
                {
                    UsabilitySmell smell = (UsabilitySmell) selectedItems[0].getData();
                    if (falsePositiveRadioButton.getSelection()) {
                        smell.setManualLabel(ManualLabel.FALSE_POSITIVE);
                    }
                }
            }
        });
        
        final Button truePositiveRadioButton = new Button(assessmentRadioButtons, SWT.RADIO);
        truePositiveRadioButton.setText("true positive");
        truePositiveRadioButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                TreeItem[] selectedItems = smellList.getSelection();
                if ((selectedItems.length == 1) &&
                    (selectedItems[0].getData() instanceof UsabilitySmell))
                {
                    UsabilitySmell smell = (UsabilitySmell) selectedItems[0].getData();
                    if (truePositiveRadioButton.getSelection()) {
                        smell.setManualLabel(ManualLabel.TRUE_POSITIVE);
                    }
                }
            }
        });
        
        new Label(detailsAssessmentComposite, SWT.NONE).setText("Tags:");

        tagCheckBoxParent = new Composite(detailsAssessmentComposite, SWT.NONE);
        tagCheckBoxParent.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        tagCheckBoxParent.setLayout(new RowLayout());
        
        recreateTagsSashForm();
        
        final Text addTagField = new Text(detailsAssessmentComposite, SWT.BORDER);
        addTagField.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
        addTagField.setSize(addTagField.computeSize(100, 50));
        
        final Button addTagButton = new Button(detailsAssessmentComposite, SWT.NONE);
        addTagButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
        addTagButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                String tagName = addTagField.getText();
                if ((tagName != null) && (!"".equals(tagName)) && (!existingTags.contains(tagName))) {
                    existingTags.add(tagName);
                    recreateTagsSashForm();
                }
            }
        });
        addTagButton.setText("add tag");
        
        detailsTreesSashForm.setWeights(new int[] { 1, 1 });
        detailsSashForm.setWeights(new int[] { 1, 3 });
        mainSashForm.setWeights(new int[] { 1, 3 });

        //indexColumn.pack();
        //severityColumn.pack();
        //descriptionColumn.pack();
        //smellList.pack();
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     */
    private void recreateTagsSashForm() {
        for (Control child : tagCheckBoxParent.getChildren()) {
            child.dispose();
        }
        
        if (existingTags.size() > 0) {
            TreeItem[] selectedItems = smellList.getSelection();
            boolean enabled = (selectedItems.length == 1) &&
                    (selectedItems[0].getData() instanceof UsabilitySmell);

            for (String existingTag : existingTags) {
                Button checkBox = new Button(tagCheckBoxParent, SWT.CHECK);
                checkBox.setText(existingTag);
                checkBox.addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(SelectionEvent event) {
                        Button btn = (Button) event.getSource();
                        
                        TreeItem[] selectedItems = smellList.getSelection();
                        if ((selectedItems.length == 1) &&
                            (selectedItems[0].getData() instanceof UsabilitySmell))
                        {
                            UsabilitySmell smell = (UsabilitySmell) selectedItems[0].getData();
                            
                            if (btn.getSelection()) {
                                smell.addTag(btn.getText());
                            }
                            else {
                                smell.removeTag(btn.getText());
                            }
                        }
                    }
                });
                
                checkBox.setEnabled(enabled);
                if (enabled) {
                    checkBox.setSelection
                        (((UsabilitySmell) selectedItems[0].getData()).getTags().contains(existingTag));
                }
            }
        }
        else {
            new Label(tagCheckBoxParent, SWT.NONE).setText
                ("no tags added yet --> add using the text field and the button on the right");
        }
        
        tagCheckBoxParent.layout();
        tagCheckBoxParent.redraw();
    }

    /**
     * convenience method for creating the display of the instances
     */
    private void buildSmellTree() {
        int groupCount = 5;
        int groupSize = 30;
        
        List<UsabilitySmell> smells = usabilityEvalResult.getAllSmells();

        final Map<String, List<UsabilitySmell>> sortedSmells = new HashMap<>();

        for (UsabilitySmell smell : smells) {
            List<UsabilitySmell> smellList = sortedSmells.get(smell.getBriefDescription());
                    
            if (smellList == null) {
                smellList = new ArrayList<UsabilitySmell>();
                sortedSmells.put(smell.getBriefDescription(), smellList);
            }
            
            ListIterator<UsabilitySmell> it = smellList.listIterator();
            boolean added = false;
            
            while (it.hasNext()) {
                if (smell.getIntensity().getEventCoverage() >
                    it.next().getIntensity().getEventCoverage())
                {
                    it.previous();
                    it.add(smell);
                    added = true;
                    break;
                }
            }
            
            if (!added) {
                smellList.add(smell);
            }
            
            // extract the intial list of tags of the smells
            for (String tag : smell.getTags()) {
                if (!existingTags.contains(tag)) {
                    existingTags.add(tag);
                }
            }
        }

        final Map<Integer, Map<String, List<UsabilitySmell>>> allSortedSmells =
            new HashMap<Integer, Map<String, List<UsabilitySmell>>>();
        
        for (Map.Entry<String, List<UsabilitySmell>> entry : sortedSmells.entrySet()) {
            // we create groupCount groups of size groupSize
            int overallIndex = 0;
            
            for (int i = 0; i < groupCount; i++) {
                List<UsabilitySmell> smellList = new LinkedList<>();
                for (int j = 0; overallIndex < entry.getValue().size() &&
                                ((j < groupSize) || (i == (groupCount - 1))); j++)
                {
                    UsabilitySmell smell = entry.getValue().get(overallIndex++);
                    
                    int ratio = smell.getIntensity().getRatio();
                    int eventCoverage = smell.getIntensity().getEventCoverage();
                    int index = 0;
                    for (index = 0; index < smellList.size(); index++) {
                        UsabilitySmellIntensity candidate = smellList.get(index).getIntensity();
                        if ((ratio == candidate.getRatio()) &&
                            (eventCoverage > candidate.getEventCoverage()))
                        {
                            break;
                        }
                        else if (ratio > candidate.getRatio()) {
                            break;
                        }
                    }
                    
                    smellList.add(index, smell);
                }
                
                if (smellList.size() > 0) {
                    Map<String, List<UsabilitySmell>> smellGroups = allSortedSmells.get(i);
                    
                    if (smellGroups == null) {
                        smellGroups = new HashMap<>();
                        allSortedSmells.put(i, smellGroups);
                    }
                    
                    smellGroups.put(entry.getKey(), smellList);
                }
            }
        }
        
        if (smellList.getListeners(SWT.Expand).length == 0) {
            smellList.addListener(SWT.Expand, new Listener() {
               public void handleEvent(final Event event) {
                   ensureChildren((TreeItem) event.item);
                   ((TreeItem) event.item).setExpanded(true);
                   VisualizationUtils.updateColumnWidths(smellList);
               }
           });
        }

        for (int i = 0; i < allSortedSmells.size(); i++) {
            createRootItem("smells group " + (i + 1), allSortedSmells.get(i));
        }
        
    }

    /**
     * convenience method for creating the display of the instances
     */
    /*private void buildSmellTree() {
        List<UsabilitySmell> smells = usabilityEvalResult.getAllSmells();
        
        int[] eventCoverageQuantileGroups = { 990, 975, 950, 0, -1 };
        int[] minEventCoverages = new int[eventCoverageQuantileGroups.length];
        int[] maxEventCoverages = new int[eventCoverageQuantileGroups.length];

        final List<Map<String, List<UsabilitySmell>>> sortedSmells =
            new LinkedList<Map<String, List<UsabilitySmell>>>();
        
        for (int i = 0; i < eventCoverageQuantileGroups.length; i++) {
            sortedSmells.add(new HashMap<String, List<UsabilitySmell>>());
        }
        
        for (UsabilitySmell smell : smells) {
            int eventCoverageQuantile = smell.getIntensity().getEventCoverageQuantile();
            
            for (int i = 0; i < eventCoverageQuantileGroups.length; i++) {
                if (eventCoverageQuantile >= eventCoverageQuantileGroups[i]) {
                    Map<String, List<UsabilitySmell>> smellMap = sortedSmells.get(i);
                    
                    List<UsabilitySmell> smellList = smellMap.get(smell.getBriefDescription());
                    
                    if (smellList == null) {
                        smellList = new ArrayList<UsabilitySmell>();
                        smellMap.put(smell.getBriefDescription(), smellList);
                    }
                    
                    int ratio = smell.getIntensity().getRatio();
                    int eventCoverage = smell.getIntensity().getEventCoverage();
                    int index = 0;
                    for (index = 0; index < smellList.size(); index++) {
                        UsabilitySmellIntensity candidate = smellList.get(index).getIntensity();
                        if ((ratio == candidate.getRatio()) &&
                            (eventCoverage > candidate.getEventCoverage()))
                        {
                            break;
                        }
                        else if (ratio > candidate.getRatio()) {
                            break;
                        }
                    }
                    
                    smellList.add(index, smell);
                    
                    if (minEventCoverages[i] == 0) {
                        minEventCoverages[i] = smell.getIntensity().getEventCoverage();
                        maxEventCoverages[i] = smell.getIntensity().getEventCoverage();
                    }
                    else {
                        minEventCoverages[i] = Math.min
                            (minEventCoverages[i], smell.getIntensity().getEventCoverage());
                        maxEventCoverages[i] = Math.max
                            (maxEventCoverages[i], smell.getIntensity().getEventCoverage());
                    }
                    
                    break;
                }
            }
        }
        
        if (smellList.getListeners(SWT.Expand).length == 0) {
            smellList.addListener(SWT.Expand, new Listener() {
               public void handleEvent(final Event event) {
                   ensureChildren((TreeItem) event.item);
                   ((TreeItem) event.item).setExpanded(true);
               }
           });
        }

        double taskPercentages = 0;
        double taskPercentagesCoveredByPreceedingGroups = 0;
        
        for (int i = 0; i < eventCoverageQuantileGroups.length; i++) {
            taskPercentages = ((1000 - eventCoverageQuantileGroups[i]) / 10.0) -
                taskPercentagesCoveredByPreceedingGroups;
            
            if (eventCoverageQuantileGroups[i] > -1) {
                createRootItem("smells for " + taskPercentages + "% of tasks covering " +
                               minEventCoverages[i] + " to " + maxEventCoverages[i] +
                               " recorded events", sortedSmells.get(i));
            }
            else {
                createRootItem("other smells not related to specific tasks", sortedSmells.get(i));

            }
            
            taskPercentagesCoveredByPreceedingGroups += taskPercentages;
        }
        
    }

    /**
     * 
     */
    private void createRootItem(String name, Map<String, List<UsabilitySmell>> smells) {
        TreeItem smellItem = new TreeItem(smellList, SWT.NULL);
        
        int count = 0;
        for (Map.Entry<String, List<UsabilitySmell>> entry : smells.entrySet()) {
            count += entry.getValue().size();
        }

        smellItem.setText(name + " (" + count + " smells)");
        smellItem.setData(smells);
        
        if (count > 0) {
            // simulate a child
            new TreeItem(smellItem, SWT.NULL);
        }
    }

    /**
     *
     */
    private void clearSmellDetails() {
        description.setText("");
        involvedTargetsTree.removeAll();
        involvedTasks.removeAll();
        
        for (Control child : assessmentRadioButtons.getChildren()) {
            if (child instanceof Button) {
                child.setEnabled(false);
                ((Button) child).setSelection(false);
            }
        }
        
        for (Control child : tagCheckBoxParent.getChildren()) {
            if (child instanceof Button) {
                child.setEnabled(false);
                ((Button) child).setSelection(false);
            }
        }
    }

    /**
     *
     */
    private void displaySmellDetails(UsabilitySmell smell) {
        clearSmellDetails();
        
        FontData data = description.getFont().getFontData()[0];
        int height = (int) (data.getHeight() * 1.5);
        Font defaultFont = new Font
            (description.getDisplay(), data.getName(), height, data.getStyle());
        
        Font boldFont = new Font
            (description.getDisplay(), data.getName(), height, data.getStyle() | SWT.BOLD);
        
        for (Object fragment : smell.getDescriptionFragments()) {
            int color;
            Font font;
            
            if (fragment instanceof String) {
                color = SWT.COLOR_BLACK;
                font = defaultFont;
            }
            else {
                color = SWT.COLOR_DARK_GREEN;
                font = boldFont;
            }
            
            int initialLength = description.getText().length();
            
            if (fragment instanceof Collection<?>) {
                int counter = 1;
                for (Object elem : ((Collection<?>) fragment)) {
                    description.append("\n");
                    description.append(Integer.toString(counter++));
                    description.append(".: ");
                    description.append(elem.toString());
                }
            }
            else {
                description.append(fragment.toString());
            }
            
            StyleRange styleRange = new StyleRange
                (initialLength, description.getText().length() - initialLength,
                 description.getDisplay().getSystemColor(color), null);

            styleRange.font = font;
                
            description.setStyleRange(styleRange);
            description.append(" ");
        }
        description.setLeftMargin(50);
        description.setRightMargin(50);
        description.setTopMargin(50);
        description.setBottomMargin(50);

        
        StyleRange styleRange = new StyleRange();
        styleRange.font = new Font(description.getDisplay(), "Courier", 12, SWT.NORMAL);
        description.setStyleRange(styleRange);
        
        assessmentRadioButtons.getChildren()[0].setEnabled(true);
        ((Button) assessmentRadioButtons.getChildren()[0]).setSelection
            (smell.getManualLabel() == ManualLabel.UNCHECKED);
        
        assessmentRadioButtons.getChildren()[1].setEnabled(true);
        ((Button) assessmentRadioButtons.getChildren()[1]).setSelection
            (smell.getManualLabel() == ManualLabel.FALSE_POSITIVE);
        
        assessmentRadioButtons.getChildren()[2].setEnabled(true);
        ((Button) assessmentRadioButtons.getChildren()[2]).setSelection
            (smell.getManualLabel() == ManualLabel.TRUE_POSITIVE);
        
        for (Control child : tagCheckBoxParent.getChildren()) {
            if (child instanceof Button) {
                child.setEnabled(true);
                ((Button) child).setSelection(smell.getTags().contains(((Button) child).getText()));
            }
        }
        
        List<ITask> involvedTaskList = getInvolvedTasks(smell);
        for (ITask involvedTask : involvedTaskList) {
            VisualizationUtils.createTreeItemFor
                (involvedTask, involvedTasks, usabilityEvalResult.getTaskModel(), true);
        }
        
        List<IEventTarget> involvedTargets = getInvolvedTargets(smell);
        if (involvedTargets.size() <= 0) {
            for (ITask involvedTask : involvedTaskList) {
                VisualizationUtils.getInvolvedTargets(involvedTask, involvedTargets);
            }
        }
        
        VisualizationUtils.addInvolvedTargets(involvedTargetsTree, involvedTargets);
        
        int weightLeft = involvedTaskList.size() == 0 ? 1 :
            Math.min(4, Math.max(3, involvedTaskList.size()));
        int weightRight = involvedTargets.size() == 0 ? 1 :
            Math.min(3, Math.max(1, involvedTargets.size()));
        ((SashForm) involvedTasks.getParent()).setWeights(new int[] { weightLeft, weightRight });
        
        VisualizationUtils.expandAll(involvedTasks, true);
        VisualizationUtils.updateColumnWidths(involvedTasks);
    }
    
    /**
     *
     */
    private void ensureChildren(TreeItem parent) {
        if ((parent.getItemCount() == 0) || (parent.getItems()[0].getData() != null)) {
            return;
        }
        
        for (int i = 0; i < parent.getItems().length; i++) {
            parent.getItems()[i].dispose();
        }
        
        if (parent.getData() instanceof Map<?, ?>) {
            @SuppressWarnings("unchecked")
            Map<String, List<UsabilitySmell>> map =
                (Map<String, List<UsabilitySmell>>) parent.getData();
            
            for (Map.Entry<String, List<UsabilitySmell>> entry : map.entrySet()) {
                TreeItem child = new TreeItem(parent, SWT.NULL);        
                child.setText(entry.getKey() + " (" + entry.getValue().size() + " smells)");
                child.setData(entry);
                
                if (entry.getValue().size() > 0) {
                    // simulate child
                    new TreeItem(child, SWT.NULL);
                }
            }
        }
        else if (parent.getData() instanceof Map.Entry<?, ?>) {
            @SuppressWarnings("unchecked")
            Map.Entry<String, List<UsabilitySmell>> entry =
                (Map.Entry<String, List<UsabilitySmell>>) parent.getData();
            
            int count = 0;
            for (UsabilitySmell smell : entry.getValue()) {
                TreeItem child = new TreeItem(parent, SWT.NULL);        
                child.setData(smell);
                child.setText(++count + ": ratio = " + smell.getIntensity().getRatio() +
                              ", covered events = " + smell.getIntensity().getEventCoverage());
            }
        }
        else if (parent.getData() instanceof ITask) {
            ITask task = (ITask) parent.getData();

            if (task instanceof IStructuringTemporalRelationship) {
                for (ITask subTask : ((IStructuringTemporalRelationship) task).getChildren()) {
                    VisualizationUtils.createTreeItemFor
                        (subTask, parent, usabilityEvalResult.getTaskModel(), true);
                }
            }
            else if (task instanceof IMarkingTemporalRelationship) {
                VisualizationUtils.createTreeItemFor
                    (((IMarkingTemporalRelationship) task).getMarkedTask(), parent,
                     usabilityEvalResult.getTaskModel(), true);
            }
        }
    }
    
    /**
     *
     */
    private List<ITask> getInvolvedTasks(UsabilitySmell smell) {
        List<Object> fragments = smell.getDescriptionFragments();
        List<ITask> involvedTasks = new ArrayList<ITask>();
        
        for (Object fragment : fragments) {
            if (fragment instanceof ITask) {
                involvedTasks.add((ITask) fragment);
            }
        }
        
        return involvedTasks;
    }

    /**
     *
     */
    private List<IEventTarget> getInvolvedTargets(UsabilitySmell smell) {
        List<Object> fragments = smell.getDescriptionFragments();
        List<IEventTarget> involvedTargets = new ArrayList<IEventTarget>();
        
        for (Object fragment : fragments) {
            if (fragment instanceof IEventTarget) {
                involvedTargets.add((IEventTarget) fragment);
            }
            else if (fragment instanceof Collection<?>) {
                for (Object elem : (Collection<?>) fragment) {
                    if (elem instanceof IEventTarget) {
                        involvedTargets.add((IEventTarget) elem);
                    }
                }
            }
        }
        
        return involvedTargets;
    }
}
