//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Data/MaskItems.cpp
//! @brief     Implements MaskItems classes
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/Model/Data/MaskItems.h"
#include "Device/Mask/Ellipse.h"
#include "Device/Mask/InfinitePlane.h"
#include "Device/Mask/Line.h"
#include "Device/Mask/Polygon.h"
#include "Device/Mask/Rectangle.h"
#include "GUI/Support/XML/Backup.h"
#include <QRegularExpression>

namespace {
namespace Tag {

const QString Name("Name");
const QString MaskValue("MaskValue");
const QString IsVisible("IsVisible");
const QString BaseData("BaseData");
const QString XLow("XLow");
const QString YLow("YLow");
const QString XUp("XUp");
const QString YUp("YUp");
const QString IsClosed("IsClosed");
const QString PolygonPoint("PolygonPoint");
const QString XPosition("XPosition");
const QString YPosition("YPosition");
const QString XCenter("XCenter");
const QString YCenter("YCenter");
const QString XRadius("XRadius");
const QString YRadius("YRadius");
const QString Angle("Angle");
const QString Mask("Mask");

} // namespace Tag
} // namespace

MaskContainerItem::MaskContainerItem()
    : MaskItemObject()
    , m_model(new MaskContainerModel(this))
    , m_selectionModel(new QItemSelectionModel(m_model.get()))
{
}

QVector<MaskItem*> MaskContainerItem::maskItems() const
{
    return m_maskItems.toQVector();
}

void MaskContainerItem::insertMask(int row, MaskItem* maskItem)
{
    // takes owning of maskItem!
    m_maskItems.insert_at(row, maskItem);
}

void MaskContainerItem::addMask(MaskItem* maskItem)
{
    // takes owning of maskItem!
    m_maskItems.push_back(maskItem);
}

void MaskContainerItem::moveMask(int from_row, int to_row)
{
    m_maskItems.move(from_row, to_row);
}

void MaskContainerItem::removeMaskAt(int row)
{
    m_maskItems.delete_at(row);
}

void MaskContainerItem::removeMask(MaskItem* maskItem)
{
    m_maskItems.delete_element(maskItem);
}

RegionOfInterestItem* MaskContainerItem::regionOfInterestItem() const
{
    for (const auto& maskSel : m_maskItems)
        if (auto* roi = dynamic_cast<RegionOfInterestItem*>(maskSel.currentItem()))
            return roi;

    return nullptr;
}

void MaskContainerItem::clear()
{
    m_maskItems.clear();
}

bool MaskContainerItem::isEmpty() const
{
    return m_maskItems.empty();
}

int MaskContainerItem::size() const
{
    return m_maskItems.size();
}

MaskItem* MaskContainerItem::at(const int idx)
{
    return m_maskItems.at(idx).currentItem();
}

int MaskContainerItem::indexOfItem(MaskItem* maskItem) const
{
    return m_maskItems.index_of(maskItem);
}

void MaskContainerItem::copyFrom(const MaskContainerItem* maskContainer)
{
    ASSERT(maskContainer);
    GUI::Util::copyContents(maskContainer, this);
}

MaskContainerModel* MaskContainerItem::model()
{
    return m_model.get();
}

QItemSelectionModel* MaskContainerItem::selectionModel()
{
    return m_selectionModel.get();
}

void MaskContainerItem::updateMaskNames()
{
    const auto reg = QRegularExpression("[0-9]");

    QMap<QString, int> numMasksByType;
    for (size_t i = 0; i < m_maskItems.size(); i++) {

        QString name = m_maskItems[i].currentItem()->maskName();
        name.remove(reg);

        int numMasks = 1;
        if (numMasksByType.contains(name)) {
            numMasks = numMasksByType.value(name) + 1;
            numMasksByType.remove(name);
        }
        numMasksByType.insert(name, numMasks);
        name += QString::number(numMasks);

        m_maskItems[i].currentItem()->setMaskName(name);
    }
}

void MaskContainerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    for (const auto& sel : m_maskItems) {
        w->writeStartElement(Tag::Mask);
        sel.writeTo(w);
        w->writeEndElement();
    }
}

