#include "layer.h"
#include "./feature.h"
#include "./symbol.h"
#include "./job.h"
#include "./util.h"
#include <QDebug>
#include <QHash>


class PcbCamLayerPrivate
{
    Q_DECLARE_PUBLIC(PcbCamLayer)
public:
    PcbCamLayerPrivate(PcbCamLayer *qptr) : q_ptr(qptr) { }
    ~PcbCamLayerPrivate() {
        destoryFeatures();
    }

    void destoryFeatures() {
        qDeleteAll(features);
        features.clear();
    }

    PcbCamJob *job {nullptr};

    //Layer名称
    QString layerName;

    //Step名称
    QString stepName;

    //Layer属性
    QVariantMap layerAttrMap;

    //是否影响层
    bool isAffectedLayer {false};

    //是否工作层
    bool isWorkLayer {false};

    //是否已经加载
    bool loaded {false};

    int maxIndex {0};

    QString displayColor;

    QList<PcbCamFeature*> features;

    mutable QRectF boundingRect;

protected:
    PcbCamLayer * const q_ptr;
};

PcbCamLayer::PcbCamLayer(PcbCamJob *iJob)
    : d_ptr(new PcbCamLayerPrivate(this))
{
    Q_D(PcbCamLayer);
    d->job = iJob;
}

PcbCamLayer::~PcbCamLayer()
{

}

PcbCamJob *PcbCamLayer::job() const
{
    Q_D(const PcbCamLayer);
    return d->job;
}

QString PcbCamLayer::name() const
{
    Q_D(const PcbCamLayer);
    return d->layerName;
}

void PcbCamLayer::setName(const QString &iName)
{
    Q_D(PcbCamLayer);
    d->layerName = iName.toLower();
}

QString PcbCamLayer::step() const
{
    Q_D(const PcbCamLayer);
    return d->stepName;
}

void PcbCamLayer::setStep(const QString &iStep)
{
    Q_D(PcbCamLayer);
    d->stepName = iStep.toLower();
}

QVariant PcbCamLayer::attr(const QString &iName) const
{
    Q_D(const PcbCamLayer);
    return d->layerAttrMap.value(iName.toLower());
}

bool PcbCamLayer::hasAttr(const QString &iName) const
{
    Q_D(const PcbCamLayer);
    return d->layerAttrMap.contains(iName.toLower());
}

void PcbCamLayer::initAttr(const QVariantMap &iAttrMap)
{
    Q_D(PcbCamLayer);
    d->layerAttrMap = iAttrMap;
}

void PcbCamLayer::setAttr(const QString &iName, const QVariant &iValue)
{
    Q_D(PcbCamLayer);
    d->layerAttrMap.insert(iName.toLower(), iValue);
}

void PcbCamLayer::setAffectedLayer(bool iAffected)
{
    Q_D(PcbCamLayer);
    d->isAffectedLayer = iAffected;
}

bool PcbCamLayer::isAffectedLayer() const
{
    Q_D(const PcbCamLayer);
    return d->isAffectedLayer;
}

void PcbCamLayer::setWorkLayer(bool iBol)
{
    Q_D(PcbCamLayer);
    d->isWorkLayer = iBol;
}

bool PcbCamLayer::isWorkLayer() const
{
    Q_D(const PcbCamLayer);
    return d->isWorkLayer;
}

void PcbCamLayer::setDisplayColor(const QString &iColor)
{
    Q_D(PcbCamLayer);
    d->displayColor = iColor;
}

QString PcbCamLayer::displayColor() const
{
    Q_D(const PcbCamLayer);
    return d->displayColor;
}


QList<PcbCamFeature *> PcbCamLayer::features() const
{
    Q_D(const PcbCamLayer);
    return d->features;
}

void PcbCamLayer::clearFeatures()
{
    Q_D(PcbCamLayer);
    d->destoryFeatures();
    d->maxIndex = 0;
}

void PcbCamLayer::addFeature(PcbCamFeature *iFeature)
{
    Q_D(PcbCamLayer);
    iFeature->setIndex(++d->maxIndex);
    d->features.append(iFeature);
}

void PcbCamLayer::addFeatures(const QList<PcbCamFeature *> &iFeatures)
{
    for (PcbCamFeature *feat : iFeatures) {
        addFeature(feat);
    }
}

