/**
 * UGENE - Integrated Bioinformatics Tools.
 * Copyright (C) 2008-2024 UniPro <ugene@unipro.ru>
 * http://ugene.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include "BreakpointManagerView.h"

#include <QAction>
#include <QCheckBox>
#include <QGraphicsScene>
#include <QGroupBox>
#include <QMenu>
#include <QToolBar>
#include <QTreeWidget>
#include <QVBoxLayout>

#include <U2Core/Counter.h>
#include <U2Core/QObjectScopedPointer.h>
#include <U2Core/U2SafePoints.h>

#include <U2Designer/BreakpointHitCountDialog.h>
#include <U2Designer/DelegateEditors.h>
#include <U2Designer/EditBreakpointLabelsDialog.h>
#include <U2Designer/NewBreakpointDialog.h>

#include <U2Lang/WorkflowDebugStatus.h>
#include <U2Lang/WorkflowSettings.h>

#include "WorkflowViewItems.h"

const char* VIEW_ELEMENT_NAME_COLUMN_TITLE = "Element Name";
const char* VIEW_LABELS_COLUMN_TITLE = "Labels";
const char* VIEW_BREAKPOINT_STATE_COLUMN_NAME = "State";
const char* VIEW_BREAKPOINT_CONDITION_COLUMN_NAME = "Condition";
const char* VIEW_BREAKPOINT_HIT_COUNT_COLUMN_NAME = "Hit Count";

const bool DEFAULT_BREAKPOINT_STATE = true;
const int BREAKPOINT_STATE_COLUMN_NUMBER = 0;
const int BREAKPOINT_LABELS_COLUMN_NUMBER = 2;
const int BREAKPOINT_CONDITION_COLUMN_NUMBER = 3;
const int BREAKPOINT_HIT_COUNT_COLUMN_NUMBER = 4;
const char* DEFAULT_BREAKPOINT_LABEL = "";
const char* DEFAULT_BREAKPOINT_CONDITION = "(no condition)";
const char* CURRENT_HIT_COUNT_BEGINNING = " (currently ";
const char* CURRENT_HIT_COUNT_ENDING = ")";

const QString BREAKPOINT_LABELS_SEPARATOR = ";";

const char* CONDITION_LABEL_BEGIN = "when '";
const char* CONDITION_LABEL_END_IS_TRUE = "' is true";
const char* CONDITION_LABEL_END_HAS_CHANGED = "' has changed";

const QColor BREAKPOINT_HIGHLIGHTING_COLOR = Qt::yellow;
const QColor BREAKPOINT_DEFAULT_COLOR = Qt::white;

namespace U2 {

QMap<BreakpointConditionParameter, HitCondition>
    BreakpointManagerView::conditionParametertranslations = QMap<BreakpointConditionParameter, HitCondition>();

BreakpointManagerView::BreakpointManagerView(WorkflowDebugStatus* initDebugInfo,
                                             const QSharedPointer<Schema>& initScheme,
                                             QGraphicsScene* scene,
                                             QWidget* parent)
    : QWidget(parent),
      debugInfo(initDebugInfo),
      scene(scene),
      scheme(initScheme),
      breakpointsList(nullptr),
      actorConnections(),
      breakpointStateControls(),
      allExistingLabels(),
      lastReachedBreakpoint(nullptr),
      newBreakpointAction(nullptr),
      deleteSelectedBreakpointAction(nullptr),
      deleteAllBreakpointsAction(nullptr),
      disableAllBreakpointsAction(nullptr),
      highlightItemWithBreakpoint(nullptr),
      hitCountAction(nullptr),
      editLabelsAction(nullptr),
      setConditionAction(nullptr) {
    Q_ASSERT(debugInfo != nullptr && scheme != nullptr);

    createActions();
    Q_ASSERT(newBreakpointAction != nullptr && deleteAllBreakpointsAction != nullptr && deleteSelectedBreakpointAction != nullptr && disableAllBreakpointsAction != nullptr && hitCountAction != nullptr && editLabelsAction != nullptr && setConditionAction != nullptr);

    initBreakpointsList();
    Q_ASSERT(breakpointsList != nullptr);

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    QVBoxLayout* contentLayout = new QVBoxLayout(this);
    contentLayout->setSpacing(0);
    contentLayout->setMargin(0);
    contentLayout->setContentsMargins(0, 0, 0, 0);
    contentLayout->addWidget(initToolBar());
    contentLayout->addWidget(breakpointsList);

    connect(debugInfo, SIGNAL(si_pauseStateChanged(bool)), SLOT(sl_pauseStateChanged(bool)));
    connect(debugInfo, SIGNAL(si_breakpointAdded(const ActorId&)), SLOT(sl_breakpointAdded(const ActorId&)));
    connect(debugInfo, SIGNAL(si_breakpointEnabled(const ActorId&)), SLOT(sl_breakpointEnabled(const ActorId&)));
    connect(debugInfo, SIGNAL(si_breakpointRemoved(const ActorId&)), SLOT(sl_breakpointRemoved(const ActorId&)));
    connect(debugInfo, SIGNAL(si_breakpointDisabled(const ActorId&)), SLOT(sl_breakpointDisabled(const ActorId&)));

    connect(breakpointsList, SIGNAL(itemSelectionChanged()), SLOT(sl_breakpointsSelectionChanged()));
    connect(breakpointsList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(sl_breakpointDoubleClicked(QTreeWidgetItem*, int)));

    if (conditionParametertranslations.isEmpty()) {
        conditionParametertranslations[IS_TRUE] = CONDITION_IS_TRUE;
        conditionParametertranslations[HAS_CHANGED] = CONDITION_HAS_CHANGED;
    }

    hide();

    installEventFilter(this);
}

void BreakpointManagerView::createActions() {
    newBreakpointAction = new QAction(tr("&Break at element..."), this);
    newBreakpointAction->setIcon(QIcon(":workflow_designer/images/breakpoint.png"));
    newBreakpointAction->setShortcut(QKeySequence("Ctrl+B"));
    connect(newBreakpointAction, SIGNAL(triggered()), SLOT(sl_newBreakpoint()));
    connect(newBreakpointAction, SIGNAL(triggered()), scene, SLOT(update()));
    newBreakpointAction->setEnabled(true);

    deleteAllBreakpointsAction = new QAction(tr("Delete &all breakpoints"), this);
    deleteAllBreakpointsAction->setIcon(QIcon(":workflow_designer/images/delete_all_breakpoints.png"));
    deleteAllBreakpointsAction->setShortcut(QKeySequence("Shift+Del"));
    deleteAllBreakpointsAction->setShortcutContext(Qt::WidgetShortcut);
    connect(deleteAllBreakpointsAction, SIGNAL(triggered()), SLOT(sl_deleteAllBreakpoints()));
    deleteAllBreakpointsAction->setEnabled(false);

    deleteSelectedBreakpointAction = new QAction(tr("&Delete"), this);
    deleteSelectedBreakpointAction->setIcon(QIcon(":workflow_designer/images/delete_selected_breakpoints.png"));
    deleteSelectedBreakpointAction->setShortcut(QKeySequence("Del"));
    deleteSelectedBreakpointAction->setShortcutContext(Qt::WidgetShortcut);

    connect(deleteSelectedBreakpointAction, SIGNAL(triggered()), SLOT(sl_deleteSelectedBreakpoint()));
    deleteSelectedBreakpointAction->setEnabled(false);
    deleteSelectedBreakpointAction->setToolTip(tr("Delete the selected breakpoints"));

    disableAllBreakpointsAction = new QAction(tr("&Enable or disable all breakpoints"), this);
    disableAllBreakpointsAction->setIcon(QIcon(":workflow_designer/images/disable_all_breakpoints.png"));
    disableAllBreakpointsAction->setShortcut(QKeySequence("Ctrl+D"));
    connect(disableAllBreakpointsAction, SIGNAL(triggered()), SLOT(sl_disableAllBreakpoints()));
    disableAllBreakpointsAction->setEnabled(false);

    highlightItemWithBreakpoint = new QAction(tr("H&ighlight selected item"), this);
    highlightItemWithBreakpoint->setIcon(QIcon(":workflow_designer/images/highlight_item.png"));
    highlightItemWithBreakpoint->setShortcut(QKeySequence("Ctrl+H"));
    connect(highlightItemWithBreakpoint, SIGNAL(triggered()), SLOT(sl_highlightItem()));
    highlightItemWithBreakpoint->setEnabled(false);

    hitCountAction = new QAction(tr("&Hit Count..."), this);
    connect(hitCountAction, SIGNAL(triggered()), SLOT(sl_hitCount()));

    editLabelsAction = new QAction(tr("Edit &labels..."), this);
    connect(editLabelsAction, SIGNAL(triggered()), SLOT(sl_editLabels()));

    setConditionAction = new QAction(tr("&Condition..."), this);
    connect(setConditionAction, SIGNAL(triggered()), SLOT(sl_setCondition()));
}

QToolBar* BreakpointManagerView::initToolBar() {
    QToolBar* mainToolBar = new QToolBar(this);

    mainToolBar->addAction(newBreakpointAction);
    mainToolBar->addAction(deleteSelectedBreakpointAction);
    mainToolBar->addAction(deleteAllBreakpointsAction);
    mainToolBar->addAction(disableAllBreakpointsAction);
    mainToolBar->addAction(highlightItemWithBreakpoint);

    mainToolBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

    return mainToolBar;
}

void BreakpointManagerView::initBreakpointsList() {
    Q_ASSERT(breakpointsList == nullptr);

    breakpointsList = new QTreeWidget(this);
    breakpointsList->setObjectName("breakpoints list");
    QStringList headerLabels = QStringList() << tr(VIEW_BREAKPOINT_STATE_COLUMN_NAME)
                                             << tr(VIEW_ELEMENT_NAME_COLUMN_TITLE) << tr(VIEW_LABELS_COLUMN_TITLE)
                                             << tr(VIEW_BREAKPOINT_CONDITION_COLUMN_NAME) << tr(VIEW_BREAKPOINT_HIT_COUNT_COLUMN_NAME);
    breakpointsList->setColumnCount(headerLabels.size());
    breakpointsList->setHeaderLabels(headerLabels);
    breakpointsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    breakpointsList->setSortingEnabled(true);
    breakpointsList->resizeColumnToContents(BREAKPOINT_STATE_COLUMN_NUMBER);
    breakpointsList->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(breakpointsList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(sl_contextMenuForBreakpointListRequested(const QPoint&)));
}

void BreakpointManagerView::sl_breakpointAdded(const ActorId& actorId) {
    if (!actorConnections.values().contains(actorId)) {
        QCheckBox* stateCheckBox = new QCheckBox();
        stateCheckBox->setChecked(DEFAULT_BREAKPOINT_STATE);
        stateCheckBox->setAutoFillBackground(true);

        Actor* actor = scheme->actorById(actorId);
        QTreeWidgetItem* item = new QTreeWidgetItem(breakpointsList, QStringList() << QString() << actor->getLabel() << tr(DEFAULT_BREAKPOINT_LABEL) << tr(DEFAULT_BREAKPOINT_CONDITION) << getNamesOfHitCounters()[ALWAYS]);

        actorConnections[item] = actorId;
        breakpointsList->setItemWidget(item, BREAKPOINT_STATE_COLUMN_NUMBER, stateCheckBox);
        breakpointStateControls[stateCheckBox] = item;
        connect(stateCheckBox, SIGNAL(stateChanged(int)), SLOT(sl_breakpointStateChanged(int)));

        if (!deleteAllBreakpointsAction->isEnabled()) {
            deleteAllBreakpointsAction->setEnabled(true);
        }
        if (!disableAllBreakpointsAction->isEnabled()) {
            disableAllBreakpointsAction->setEnabled(true);
        }
    } else {
        auto stateController = dynamic_cast<QCheckBox*>(
            breakpointsList->itemWidget(actorConnections.key(actorId),
                                        BREAKPOINT_STATE_COLUMN_NUMBER));
        if (!stateController->isChecked()) {
            stateController->setChecked(true);
        }
    }
}

void BreakpointManagerView::clear() {
    foreach (const ActorId& id, actorConnections.values()) {
        sl_breakpointRemoved(id);
    }
}

void BreakpointManagerView::sl_breakpointRemoved(const ActorId& actorId) {
    removeBreakpointFromList(actorConnections.key(actorId));
}

void BreakpointManagerView::sl_newBreakpoint() {
    // if there is any selected items - add breakpoints automatically
    if (!scene->selectedItems().empty()) {
        foreach (QGraphicsItem* item, scene->selectedItems()) {
            if (item->type() == WorkflowProcessItemType) {
                auto processItem = qgraphicsitem_cast<WorkflowProcessItem*>(item);
                SAFE_POINT(processItem != nullptr, "WorkflowProcessItem is NULL!", );
                if (processItem->isBreakpointInserted() && !processItem->isBreakpointEnabled()) {
                    processItem->toggleBreakpointState();
                    debugInfo->setBreakpointEnabled(processItem->getProcess()->getId(), true);
                } else {
                    processItem->toggleBreakpoint();
                }
                if (processItem->isBreakpointInserted()) {
                    GCOUNTER(cvar, "Script. Breakpoint has been inserted");
                    debugInfo->addBreakpointToActor(processItem->getProcess()->getId());
                } else {
                    GCOUNTER(cvar, "Script. Breakpoint has been removed");
                    debugInfo->removeBreakpointFromActor(processItem->getProcess()->getId());
                }
            }
        }
    } else {
        // no items selected - launch new breakpoint dialog
        QStringList actorsLabels;
        foreach (Actor* actor, scheme->getProcesses()) {
            actorsLabels << actor->getLabel();
        }

        QObjectScopedPointer<NewBreakpointDialog> dialog = new NewBreakpointDialog(actorsLabels, this);

        connect(dialog.data(), SIGNAL(si_newBreakpointCreated(const QString&)), SLOT(sl_addBreakpoint(const QString&)));
        dialog->exec();
        CHECK(!dialog.isNull(), );
    }
}

void BreakpointManagerView::sl_addBreakpoint(const QString& elementName) {
    foreach (Actor* actor, scheme->getProcesses()) {
        if (elementName == actor->getLabel()) {
            debugInfo->addBreakpointToActor(actor->getId());
            return;
        }
    }
    Q_ASSERT(false);
}

void BreakpointManagerView::sl_deleteSelectedBreakpoint() {
    removeBreakpointsFromList(breakpointsList->selectedItems());
}

void BreakpointManagerView::sl_deleteAllBreakpoints() {
    removeBreakpointsFromList(breakpointsList->findItems("", Qt::MatchContains));
}

void BreakpointManagerView::sl_disableAllBreakpoints() {
    auto firstController = qobject_cast<QCheckBox*>(breakpointStateControls.begin().key());
    Q_ASSERT(firstController != nullptr);
    const bool currentState = (Qt::Checked == firstController->checkState());
    foreach (QWidget* breakpointStateController, breakpointStateControls.keys()) {
        auto checkBox = qobject_cast<QCheckBox*>(breakpointStateController);
        Q_ASSERT(checkBox != nullptr);
        checkBox->setChecked(!currentState);
    }
}

void BreakpointManagerView::removeBreakpointFromList(QTreeWidgetItem* item) {
    const int removedControlsCount = breakpointStateControls.remove(breakpointStateControls.key(item));
    const int removedActorsCount = actorConnections.remove(item);
    Q_ASSERT(1 == removedActorsCount || 0 == removedActorsCount || removedControlsCount != removedActorsCount);
    Q_UNUSED(removedControlsCount);

    if (1 == removedActorsCount) {
        if (lastReachedBreakpoint == item) {
            lastReachedBreakpoint = nullptr;
        }
        delete item;
    }
    disableGenericActionsIfNoItemsExist();
}

void BreakpointManagerView::removeBreakpointsFromList(QList<QTreeWidgetItem*> items) {
    foreach (QTreeWidgetItem* item, items) {
        debugInfo->removeBreakpointFromActor(actorConnections[item]);
    }
}

void BreakpointManagerView::sl_breakpointsSelectionChanged() {
    const int countOfSelectedBreakpoints = breakpointsList->selectedItems().size();
    deleteSelectedBreakpointAction->setEnabled(0 < countOfSelectedBreakpoints);
    highlightItemWithBreakpoint->setEnabled(1 == countOfSelectedBreakpoints);
}

void BreakpointManagerView::sl_breakpointStateChanged(int state) {
    QObject* activator = sender();
    auto stateAssigner = qobject_cast<QWidget*>(activator);
    Q_ASSERT(stateAssigner != nullptr);
    QTreeWidgetItem* breakpointItem = breakpointStateControls[stateAssigner];

    bool isBreakpointBeingEnabled = (Qt::Checked == state);
    debugInfo->setBreakpointEnabled(actorConnections[breakpointItem], isBreakpointBeingEnabled);
}

void BreakpointManagerView::disableGenericActionsIfNoItemsExist() {
    const int countOfItems = breakpointsList->findItems("", Qt::MatchContains).size();
    deleteAllBreakpointsAction->setEnabled(countOfItems > 0);
    disableAllBreakpointsAction->setEnabled(countOfItems > 0);
}

void BreakpointManagerView::sl_highlightItem() {
    const QList<QTreeWidgetItem*> selectedItems = breakpointsList->selectedItems();
    Q_ASSERT(1 == selectedItems.size());
    emit si_highlightingRequested(actorConnections[selectedItems.first()]);
}

void BreakpointManagerView::sl_breakpointDoubleClicked(QTreeWidgetItem* item, int column) {
    if (item != nullptr) {
        switch (column) {
            case BREAKPOINT_LABELS_COLUMN_NUMBER:
                sl_editLabels();
                break;
            case BREAKPOINT_CONDITION_COLUMN_NUMBER:
                sl_setCondition();
                break;
            case BREAKPOINT_HIT_COUNT_COLUMN_NUMBER:
                sl_hitCount();
                break;
        }
    }
}

void BreakpointManagerView::sl_labelsCreated(QStringList newLabels) {
    allExistingLabels += newLabels;
    debugInfo->addNewAvailableBreakpointLabels(newLabels);
}

void BreakpointManagerView::sl_labelAddedToCurrentBreakpoint(QStringList newLabels) {
    QTreeWidgetItem* currentItem = breakpointsList->currentItem();
    currentItem->setText(BREAKPOINT_LABELS_COLUMN_NUMBER,
                         newLabels.join(BREAKPOINT_LABELS_SEPARATOR));
    debugInfo->setBreakpointLabels(actorConnections[currentItem], newLabels);
}

void BreakpointManagerView::sl_breakpointEnabled(const ActorId& actor) {
    auto stateAssigner = dynamic_cast<QCheckBox*>(getBreakpointStateController(actor));
    Q_ASSERT(stateAssigner != nullptr);
    stateAssigner->setChecked(true);
}

void BreakpointManagerView::sl_breakpointDisabled(const ActorId& actor) {
    auto stateAssigner = dynamic_cast<QCheckBox*>(getBreakpointStateController(actor));
    Q_ASSERT(stateAssigner != nullptr);
    stateAssigner->setChecked(false);
}

QWidget* BreakpointManagerView::getBreakpointStateController(const ActorId& actor) {
    QTreeWidgetItem* actorViewItem = actorConnections.key(actor, nullptr);
    Q_ASSERT(actorViewItem != nullptr);
    auto stateController = dynamic_cast<QCheckBox*>(breakpointStateControls.key(actorViewItem,
                                                                                      nullptr));
    Q_ASSERT(stateController != nullptr);
    return stateController;
}

void BreakpointManagerView::sl_resetHitCount() {
    QTreeWidgetItem* currentItem = breakpointsList->currentItem();
    Q_ASSERT(currentItem != nullptr);
    debugInfo->resetHitCounterForActor(actorConnections[currentItem]);
    updateCurrentHitCountLabels(true);
}

void BreakpointManagerView::sl_hitCounterAssigned(const QString& hitCounterCondition, quint32 parameter) {
    QTreeWidgetItem* currentItem = breakpointsList->currentItem();
    Q_ASSERT(currentItem != nullptr);
    if (hitCounterCondition != currentItem->text(BREAKPOINT_HIT_COUNT_COLUMN_NUMBER)) {
        QString hitCounterLabel = hitCounterCondition;
        if (hitCounterCondition != getNamesOfHitCounters()[ALWAYS]) {
            hitCounterLabel.append(QString::number(parameter));
        }
        currentItem->setText(BREAKPOINT_HIT_COUNT_COLUMN_NUMBER, hitCounterLabel);
    }
    debugInfo->setHitCounterForActor(actorConnections[currentItem],
                                     getNamesOfHitCounters().key(hitCounterCondition),
                                     parameter);
}

void BreakpointManagerView::sl_contextMenuForBreakpointListRequested(const QPoint& pos) {
    if (breakpointsList->currentItem() != nullptr && breakpointsList->itemAt(pos) != nullptr) {
        QMenu contextMenu;
        contextMenu.addAction(deleteSelectedBreakpointAction);
        contextMenu.addSeparator();
        contextMenu.addAction(setConditionAction);
        contextMenu.addAction(hitCountAction);
        contextMenu.addAction(editLabelsAction);

        contextMenu.exec(breakpointsList->viewport()->mapToGlobal(pos));
    }
}

void BreakpointManagerView::sl_hitCount() {
    QTreeWidgetItem* item = breakpointsList->currentItem();
    const QMap<BreakpointHitCountCondition, QString>& hitCountersNames = getNamesOfHitCounters();
    const BreakpointHitCounterDump hitCounterInfo = debugInfo->getHitCounterDumpForActor(actorConnections[item]);
    QObjectScopedPointer<BreakpointHitCountDialog> hitCountDialog = new BreakpointHitCountDialog(QStringList(hitCountersNames.values()),
                                                                                                 hitCountersNames[hitCounterInfo.typeOfCondition],
                                                                                                 hitCounterInfo.hitCounterParameter,
                                                                                                 hitCounterInfo.hitCount,
                                                                                                 QStringList(hitCountersNames[ALWAYS]),
                                                                                                 this);

    connect(hitCountDialog.data(), SIGNAL(si_resetHitCount()), SLOT(sl_resetHitCount()));
    connect(hitCountDialog.data(), SIGNAL(si_hitCounterAssigned(const QString&, quint32)), SLOT(sl_hitCounterAssigned(const QString&, quint32)));

    hitCountDialog->exec();
}

void BreakpointManagerView::sl_editLabels() {
    QTreeWidgetItem* item = breakpointsList->currentItem();
    QObjectScopedPointer<EditBreakpointLabelsDialog> labelsDialog = new EditBreakpointLabelsDialog(debugInfo->getAvailableBreakpointLabels(),
                                                                                                   debugInfo->getBreakpointLabels(actorConnections[item]),
                                                                                                   this);

    connect(labelsDialog.data(), SIGNAL(si_labelsCreated(QStringList)), SLOT(sl_labelsCreated(QStringList)));
    connect(labelsDialog.data(), SIGNAL(si_labelAddedToCallingBreakpoint(QStringList)), SLOT(sl_labelAddedToCurrentBreakpoint(QStringList)));

    labelsDialog->exec();
}

void BreakpointManagerView::sl_setCondition() {
    QTreeWidgetItem* item = breakpointsList->currentItem();
    const ActorId actorId = actorConnections[item];
    BreakpointConditionDump conditionInfo = debugInfo->getConditionDumpForActor(actorId);
    HitCondition conditionParameter = conditionParametertranslations[conditionInfo.conditionParameter];

    QObjectScopedPointer<BreakpointConditionEditDialog> conditionDialog = new BreakpointConditionEditDialog(this,
                                                                                                            AttributeScriptDelegate::createScriptHeader(*(scheme->actorById(actorId)->getCondition())),
                                                                                                            (conditionInfo.isEnabled | conditionInfo.condition.isEmpty()),
                                                                                                            conditionInfo.condition,
                                                                                                            conditionParameter);

    connect(conditionDialog.data(), SIGNAL(si_conditionTextChanged(const QString&)), SLOT(sl_conditionTextChanged(const QString&)));
    connect(conditionDialog.data(), SIGNAL(si_conditionParameterChanged(HitCondition)), SLOT(sl_conditionParameterChanged(HitCondition)));
    connect(conditionDialog.data(), SIGNAL(si_conditionSwitched(bool)), SLOT(sl_conditionSwitched(bool)));

    conditionDialog->exec();
}

void BreakpointManagerView::sl_conditionTextChanged(const QString& text) {
    QTreeWidgetItem* item = breakpointsList->currentItem();
    const ActorId actorId = actorConnections[item];

    debugInfo->setConditionTextForActor(actorId, text);
    BreakpointConditionDump conditionInfo = debugInfo->getConditionDumpForActor(actorId);
    if (conditionInfo.isEnabled && !text.isEmpty()) {
        QString conditionLabel = tr(CONDITION_LABEL_BEGIN) + text;
        QString conditionLabelEnd;
        switch (conditionInfo.conditionParameter) {
            case IS_TRUE:
                conditionLabelEnd = tr(CONDITION_LABEL_END_IS_TRUE);
                break;
            case HAS_CHANGED:
                conditionLabelEnd = tr(CONDITION_LABEL_END_HAS_CHANGED);
                break;
            default:
                Q_ASSERT(false);
        }
        conditionLabel.append(conditionLabelEnd);
        item->setText(BREAKPOINT_CONDITION_COLUMN_NUMBER, conditionLabel);
    } else {
        item->setText(BREAKPOINT_CONDITION_COLUMN_NUMBER, tr(DEFAULT_BREAKPOINT_CONDITION));
    }
}

void BreakpointManagerView::sl_conditionSwitched(bool enabled) {
    QTreeWidgetItem* item = breakpointsList->currentItem();
    debugInfo->setConditionEnabledForActor(actorConnections[item], enabled);
}

void BreakpointManagerView::sl_conditionParameterChanged(HitCondition newParameter) {
    QTreeWidgetItem* item = breakpointsList->currentItem();
    BreakpointConditionParameter translatedParameter = conditionParametertranslations.key(
        newParameter);
    debugInfo->setConditionParameterForActor(actorConnections[item], translatedParameter);
}

void BreakpointManagerView::sl_pauseStateChanged(bool paused) {
    updateCurrentHitCountLabels(paused);
    if (!paused && lastReachedBreakpoint != nullptr) {
        setBreakpointBackgroundColor(lastReachedBreakpoint, BREAKPOINT_DEFAULT_COLOR);
        lastReachedBreakpoint = nullptr;
    }
}

void BreakpointManagerView::updateCurrentHitCountLabels(bool show) const {
    foreach (QTreeWidgetItem* item, actorConnections.keys()) {
        QString hitCountLabel = item->text(BREAKPOINT_HIT_COUNT_COLUMN_NUMBER);
        const int currentHitCountBegining = hitCountLabel.indexOf(tr(CURRENT_HIT_COUNT_BEGINNING));
        if (-1 != currentHitCountBegining) {
            hitCountLabel = hitCountLabel.left(currentHitCountBegining);
        }
        if (show) {
            hitCountLabel += tr(CURRENT_HIT_COUNT_BEGINNING) + QString::number(debugInfo->getHitCountForActor(actorConnections[item])) + tr(CURRENT_HIT_COUNT_ENDING);
        }
        item->setText(BREAKPOINT_HIT_COUNT_COLUMN_NUMBER, hitCountLabel);
    }
}

void BreakpointManagerView::onBreakpointReached(ActorId actor) {
    QTreeWidgetItem* item = actorConnections.key(actor, nullptr);
    Q_ASSERT(item != nullptr);
    if (lastReachedBreakpoint != nullptr) {
        setBreakpointBackgroundColor(lastReachedBreakpoint, BREAKPOINT_DEFAULT_COLOR);
    }
    setBreakpointBackgroundColor(item, BREAKPOINT_HIGHLIGHTING_COLOR);
    lastReachedBreakpoint = item;
}

bool BreakpointManagerView::eventFilter(QObject* /*object*/, QEvent* event) {
    CHECK(event != nullptr, false);
    if (event->type() == QEvent::KeyPress) {
        auto keyEvent = static_cast<QKeyEvent*>(event);
        CHECK(keyEvent != nullptr, false);

        bool shiftPressed = keyEvent->modifiers().testFlag(Qt::ShiftModifier);
        if (shiftPressed && keyEvent->key() == Qt::Key_Delete) {
            sl_deleteAllBreakpoints();
        }
        if (keyEvent->matches(QKeySequence::Delete)) {
            sl_deleteSelectedBreakpoint();
        }
    }
    return true;
}

void BreakpointManagerView::setBreakpointBackgroundColor(QTreeWidgetItem* breakpoint,
                                                         const QColor& newBackground) {
    for (int columnNumber = 0; columnNumber < breakpointsList->columnCount(); ++columnNumber) {
        breakpoint->setBackgroundColor(columnNumber, newBackground);
    }
}

void BreakpointManagerView::paintEvent(QPaintEvent* /*event*/) {
    static const QColor nonActiveColor = breakpointsList->palette().window().color();
    if (!this->isEnabled()) {
        foreach (QTreeWidgetItem* item, breakpointsList->findItems(QString(), Qt::MatchContains)) {
            if (BREAKPOINT_DEFAULT_COLOR == item->backgroundColor(BREAKPOINT_LABELS_COLUMN_NUMBER)) {
                setBreakpointBackgroundColor(item, nonActiveColor);
            }
        }
    } else {
        foreach (QTreeWidgetItem* item, breakpointsList->findItems(QString(), Qt::MatchContains)) {
            if (nonActiveColor == item->backgroundColor(BREAKPOINT_LABELS_COLUMN_NUMBER)) {
                setBreakpointBackgroundColor(item, BREAKPOINT_DEFAULT_COLOR);
            }
        }
    }
}

}  // namespace U2
