#include "util.h"
#include <QTransform>
#include <QPainterPath>
#include <QtMath>
#include <QPainter>
#include <QDebug>
#include <QElapsedTimer>
#include <QMap>
#include <QString>
#include <QRegularExpression>
#include "./common.h"
#include "./feature.h"


static QMap<int, QString> WEEK_DAY_STRING {
    {1, "MONDAY"},
    {2, "TUESDAY"},
    {3, "WEDNESDAY"},
    {4, "THURSDAY"},
    {5, "FRIDAY"},
    {6, "SATURDAY"},
    {7, "SUNDAY"}
};

QString PcbCamUtil::formatNumber(double iNumber, int iPercision)
{
    QString res = QString::number(iNumber, 'f', iPercision);
    if (res.contains(".")) {
        while (true) {
            if (res.at(res.length() - 1) == '0') {
                res.chop(1);
            }
            else {
                if (res.at(res.length() - 1) == '.') {
                    res.chop(1);
                }
                break;
            }
        }
    }
    return res;
}


QVariantMap PcbCamUtil::calcPathArc(const QPointF &iPs, const QPointF &iPe, const QPointF &iPc, bool iCw)
{
    qreal xs = iPs.x();
    qreal ys = iPs.y();
    qreal xe = iPe.x();
    qreal ye = iPe.y();
    qreal xc = iPc.x();
    qreal yc = iPc.y();

    qreal sax = xs - xc, say = ys - yc;
    qreal eax = xe - xc, eay = ye - yc;

    qreal r = qSqrt(sax * sax + say * say);

    qreal sa = qAtan2(-say, sax);
    qreal ea = qAtan2(-eay, eax);

    if (iCw) {
        if (ea <= sa) {
            ea += 2.0 * M_PI;
        }
    }
    else {
        if (sa <= ea) {
            sa += 2.0 * M_PI;
        }
    }

    QVariantMap retData;
    retData.insert("start_angle", sa * (180.0 / M_PI));
    retData.insert("sweep_length", (ea - sa) * (180.0 / M_PI));
    retData.insert("rect", QRectF(xc - r, yc - r, r * 2.0, r * 2.0));
    return retData;
}

QPainterPath PcbCamUtil::arc2path(const QPointF &iPs, const QPointF &iPe, const QPointF &iPc, bool iCw)
{
    QPainterPath path;
    QVariantMap calcData = calcPathArc(iPs, iPe, iPc, iCw);
    double startAngle = calcData.value("start_angle").toDouble();
    double sweepLength = calcData.value("sweep_length").toDouble();
    QRectF rect = calcData.value("rect").toRectF();
    path.arcMoveTo(rect, startAngle);
    path.arcTo(rect, startAngle, sweepLength);
    return path;
}

QPainterPath PcbCamUtil::line2path(const QLineF &iLine)
{
    QPainterPath path;
    path.moveTo(iLine.p1());
    path.lineTo(iLine.p2());
    return path;
}

QPen PcbCamUtil::sym2pen(PcbCamSymbol *iSymbol)
{
    QPen pen;
    if (iSymbol->type() == PcbCam::Sym_R) {
        const PcbCamSymbolR *sym = static_cast<const PcbCamSymbolR*>(iSymbol);
        pen.setCapStyle(Qt::RoundCap);
        pen.setWidthF(sym->size() > 0.0 ? sym->size() : 0.00001);
    }
    else if (iSymbol->type() == PcbCam::Sym_S) {
        const PcbCamSymbolR *sym = static_cast<const PcbCamSymbolR*>(iSymbol);
        pen.setCapStyle(Qt::SquareCap);
        pen.setWidthF(sym->size() > 0.0 ? sym->size() : 0.00001);
    }
    else if (iSymbol->type() == PcbCam::Sym_DonutR) {
        const PcbCamSymbolDonutR *sym = static_cast<const PcbCamSymbolDonutR*>(iSymbol);
        pen.setCapStyle(Qt::RoundCap);
        pen.setWidthF(sym->outerDiam() > 0.0 ? sym->outerDiam() : 0.00001);
    }
    else if (iSymbol->type() == PcbCam::Sym_DonutS) {
        const PcbCamSymbolDonutS *sym = static_cast<const PcbCamSymbolDonutS*>(iSymbol);
        pen.setCapStyle(Qt::SquareCap);
        pen.setWidthF(sym->outerDiam() > 0.0 ? sym->outerDiam() : 0.00001);
    }
    else if (iSymbol->type() == PcbCam::Sym_Rect) {
        pen.setCapStyle(Qt::SquareCap);
        pen.setWidthF(qMin(iSymbol->boundingRect().width(), iSymbol->boundingRect().height()));
    }
    else {
        pen.setCapStyle(Qt::RoundCap);
        pen.setWidthF(qMin(iSymbol->boundingRect().width(), iSymbol->boundingRect().height()));
    }
    return pen;
}