PcbCamFeature *PcbCamLayer::addLine(const QLineF &iLine, const QString &iSymbol, PcbCam::Polarity iPolarity, const QVariantMap &iAttr)
{
    Q_D(PcbCamLayer);
    if (d->job == nullptr) return nullptr;
    if (!d->job->isSymbolExists(iSymbol)) {
        d->job->loadSymbol(iSymbol);
    }
    auto sym = d->job->symbol(iSymbol);

    auto feat = new PcbCamFeatureLine();
    feat->setLine(iLine);
    feat->setPolarity(iPolarity);
    feat->setSymbol(sym);
    feat->setAttr(iAttr);
    addFeature(feat);
    return feat;
}

PcbCamFeature *PcbCamLayer::addPad(const QPointF &iPos, const QString &iSymbol, PcbCam::Orient iOrient, PcbCam::Polarity iPolarity, const QVariantMap &iAttr)
{
    Q_D(PcbCamLayer);
    if (d->job == nullptr) return nullptr;
    if (!d->job->isSymbolExists(iSymbol)) {
        d->job->loadSymbol(iSymbol);
    }
    auto sym = d->job->symbol(iSymbol);

    auto feat = new PcbCamFeaturePad();
    feat->setPos(iPos);
    feat->setOrient(iOrient);
    feat->setPolarity(iPolarity);
    feat->setSymbol(sym);
    feat->setAttr(iAttr);
    addFeature(feat);
    return feat;
}

PcbCamFeature *PcbCamLayer::addArc(const QPointF &iPs, const QPointF &iPe, const QPointF &iPc, bool iCw, const QString &iSymbol, PcbCam::Polarity iPolarity, const QVariantMap &iAttr)
{
    Q_D(PcbCamLayer);
    if (d->job == nullptr) return nullptr;
    if (!d->job->isSymbolExists(iSymbol)) {
        d->job->loadSymbol(iSymbol);
    }
    auto sym = d->job->symbol(iSymbol);

    auto feat = new PcbCamFeatureArc();
    feat->setData(iPs, iPe, iPc, iCw);
    feat->setPolarity(iPolarity);
    feat->setSymbol(sym);
    feat->setAttr(iAttr);
    addFeature(feat);
    return feat;
}

PcbCamFeature *PcbCamLayer::addText(const QPointF &iPos, const QString &iText, const QSizeF &iSize, PcbCam::Orient iOrient, PcbCam::Polarity iPolarity, const QVariantMap &iAttr, qreal iWidthFactor, const QString &iFont)
{
    Q_D(PcbCamLayer);
    if (d->job == nullptr) return nullptr;
    if (!d->job->isSymbolExists(iFont)) {
        d->job->loadFont(iFont);
    }
    auto font = d->job->font(iFont);

    auto feat = new PcbCamFeatureText();
    feat->setData(iPos, font, iOrient, iSize, iWidthFactor, iText);
    feat->setPolarity(iPolarity);
    feat->setAttr(iAttr);
    addFeature(feat);
    return feat;
}

PcbCamFeature *PcbCamLayer::addSurface(const QPainterPath &iPath, PcbCam::Polarity iPolarity, const QVariantMap &iAttr)
{
    Q_D(PcbCamLayer);
    if (d->job == nullptr) return nullptr;
    auto feat = new PcbCamFeatureSurface();
    feat->setPath(iPath);
    feat->setPolarity(iPolarity);
    feat->setAttr(iAttr);
    addFeature(feat);
    return feat;
}

PcbCamFeature *PcbCamLayer::addPath(const QPainterPath &iPath, int iWidth, PcbCam::Polarity iPolarity, const QVariantMap &iAttr)
{
    Q_D(PcbCamLayer);
    if (d->job == nullptr) return nullptr;
    auto feat = new PcbCamFeaturePath();
    feat->setPath(iPath);
    feat->setWidth(iWidth);
    feat->setPolarity(iPolarity);
    feat->setAttr(iAttr);
    addFeature(feat);
    return feat;
}



void PcbCamLayer::deleteFeature(int iIndex)
{
    Q_D(PcbCamLayer);
    for (int i = 0; i < d->features.count(); ++i) {
        PcbCamFeature *feat = d->features[i];
        if (feat->index() == iIndex) {
            d->features.removeAt(i);
            delete feat;
            return;
        }
    }
}