void MaskContainerItem::readFrom(QXmlStreamReader* r, MessageService*)
{
    clear();

    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        if (tag == Tag::Mask) {
            addMask(nullptr);
            m_maskItems.back().readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

/* ------------------------------------------------------------------------- */

QString MaskItem::maskName() const
{
    return m_maskName;
}

void MaskItem::setMaskName(const QString& name)
{
    m_maskName = name;
}

/* ------------------------------------------------------------------------- */

bool MaskItem::maskValue() const
{
    return m_maskValue;
}

void MaskItem::setMaskValue(bool mask_value)
{
    m_maskValue = mask_value;
    emit maskVisibilityChanged();
}

bool MaskItem::isVisible() const
{
    return m_isVisible;
}

void MaskItem::setIsVisible(bool visible)
{
    m_isVisible = visible;
    emit maskVisibilityChanged();
}

void MaskItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // name
    w->writeStartElement(Tag::Name);
    XML::writeAttribute(w, XML::Attrib::value, m_maskName);
    w->writeEndElement();

    // mask value
    w->writeStartElement(Tag::MaskValue);
    XML::writeAttribute(w, XML::Attrib::value, m_maskValue);
    w->writeEndElement();

    // is visible?
    w->writeStartElement(Tag::IsVisible);
    XML::writeAttribute(w, XML::Attrib::value, m_isVisible);
    w->writeEndElement();
}

void MaskItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // name
        if (tag == Tag::Name) {
            XML::readAttribute(r, XML::Attrib::value, &m_maskName);
            XML::gotoEndElementOfTag(r, tag);

            // mask value
        } else if (tag == Tag::MaskValue) {
            XML::readAttribute(r, XML::Attrib::value, &m_maskValue);
            XML::gotoEndElementOfTag(r, tag);

            // is visible?
        } else if (tag == Tag::IsVisible) {
            XML::readAttribute(r, XML::Attrib::value, &m_isVisible);
            m_wasVisible = m_isVisible;
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

MaskItem::MaskItem()
    : MaskItemObject()
{
}

/* ------------------------------------------------------------------------- */

RectangleItem::RectangleItem()
    : MaskItem()
{
    setMaskName("RectangleMask");
}

std::unique_ptr<IShape2D> RectangleItem::createShape(double scale) const
{
    double xlow = scale * xLow();
    double ylow = scale * yLow();
    double xup = scale * xUp();
    double yup = scale * yUp();
    return std::make_unique<Rectangle>(xlow, ylow, xup, yup);
}

double RectangleItem::xLow() const
{
    return m_xLow;
}

void RectangleItem::setXLow(double val)
{
    m_xLow = val;
    emit maskGeometryChanged();
}

double RectangleItem::yLow() const
{
    return m_yLow;
}

void RectangleItem::setYLow(double val)
{
    m_yLow = val;
    emit maskGeometryChanged();
}

double RectangleItem::xUp() const
{
    return m_xUp;
}

void RectangleItem::setXUp(double val)
{
    m_xUp = val;
    emit maskGeometryChanged();
}

double RectangleItem::yUp() const
{
    return m_yUp;
}

void RectangleItem::setYUp(double val)
{
    m_yUp = val;
    emit maskGeometryChanged();
}

void RectangleItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    MaskItem::writeTo(w);
    w->writeEndElement();

    // x low
    w->writeStartElement(Tag::XLow);
    XML::writeAttribute(w, XML::Attrib::value, m_xLow);
    w->writeEndElement();

    // y low
    w->writeStartElement(Tag::YLow);
    XML::writeAttribute(w, XML::Attrib::value, m_yLow);
    w->writeEndElement();

    // x up
    w->writeStartElement(Tag::XUp);
    XML::writeAttribute(w, XML::Attrib::value, m_xUp);
    w->writeEndElement();

    // y up
    w->writeStartElement(Tag::YUp);
    XML::writeAttribute(w, XML::Attrib::value, m_yUp);
    w->writeEndElement();
}

void RectangleItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            MaskItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // x low
        } else if (tag == Tag::XLow) {
            XML::readAttribute(r, XML::Attrib::value, &m_xLow);
            XML::gotoEndElementOfTag(r, tag);

            // y low
        } else if (tag == Tag::YLow) {
            XML::readAttribute(r, XML::Attrib::value, &m_yLow);
            XML::gotoEndElementOfTag(r, tag);

            // x up
        } else if (tag == Tag::XUp) {
            XML::readAttribute(r, XML::Attrib::value, &m_xUp);
            XML::gotoEndElementOfTag(r, tag);

            // y up
        } else if (tag == Tag::YUp) {
            XML::readAttribute(r, XML::Attrib::value, &m_yUp);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

/* ------------------------------------------------------------------------- */

RegionOfInterestItem::RegionOfInterestItem()
    : RectangleItem()
{
    setMaskName("RegionOfInterest");
    setMaskValue(false);
}

std::unique_ptr<IShape2D> RegionOfInterestItem::createShape(double scale) const
{
    auto shape = RectangleItem::createShape(scale);
    dynamic_cast<Rectangle*>(shape.get())->setInverted(true);
    return shape;
}

/* ------------------------------------------------------------------------- */

PolygonPointItem::PolygonPointItem()
    : MaskItemObject()
{
}

double PolygonPointItem::posX() const
{
    return m_posX;
}

void PolygonPointItem::setPosX(double val)
{
    m_posX = val;
    emit maskGeometryChanged();
}

double PolygonPointItem::posY() const
{
    return m_posY;
}

void PolygonPointItem::setPosY(double val)
{
    m_posY = val;
    emit maskGeometryChanged();
}

void PolygonPointItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::xPos, posX());
    XML::writeAttribute(w, XML::Attrib::yPos, posY());
    XML::writeAttribute(w, XML::Attrib::version, uint(1));
}

void PolygonPointItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    double xPos = 0;
    XML::readAttribute(r, XML::Attrib::xPos, &xPos);
    setPosX(xPos);

    double yPos = 0;
    XML::readAttribute(r, XML::Attrib::yPos, &yPos);
    setPosY(yPos);
}

/* ------------------------------------------------------------------------- */

PolygonItem::PolygonItem()
    : MaskItem()
{
    setMaskName("PolygonMask");
}

std::unique_ptr<IShape2D> PolygonItem::createShape(double scale) const
{
    std::vector<double> x, y;
    for (PolygonPointItem* item : m_points) {
        x.push_back(scale * item->posX());
        y.push_back(scale * item->posY());
    }
    return std::make_unique<Polygon>(x, y);
}

bool PolygonItem::isClosed() const
{
    return m_isClosed;
}

void PolygonItem::setIsClosed(bool closed)
{
    m_isClosed = closed;
}

QVector<PolygonPointItem*> PolygonItem::points() const
{
    return QVector<PolygonPointItem*>(m_points.begin(), m_points.end());
}

void PolygonItem::addPoint(double x, double y)
{
    auto* pointItem = new PolygonPointItem;
    pointItem->setPosX(x);
    pointItem->setPosY(y);
    m_points.emplace_back(pointItem); // TODO: check whether the order of points matter
}

void PolygonItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    MaskItem::writeTo(w);
    w->writeEndElement();

    // is closed?
    w->writeStartElement(Tag::IsClosed);
    XML::writeAttribute(w, XML::Attrib::value, m_isClosed); // TODO do we need to save 'isClosed'?
    w->writeEndElement();

    // polygon points
    for (const auto* p : m_points) {
        w->writeStartElement(Tag::PolygonPoint);
        p->writeTo(w);
        w->writeEndElement();
    }
}

void PolygonItem::readFrom(QXmlStreamReader* r)
{
    m_points.clear();

    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            MaskItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // is closed?
        } else if (tag == Tag::IsClosed) {
            XML::readAttribute(r, XML::Attrib::value,
                               &m_isClosed); // TODO do we need to read 'isClosed'?
            XML::gotoEndElementOfTag(r, tag);

            // polygon point
        } else if (tag == Tag::PolygonPoint) {
            PolygonPointItem point;
            point.readFrom(r);
            addPoint(point.posX(), point.posY());
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

/* ------------------------------------------------------------------------- */

VerticalLineItem::VerticalLineItem()
    : MaskItem()
{
    setMaskName("VerticalLineMask");
}

std::unique_ptr<IShape2D> VerticalLineItem::createShape(double scale) const
{
    return std::make_unique<VerticalLine>(scale * posX());
}

double VerticalLineItem::posX() const
{
    return m_posX;
}

void VerticalLineItem::setPosX(double val)
{
    m_posX = val;
    emit maskGeometryChanged(this);
}

void VerticalLineItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    MaskItem::writeTo(w);
    w->writeEndElement();

    // x position
    w->writeStartElement(Tag::XPosition);
    XML::writeAttribute(w, XML::Attrib::value, m_posX);
    w->writeEndElement();
}

void VerticalLineItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            MaskItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // x position
        } else if (tag == Tag::XPosition) {
            XML::readAttribute(r, XML::Attrib::value, &m_posX);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

/* ------------------------------------------------------------------------- */

HorizontalLineItem::HorizontalLineItem()
    : MaskItem()
{
    setMaskName("HorizontalLineMask");
}

std::unique_ptr<IShape2D> HorizontalLineItem::createShape(double scale) const
{
    return std::make_unique<HorizontalLine>(scale * posY());
}

double HorizontalLineItem::posY() const
{
    return m_posY;
}

void HorizontalLineItem::setPosY(double pos_y)
{
    m_posY = pos_y;
    emit maskGeometryChanged(this);
}

void HorizontalLineItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    MaskItem::writeTo(w);
    w->writeEndElement();

    // y position
    w->writeStartElement(Tag::YPosition);
    XML::writeAttribute(w, XML::Attrib::value, m_posY);
    w->writeEndElement();
}

void HorizontalLineItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            MaskItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // y position
        } else if (tag == Tag::YPosition) {
            XML::readAttribute(r, XML::Attrib::value, &m_posY);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

/* ------------------------------------------------------------------------- */

EllipseItem::EllipseItem()
    : MaskItem()
{
    setMaskName("EllipseMask");
}

std::unique_ptr<IShape2D> EllipseItem::createShape(double scale) const
{
    double xcenter = scale * xCenter();
    double ycenter = scale * yCenter();
    double xradius = scale * xRadius();
    double yradius = scale * yRadius();
    double _angle = scale * angle();

    return std::make_unique<Ellipse>(xcenter, ycenter, xradius, yradius, _angle);
}

double EllipseItem::xCenter() const
{
    return m_xCenter;
}

void EllipseItem::setXCenter(double val)
{
    m_xCenter = val;
    emit maskGeometryChanged();
}

double EllipseItem::yCenter() const
{
    return m_yCenter;
}

void EllipseItem::setYCenter(double val)
{
    m_yCenter = val;
    emit maskGeometryChanged();
}

double EllipseItem::xRadius() const
{
    return m_xRadius;
}

void EllipseItem::setXRadius(double val)
{
    m_xRadius = val;
    emit maskGeometryChanged();
}

double EllipseItem::yRadius() const
{
    return m_yRadius;
}

void EllipseItem::setYRadius(const double val)
{
    m_yRadius = val;
    emit maskGeometryChanged();
}

double EllipseItem::angle() const
{
    return m_angle;
}

void EllipseItem::setAngle(const double val)
{
    m_angle = val;
    emit maskGeometryChanged();
}

void EllipseItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    MaskItem::writeTo(w);
    w->writeEndElement();

    // x center
    w->writeStartElement(Tag::XCenter);
    XML::writeAttribute(w, XML::Attrib::value, m_xCenter);
    w->writeEndElement();

    // y center
    w->writeStartElement(Tag::YCenter);
    XML::writeAttribute(w, XML::Attrib::value, m_yCenter);
    w->writeEndElement();

    // x radius
    w->writeStartElement(Tag::XRadius);
    XML::writeAttribute(w, XML::Attrib::value, m_xRadius);
    w->writeEndElement();

    // y radius
    w->writeStartElement(Tag::YRadius);
    XML::writeAttribute(w, XML::Attrib::value, m_yRadius);
    w->writeEndElement();

    // rotation angle
    w->writeStartElement(Tag::Angle);
    XML::writeAttribute(w, XML::Attrib::value, m_angle);
    w->writeEndElement();
}

void EllipseItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            MaskItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // x center
        } else if (tag == Tag::XCenter) {
            XML::readAttribute(r, XML::Attrib::value, &m_xCenter);
            XML::gotoEndElementOfTag(r, tag);

            // y center
        } else if (tag == Tag::YCenter) {
            XML::readAttribute(r, XML::Attrib::value, &m_yCenter);
            XML::gotoEndElementOfTag(r, tag);

            // x radius
        } else if (tag == Tag::XRadius) {
            XML::readAttribute(r, XML::Attrib::value, &m_xRadius);
            XML::gotoEndElementOfTag(r, tag);

            // y radius
        } else if (tag == Tag::YRadius) {
            XML::readAttribute(r, XML::Attrib::value, &m_yRadius);
            XML::gotoEndElementOfTag(r, tag);

            // rotation angle
        } else if (tag == Tag::Angle) {
            XML::readAttribute(r, XML::Attrib::value, &m_angle);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

/* ------------------------------------------------------------------------- */

MaskAllItem::MaskAllItem()
    : MaskItem()
{
    setMaskName("MaskAllMask");
}

std::unique_ptr<IShape2D> MaskAllItem::createShape(double) const
{
    return std::make_unique<InfinitePlane>();
}

/* ------------------------------------------------------------------------- */

MaskItemObject::MaskItemObject() = default;

MaskItemObject::~MaskItemObject()
{
    emit maskToBeDestroyed(this);
}
/* ------------------------------------------------------------------------- */

// Implementation of MaskContainerModel is based on the Qt source code for QStringListModel

MaskContainerModel::MaskContainerModel(MaskContainerItem* container)
    : maskContainer(container)
{
}

MaskContainerModel::~MaskContainerModel() = default;

// Begin overridden methods from QAbstractListModel

int MaskContainerModel::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid())
        return 0;

    return maskContainer->size();
}

QVariant MaskContainerModel::data(const QModelIndex& index, int role) const
{
    const qsizetype row = index.row();

    if (row < 0 || row >= maskContainer->size())
        return {};

    if (role == Qt::DisplayRole || role == Qt::EditRole)
        return {maskContainer->at(row)->maskName()};

    return {};
}

// End overridden methods from QAbstractListModel

void MaskContainerModel::insertMask(int row, MaskItem* maskItem)
{
    QAbstractListModel::beginInsertRows(maskContainer->rootIndex, row, row);
    maskContainer->insertMask(row, maskItem);
    QAbstractListModel::endInsertRows();
}

void MaskContainerModel::addMask(MaskItem* maskItem)
{
    qsizetype row = maskContainer->size() - 1;
    QAbstractListModel::beginInsertRows(maskContainer->rootIndex, row, row);
    maskContainer->addMask(maskItem);
    QAbstractListModel::endInsertRows();
}


//! Move mask to a given row
void MaskContainerModel::moveMask(int from_row, int to_row)
{
    emit QAbstractListModel::beginMoveRows(maskContainer->rootIndex, from_row, from_row,
                                           maskContainer->rootIndex, to_row);
    maskContainer->moveMask(from_row, to_row);
    emit QAbstractListModel::endMoveRows();
}

void MaskContainerModel::removeMaskAt(int row)
{
    QAbstractListModel::beginRemoveRows(maskContainer->rootIndex, row, row);
    maskContainer->removeMaskAt(row);
    QAbstractListModel::endRemoveRows();
}

void MaskContainerModel::removeMask(MaskItem* maskItem)
{
    const int row = maskContainer->indexOfItem(maskItem);
    removeMaskAt(row);
}

RegionOfInterestItem* MaskContainerModel::regionOfInterestItem() const
{
    for (MaskItem* maskItem : maskContainer->maskItems())
        if (auto* reg = dynamic_cast<RegionOfInterestItem*>(maskItem))
            return reg;

    return nullptr;
}

QModelIndex MaskContainerModel::indexOfItem(MaskItemObject* item) const
{
    const int row = maskContainer->indexOfItem(dynamic_cast<MaskItem*>(item));
    return QAbstractListModel::index(row, 0, {});
}

MaskItem* MaskContainerModel::itemForIndex(const QModelIndex& index) const
{
    if (index.isValid())
        return maskContainer->at(index.row());

    return nullptr;
}

void MaskContainerModel::clear()
{
    QAbstractListModel::beginResetModel();
    maskContainer->clear();
    QAbstractListModel::endResetModel();
}

void MaskContainerModel::copy(const MaskContainerModel* src)
{
    clear();
    if (src)
        for (auto mask : src->maskContainer->maskItems())
            addMask(mask);
}