QTransform PcbCamUtil::matrix(const QPointF &iPos, PcbCam::Orient iOrient)
{
    if (iOrient == PcbCam::Orient_N_0) {
        return QTransform(1, 0, 0, 1, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_N_90) {
        return QTransform(0, -1, 1, 0, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_N_180) {
        return QTransform(-1, 0, 0, -1, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_N_270) {
        return QTransform(0, 1, -1, 0, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_Y_0) {
        return QTransform(-1, 0, 0, 1, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_Y_90) {
        return QTransform(0, -1, -1, 0, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_Y_180) {
        return QTransform(1, 0, 0, -1, iPos.x(), iPos.y());
    }
    else if (iOrient == PcbCam::Orient_Y_270) {
        return QTransform(0, 1, 1, 0, iPos.x(), iPos.y());
    }
    return QTransform(1, 0, 0, 1, iPos.x(), iPos.y());
}

QTransform PcbCamUtil::matrix(const QPointF &iPos, const QPointF &iDatum, qreal iAngle, bool iMirror)
{
    QTransform mat1, mat2, mat3, mat4;
    mat1.translate(-iDatum.x() , -iDatum.y());
    mat2.rotate((iAngle > 0) ? 360.0 - iAngle : 0);
    if (iMirror) mat3.scale(-1, 1);
    mat4.translate(iPos.x(), iPos.y());
    return  mat1 * mat2 * mat3* mat4 ;
}

void PcbCamUtil::drawFeatures(QPainter *painter,
                              const QList<PcbCamFeature *> &iFeatures,
                              PcbCam::Polarity iPolarity,
                              const QColor &iColor,
                              const QTransform &iMatrix,
                              const QRectF &iSceneRect,
                              PcbCam::DisplayOptions iDispOption,
                              PcbCamJob *iJob,
                              PcbCamStep *iStep,
                              PcbCamLayer *iLayer)
{
    painter->save();
    painter->setTransform(iMatrix);
    QSizeF minSize = iMatrix.inverted().mapRect(QRectF(0, 0, 2, 2)).size();
    QPainterPath clipPath;
    clipPath.addRect(iSceneRect);

    //边线Pen
    QPen outlinePen(iColor);
    outlinePen.setWidth(1);
    outlinePen.setCosmetic(true);
    outlinePen.setCapStyle(Qt::RoundCap);
    outlinePen.setJoinStyle(Qt::RoundJoin);

    QPen pointPen(iColor);
    pointPen.setWidth(1);
    pointPen.setCosmetic(true);
    pointPen.setCapStyle(Qt::SquareCap);

    //填充Brush
    QBrush fillBrush(iColor);

    //NegativeBrush
    QBrush negativeBrush(iColor);
    negativeBrush.setStyle(Qt::Dense4Pattern);
    negativeBrush.setTransform(iMatrix.inverted());

    QRectF featrect;
    for ( PcbCamFeature *feat : iFeatures) {
        featrect = feat->boundingRect();

        if (!iSceneRect.intersects(featrect)) continue;

        PcbCam::Polarity featPolarity = (iPolarity == PcbCam::Positive)
                ? feat->polarity()
                : (feat->polarity() == PcbCam::Positive)
                  ? PcbCam::Negative : PcbCam::Positive;


        if (featPolarity == PcbCam::Negative && iDispOption & PcbCam::DisplayFill &&  !(iDispOption & PcbCam::DisplayNegative)) {
            painter->setCompositionMode(QPainter::CompositionMode_Clear);
        }
        else {
            painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
        }


        if (featrect.width() < minSize.width() && featrect.height() < minSize.height()) {
            painter->setPen(pointPen);
            painter->drawPoint(featrect.center());
            continue;
        }

        if (iDispOption & PcbCam::DisplayFill) { //填充模式
            painter->setBrush(fillBrush);
            painter->setPen(Qt::NoPen);
        }
        else {
            painter->setPen(outlinePen);
            painter->setBrush(Qt::NoBrush);
        }

        if ((feat->type() == PcbCam::LineFeature || feat->type() == PcbCam::ArcFeature)
                && (feat->symbol()->boundingRect().width() < minSize.width() || feat->symbol()->boundingRect().height() < minSize.width()))
        {
            // 处理细线的情况
            painter->setPen(outlinePen);
            painter->setBrush(Qt::NoBrush);
            if (feat->type() == PcbCam::LineFeature) {
                const PcbCamFeatureLine *f = static_cast<const PcbCamFeatureLine*>(feat);
                painter->drawPath(PcbCamUtil::line2path(f->line()));
            }
            else if (feat->type() == PcbCam::ArcFeature) {
                const PcbCamFeatureArc *f = static_cast<const PcbCamFeatureArc*>(feat);
                painter->drawPath(PcbCamUtil::arc2path(f->ps(), f->pe(), f->pc(), f->cw()));
            }
            continue;
        }
        else if (featrect.width() < minSize.width() || featrect.height() < minSize.height()) {
            painter->drawRect(featrect);
            continue;
        }

        if (feat->type() == PcbCam::PadFeature && feat->symbol()->type() == PcbCam::Sym_User) {
            const PcbCamFeaturePad *f = static_cast<const PcbCamFeaturePad*>(feat);
            const PcbCamSymbolUser *s = static_cast<const PcbCamSymbolUser*>(f->symbol());
            QTransform mat = matrix(f->pos(), f->orient()) * iMatrix ;
            drawFeatures(painter, s->features(), featPolarity, iColor,
                         mat, mat.inverted().mapRect(iMatrix.mapRect(iSceneRect)),
                         iDispOption, iJob, iStep, iLayer);
            continue;
        }
        else if (feat->type() == PcbCam::PathFeature) {
            const PcbCamFeaturePath *f = static_cast<const PcbCamFeaturePath*>(feat);
            QPen pathpen(iColor);
            pathpen.setWidth(f->width());
            pathpen.setCosmetic(true);
            pathpen.setCapStyle(Qt::RoundCap);
            pathpen.setJoinStyle(Qt::RoundJoin);
            painter->setPen(pathpen);
            painter->setBrush(Qt::NoBrush);
            painter->drawPath(f->path());
            continue;
        }
        else if (feat->type() == PcbCam::ComponentFeature) {
            const PcbCamFeatureComponent *f = static_cast<const PcbCamFeatureComponent*>(feat);
            const PcbCamSymbolUser *s = static_cast<const PcbCamSymbolUser*>(f->symbol());
            QTransform mat = matrix(f->pos(), QPointF(0, 0), f->rotation(), f->mirror()) * iMatrix ;
            drawFeatures(painter, s->features(), featPolarity, iColor,
                         mat, mat.inverted().mapRect(iMatrix.mapRect(iSceneRect)),
                         iDispOption, iJob, iStep, iLayer);
            painter->save();
            QTransform txtmat;
            txtmat.scale(100, -100);
            painter->scale(0.01, -0.01);
            QFont font;
            font.setFamily(painter->font().family());
            font.setPixelSize(2);
            painter->setFont(font);
            painter->setPen(QColor(iColor));
            painter->drawText(txtmat.mapRect(f->boundingRect()), Qt::AlignCenter|Qt::TextDontClip, f->compName());
            painter->restore();
            continue;
        }
        else if (feat->type() == PcbCam::TextFeature) {
            PcbCamFeatureText *f = static_cast< PcbCamFeatureText*>(feat);
            if (iStep != nullptr && iLayer != nullptr) {
                //替换动态文字

                if (f->text().contains("$$")) {
                    QString old_dynamic_text = f->dynamicText();
                    QString new_dynamic_text = f->text();
                    if (iDispOption & PcbCam::DisplayTextValue) {

                        //找出需替换的替换字符
                        const QDate &date = QDate::currentDate();
                        const QTime &time = QTime::currentTime();
                        QRegularExpression rx("\\$\\$(\\S+)");
                        int replace_offset = 0;
                        auto match_iter = rx.globalMatch(new_dynamic_text);
                        while (match_iter.hasNext()) {
                            auto match = match_iter.next();
                            QString attrname = match.captured(1).toLower();
                            QString varname = match.captured(0);
                            int capture_start = match.capturedStart(0);

                            QString replacement;
                            if (attrname == "job" && iJob != nullptr) {
                                replacement = iJob->jobname().toUpper();
                            }
                            else if (attrname == "step" && iStep != nullptr) {
                                replacement = iStep->name().toUpper();
                            }
                            else if (attrname == "layer" && iLayer != nullptr) {
                                replacement = iLayer->name().toUpper();
                            }
                            else if (attrname == "x_mm") {
                                //TODO 目前都会后补7个零，后续需把后面的零去除
                                replacement = QString("%1").arg(f->pos().x() * 25.4, 0, 'f', 7);
                            }
                            else if (attrname == "y_mm") {
                                replacement = QString("%1").arg(f->pos().y() * 25.4, 0, 'f', 7);
                            }
                            else if (attrname == "x") {
                                replacement = QString("%1").arg(f->pos().x(), 0, 'f', 7);
                            }
                            else if (attrname == "y") {
                                replacement = QString("%1").arg(f->pos().y() , 0, 'f', 7);
                            }
                            else if (attrname == "date-mmddyy" || attrname == "date") {
                                replacement = QString().asprintf("%02d/%02d/%02d", date.month(), date.day(), date.year()%100);
                            }
                            else if (attrname == "date-ddmmyy" ) {
                                replacement = QString().asprintf("%02d/%02d/%02d", date.day(), date.month(), date.year()%100);
                            }
                            else if (attrname == "date-mmddyyyy") {
                                replacement = QString().asprintf("%02d/%02d/%04d", date.month(), date.day(), date.year());
                            }
                            else if (attrname == "date-ddmmyyyy") {
                                replacement = QString().asprintf("%02d/%02d/%04d", date.day(), date.month(), date.year());
                            }
                            else if (attrname == "dd") {
                                replacement = QString().asprintf("%02d", date.day());
                            }
                            else if (attrname == "mm") {
                                replacement = QString().asprintf("%02d", date.month());
                            }
                            else if (attrname == "yy") {
                                replacement = QString().asprintf("%02d", date.year()%100);
                            }
                            else if (attrname == "yyyy") {
                                replacement = QString().asprintf("%04d", date.year());
                            }
                            else if (attrname == "week-day") {
                                replacement = WEEK_DAY_STRING.value(date.dayOfWeek());
                            }
                            else if (attrname == "ww") {
                                replacement = QString().asprintf("%02d", date.weekNumber());
                            }
                            else if (attrname == "time") {
                                replacement = QString().asprintf("%02d:%02d", time.hour(), time.minute());
                            }

                            else if (f->hasAttr(attrname)) {
                                replacement = f->attr(attrname).toString().toUpper();
                            }
                            else if (iLayer != nullptr && iLayer->hasAttr(attrname)) {
                                replacement = iLayer->attr(attrname).toString().toUpper();
                            }
                            else if (iStep != nullptr && iStep->hasAttr(attrname)) {
                                replacement = iStep->attr(attrname).toString().toUpper();
                            }
                            else if (iJob != nullptr && iJob->hasJobAttr(attrname)) {
                                replacement = iJob->jobAttr(attrname).toString().toUpper();
                            }
                            else {
                                replacement = varname;
                            }

                            new_dynamic_text.replace(capture_start + replace_offset, varname.length(), replacement);
                            replace_offset += replacement.length() - varname.length();
                        }
                    }

                    if (new_dynamic_text != old_dynamic_text) {
                        f->setDynamicText(new_dynamic_text);
                    }
                }
            }
            QTransform mat = matrix(f->pos(), f->orient()) * iMatrix ;
            drawFeatures(painter, f->features(), featPolarity, iColor,
                         mat, mat.inverted().mapRect(iMatrix.mapRect(iSceneRect)),
                         iDispOption, iJob, iStep, iLayer);
            continue;

        }
        else if ((feat->type() == PcbCam::LineFeature || feat->type() == PcbCam::ArcFeature)
                 && ((feat->symbol()->type() == PcbCam::Sym_R && static_cast<PcbCamSymbolR*>(feat->symbol())->size() <= 0.0000001)
                     || (feat->symbol()->type() == PcbCam::Sym_S && static_cast<PcbCamSymbolS*>(feat->symbol())->size() <= 0.0000001)))
        {
            // 处理r0 s0的情况
            painter->setPen(outlinePen);
            painter->setBrush(Qt::NoBrush);
            if (feat->type() == PcbCam::LineFeature) {
                const PcbCamFeatureLine *f = static_cast<const PcbCamFeatureLine*>(feat);
                painter->drawPath(PcbCamUtil::line2path(f->line()));
            }
            else if (feat->type() == PcbCam::ArcFeature) {
                const PcbCamFeatureArc *f = static_cast<const PcbCamFeatureArc*>(feat);
                painter->drawPath(PcbCamUtil::arc2path(f->ps(), f->pe(), f->pc(), f->cw()));
            }
            continue;
        }


        QTransform tmpmat;
        if (iDispOption & PcbCam::DisplayOutline) { //边线模式
            painter->drawPath(feat->shape().intersected(clipPath));
            if (feat->type() == PcbCam::LineFeature) {

            }
            else if (feat->type() == PcbCam::ArcFeature) {

            }
        }
        else if (iDispOption & PcbCam::DisplaySketeon) { //中心线模式
            if (feat->type() == PcbCam::LineFeature) {
                const PcbCamFeatureLine *f = static_cast<const PcbCamFeatureLine*>(feat);
                painter->drawPath(PcbCamUtil::line2path(f->line()));
            }
            else if (feat->type() == PcbCam::ArcFeature) {
                const PcbCamFeatureArc *f = static_cast<const PcbCamFeatureArc*>(feat);
                painter->drawPath(PcbCamUtil::arc2path(f->ps(), f->pe(), f->pc(), f->cw()));
            }
            else {
                painter->drawPath(feat->shape().intersected(clipPath));
            }
        }
        else { //填充模式

            if (featPolarity == PcbCam::Negative && (iDispOption & PcbCam::DisplayNegative)) {
                painter->setBrush(fillBrush);
                painter->setCompositionMode(QPainter::CompositionMode_Clear);
                painter->drawPath(feat->shape().intersected(clipPath));
                painter->setBrush(negativeBrush);
                painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
                painter->drawPath(feat->shape().intersected(clipPath));
            }
            else {
                painter->setBrush(fillBrush);
                painter->drawPath(feat->shape().intersected(clipPath));
            }
        }
    }
    painter->restore();
}

int PcbCamUtil::orient2angle(PcbCam::Orient iOrient)
{
    switch (iOrient) {
    case PcbCam::Orient_N_0:
    case PcbCam::Orient_Y_0:
        return 0;

    case PcbCam::Orient_N_90:
    case PcbCam::Orient_Y_90:
        return 90;

    case PcbCam::Orient_N_180:
    case PcbCam::Orient_Y_180:
        return 180;


    case PcbCam::Orient_N_270:
    case PcbCam::Orient_Y_270:
        return 270;
    default:
        return 0;
    }
}

bool PcbCamUtil::orient2mirror(PcbCam::Orient iOrient)
{
    return (iOrient == PcbCam::Orient_Y_0
            || iOrient == PcbCam::Orient_Y_90
            || iOrient == PcbCam::Orient_Y_180
            || iOrient == PcbCam::Orient_Y_270 );
}

bool PcbCamUtil::isFeatMatchFilter(PcbCamFeature *iFeat,
                                   PcbCam::FeatureTypes iTypes,
                                   PcbCam::Polarities iPolarities,
                                   bool iIsFilterProfile,
                                   PcbCam::ProfileRegions iProfileRegion,
                                   const QPainterPath &iProfilePath,
                                   bool iIsFilterIncludeSymbol,
                                   QSet<PcbCamSymbol *> iIncludeSymbols,
                                   bool iIsFilterExcludeSymbol,
                                   QSet<PcbCamSymbol *> iExcludeSymbols,
                                   bool iIsFilterIncludeAttr,
                                   QList<PcbCam::AttrCompareInfo> iIncludeAttrs,
                                   PcbCam::LogicOperator iIncludeAttrLogic,
                                   bool iIsFilterExcludeAttr,
                                   QList<PcbCam::AttrCompareInfo> iExcludeAttrs,
                                   PcbCam::LogicOperator iExcludeAttrLogic)
{
    bool feat_match = false;
    if ((iTypes & iFeat->type())
            && (iPolarities & iFeat->polarity())
            && (!iIsFilterIncludeSymbol || iIncludeSymbols.contains(iFeat->symbol()))
            && (!iIsFilterExcludeSymbol || iFeat->symbol() == nullptr || !iExcludeSymbols.contains(iFeat->symbol()))
            )
    {
        auto featattr = iFeat->attr();
        bool include_attr_match = true;
        if (iIsFilterIncludeAttr) {
            include_attr_match = (iIncludeAttrLogic == PcbCam::Logic_And);
            for (auto attrcmp : iIncludeAttrs) {
                bool attrmatch = (featattr.contains(attrcmp.name)
                                  && ((attrcmp.type == 0)
                                      || (attrcmp.type == 1 && featattr.value(attrcmp.name).toString() == attrcmp.strValue)
                                      || (attrcmp.type == 2 && featattr.value(attrcmp.name).toDouble() >= attrcmp.minValue && featattr.value(attrcmp.name).toDouble() <= attrcmp.maxValue)));
                if (iIncludeAttrLogic == PcbCam::Logic_And && !attrmatch) {
                    include_attr_match = false;
                    break;
                }
                else if (iIncludeAttrLogic == PcbCam::Logic_Or && attrmatch) {
                    include_attr_match = true;
                    break;
                }
            }
        }


        bool exclude_attr_match = false;
        if (include_attr_match) {
            if (iIsFilterExcludeAttr) {
                exclude_attr_match = (iExcludeAttrLogic == PcbCam::Logic_And);
                for (auto attrcmp : iExcludeAttrs) {
                    bool attrmatch = (featattr.contains(attrcmp.name)
                                      && ((attrcmp.type == 0)
                                          || (attrcmp.type == 1 && featattr.value(attrcmp.name).toString() == attrcmp.strValue)
                                          || (attrcmp.type == 2 && featattr.value(attrcmp.name).toDouble() >= attrcmp.minValue && featattr.value(attrcmp.name).toDouble() <= attrcmp.maxValue)));
                    if (iExcludeAttrLogic == PcbCam::Logic_And && !attrmatch) {
                        exclude_attr_match = false;
                        break;
                    }
                    else if (iExcludeAttrLogic == PcbCam::Logic_Or && attrmatch) {
                        exclude_attr_match = true;
                        break;
                    }
                }
            }
        }

        feat_match = (include_attr_match && !exclude_attr_match);
    }

    if (!feat_match) return false;

    if (!iIsFilterProfile) return true;


    if (iProfileRegion == PcbCam::AllProfile) {
        return true;
    }
    else if (iProfileRegion == PcbCam::InProfile) {
        return iFeat->shape().intersects(iProfilePath);
    }
    else if (iProfileRegion == PcbCam::OutProfile) {
        return !iFeat->shape().intersects(iProfilePath);
    }
    return true;
}



QPainterPath PcbCamUtil::findClosedPath(const QList<PcbCamFeature *> &iFeatures, double iTol)
{
    auto allpaths = findPolyline(iFeatures, iTol);

    QPainterPath finalpath;
    double max_area = 0.0;
    for (auto path : allpaths) {
        if (path.elementCount() >= 3
                && qAbs(path.elementAt(0).x - path.elementAt(path.elementCount()-1).x) <= iTol
                && qAbs(path.elementAt(0).y - path.elementAt(path.elementCount()-1).y) <= iTol)
        {
            auto rect = path.boundingRect();
            auto area = rect.width() * rect.height();
            if (area > max_area) {
                finalpath = path;
                max_area = area;
            }
        }
    }
    finalpath.closeSubpath();
    return finalpath;
}

QList<QPainterPath> PcbCamUtil::findPolyline(const QList<PcbCamFeature *> &iFeatures, double iTol)
{
    QList<PcbCamFeature*> feats;
    for (auto f: iFeatures) {
        if (f->type() == PcbCam::LineFeature || f->type() == PcbCam::ArcFeature) {
            feats << f;
        }
    }

    auto appendFeatureToPath = [](QPainterPath *iPath, PcbCamFeature *iFeat, bool iInvert = false){
        if (iFeat->type() == PcbCam::LineFeature) {
            auto feat = static_cast<PcbCamFeatureLine*>(iFeat);
            auto ps = iInvert ? feat->line().p2() : feat->line().p1();
            auto pe = iInvert ? feat->line().p1() : feat->line().p2();
            if (iPath->isEmpty()) {
                iPath->moveTo(ps);
                iPath->lineTo(pe);
            }
            else {
                iPath->lineTo(pe);
            }

        }
        else if (iFeat->type() == PcbCam::ArcFeature) {
            auto feat = static_cast<PcbCamFeatureArc*>(iFeat);
            auto ps = iInvert ? feat->pe() : feat->ps();
            auto pe = iInvert ? feat->ps() : feat->pe();
            auto pc = feat->pc();
            bool cw = iInvert ? !feat->cw() : feat->cw();
            QVariantMap calcData = calcPathArc(ps, pe, pc, cw);
            double startAngle = calcData.value("start_angle").toDouble();
            double sweepLength = calcData.value("sweep_length").toDouble();
            QRectF rect = calcData.value("rect").toRectF();
            if (iPath->isEmpty()) {
                iPath->arcMoveTo(rect, startAngle);
                iPath->arcTo(rect, startAngle, sweepLength);
            }
            else {
                iPath->arcTo(rect, startAngle, sweepLength);
            }
        }
    };

    QList<QPainterPath> allpaths;
    QPainterPath path;
    QPointF lastCoord;
    while (feats.count() > 0) {
        bool findnext = false;
        bool invert = false;
        QPointF ps, pe;
        for (int n = 0; n < feats.count(); n++) {
            auto feat = feats[n];
            if (feat->type() == PcbCam::LineFeature) {
                auto linefeat = static_cast<PcbCamFeatureLine*>(feat);
                ps = linefeat->line().p1();
                pe = linefeat->line().p2();
            }
            else if (feat->type() == PcbCam::ArcFeature) {
                auto arcfeat = static_cast<PcbCamFeatureArc*>(feat);
                ps = arcfeat->ps();
                pe = arcfeat->pe();
            }

            if (path.isEmpty()) {
                findnext = true;
                invert = false;
            }
            else if (qAbs(lastCoord.x() - ps.x()) <= iTol && qAbs(lastCoord.y() - ps.y()) <= iTol) {
                findnext = true;
                invert = false;
            }
            else if (qAbs(lastCoord.x() - pe.x()) <= iTol && qAbs(lastCoord.y() - pe.y()) <= iTol) {
                findnext = true;
                invert = true;
            }

            if (findnext) {
                appendFeatureToPath(&path, feat, invert);
                feats.removeAt(n);
                lastCoord = invert ? ps : pe;
                break;
            }
        }
        if (!findnext) {
            if (!path.isEmpty()) {
                allpaths.append(path);
            }
            path = QPainterPath();
        }
    }

    if (!path.isEmpty()) {
        allpaths.append(path);
    }

    return allpaths;
}