QList<PcbCamFeature *> PcbCamLayer::selectFeature(const QPainterPath &iRegionPath,
                                                  const PcbCam::FeatureSelectionFilter &iFilter,
                                                  PcbCam::FeatureSelectionOperation iSelOp,
                                                  PcbCam::FeatureSelectionMode iSelMode)
{
    Q_D(PcbCamLayer);
    QList<PcbCamFeature *> result;
    auto stepProfile = d->job->step(d->stepName)->profile();

    bool isFilterRange = !iRegionPath.isEmpty();
    bool isFilterProfile = (iFilter.profile != PcbCam::AllProfile && !stepProfile.isEmpty());
    bool isFilterInclueSym = !iFilter.includeSymbols.isEmpty();
    bool isFilterExcludeSym = !iFilter.excludeSymbols.isEmpty();
    bool isFilterIncludeAttr = !iFilter.includeAttributes.isEmpty();
    bool isFilterExcludeAttr = !iFilter.excludeAttributes.isEmpty();


    QSet<PcbCamSymbol*> includeSymbols;
    for (auto symname : iFilter.includeSymbols) {
        auto symbol = d->job->symbol(symname);
        if (symbol != nullptr) {
            includeSymbols.insert(symbol);
        }
    }

    QSet<PcbCamSymbol*> excludeSymbols;
    for (auto symname : iFilter.excludeSymbols) {
        auto symbol = d->job->symbol(symname);
        if (symbol != nullptr) {
            excludeSymbols.insert(symbol);
        }
    }

    QList<PcbCam::AttrCompareInfo> includeAttrs;
    for (auto attr : iFilter.includeAttributes) {
        includeAttrs.append(PcbCam::AttrCompareInfo(attr));
    }

    QList<PcbCam::AttrCompareInfo> excludeAttrs;
    for (auto attr : iFilter.excludeAttributes) {
        excludeAttrs.append(PcbCam::AttrCompareInfo(attr));
    }

    if (iSelOp == PcbCam::ReplaceFeatureSelection) {
        this->clearSelection();
    }

    for (auto feat : d->features) {
        //是否满足过滤条件
        bool feat_match = PcbCamUtil::isFeatMatchFilter(feat,
                                                        iFilter.type,
                                                        iFilter.polarity,
                                                        isFilterProfile,
                                                        iFilter.profile,
                                                        stepProfile,
                                                        isFilterInclueSym,
                                                        includeSymbols,
                                                        isFilterExcludeSym,
                                                        excludeSymbols,
                                                        isFilterIncludeAttr,
                                                        includeAttrs,
                                                        iFilter.includeAttributesLogic,
                                                        isFilterExcludeAttr,
                                                        excludeAttrs,
                                                        iFilter.excludeAttributesLogic);
        if (!feat_match) continue;

        if (isFilterRange) {
            bool range_match = false;
            if (iSelMode == PcbCam::ContainsFeatureShape) {
                if (iRegionPath.contains(feat->boundingRect())) range_match = true;
            }
            else if (iSelMode == PcbCam::IntersectsFeatureShape) {
                if (iRegionPath.intersects(feat->shape())) range_match = true;
            }
            if (!range_match) continue;
        }

        result.append(feat);
        if (iSelOp == PcbCam::ReplaceFeatureSelection || iSelOp == PcbCam::AppendFeatureSelection) {
            feat->setSelected(true);
        }
        else if (iSelOp == PcbCam::RemoveFeatureSelection) {
            feat->setSelected(false);
        }
    }
    return result;
}

QList<PcbCamFeature *> PcbCamLayer::selectedFeatures() const
{
    Q_D(const PcbCamLayer);
    QList<PcbCamFeature*> result;
    for ( PcbCamFeature* feat : d->features) {
        if (feat->isSelected()) result.append(feat);
    }
    return result;
}

void PcbCamLayer::clearSelection()
{
    Q_D(PcbCamLayer);
    for (PcbCamFeature* feat : d->features) {
        if (feat->isSelected()) feat->setSelected(false);
    }
}

bool PcbCamLayer::hasFeatureSelected() const
{
    Q_D(const PcbCamLayer);
    for ( PcbCamFeature* feat : d->features) {
        if (feat->isSelected()) return true;
    }
    return false;
}

QRectF PcbCamLayer::boundingRect() const
{
    Q_D(const PcbCamLayer);
    if (d->boundingRect.isEmpty()) {
        for (const PcbCamFeature* feat : d->features) {
            d->boundingRect = d->boundingRect.united(feat->boundingRect());
        }
    }
    return d->boundingRect;
}

