#include "odbedadataparser.h"
#include <QTextStream>
#include <QtMath>


class PcbCamOdbEdaDataParserPrivate
{
    Q_DECLARE_PUBLIC(PcbCamOdbEdaDataParser)
public:
    PcbCamOdbEdaDataParserPrivate(PcbCamOdbEdaDataParser *qptr) : q_ptr(qptr) { }
    ~PcbCamOdbEdaDataParserPrivate() { }

    QByteArray edaData;
    QList<QList<PcbCamOdbFeatureInfo>> packageList;
    QStringList errorList;
    QList<PcbCamOdbFeatureInfo> tmpFeatureList;

    PcbCamOdbFeatureInfo lastSurfaceInfo;
    QPainterPath lastPath;
    qreal lastSurfacePx {0.0};
    qreal lastSurfacePy {0.0};


protected:
    PcbCamOdbEdaDataParser * const q_ptr;
};

PcbCamOdbEdaDataParser::PcbCamOdbEdaDataParser(const QByteArray &iEdaData)
    : d_ptr(new PcbCamOdbEdaDataParserPrivate(this))
{
    Q_D(PcbCamOdbEdaDataParser);
    d->edaData = iEdaData;
}

PcbCamOdbEdaDataParser::~PcbCamOdbEdaDataParser()
{

}

void PcbCamOdbEdaDataParser::parse()
{
    Q_D(PcbCamOdbEdaDataParser);
    d->packageList.clear();
    d->errorList.clear();
    d->tmpFeatureList.clear();

    bool isInSurface = false;
    QTextStream stream(&d->edaData);
    QString line;
    int sectionFlag = 0; // 0:未知 1:PKG(Package Record) 2:PIN(Pin Record) 3:FGR(Feature Group Record) 4:FID(Feature ID Record) 5:PRP(Property Record)
    while (stream.readLineInto(&line)) {
        if (line.startsWith("#") || line.length() == 0) { // comment
            continue;
        }

        if (line.startsWith("PKG ")) {
            if (!d->tmpFeatureList.isEmpty()) {
                d->packageList.append(d->tmpFeatureList);
            }
            d->tmpFeatureList.clear();
            sectionFlag = 1;
        }
        else if (line.startsWith("PIN ")) {
            sectionFlag = 2;
        }
        else if (line.startsWith("FGR ")) {
            sectionFlag = 3;
        }
        else if (line.startsWith("FID ")) {
            sectionFlag = 4;
        }
        else if (line.startsWith("PRP ")) {
            sectionFlag = 5;
        }

        if (sectionFlag >= 1 && sectionFlag <= 4) {
            if (isInSurface) {
                if (line.startsWith("CE")) {
                    isInSurface = false;
                    PcbCamOdbFeatureInfo feat;
                    feat.type = PcbCam::PathFeature;
                    feat.path = d->lastPath;
                    feat.widthFactor = sectionFlag == 1 ? 2 : 1;
                    d->tmpFeatureList.append(feat);
                    d->lastPath = QPainterPath();
                }
                else {
                    parseSurfaceLine(line);
                }
                continue;
            }

            if (line.startsWith("CR")) { // circle record
                QStringList params = line.split(" ", Qt::SkipEmptyParts);
                PcbCamOdbFeatureInfo feat;
                feat.type = PcbCam::PathFeature;
                QPainterPath path;
                double xc = params.value(1).toDouble();
                double yc = params.value(2).toDouble();
                double r = params.value(3).toDouble();
                path.addEllipse(QPointF(xc, yc), r*2.0, r*2.0);
                feat.path = path;
                feat.widthFactor = sectionFlag == 1 ? 2 : 1;
                d->tmpFeatureList.append(feat);
            }
            else if (line.startsWith("SQ")) { // square record
                QStringList params = line.split(" ", Qt::SkipEmptyParts);
                PcbCamOdbFeatureInfo feat;
                feat.type = PcbCam::PathFeature;
                QPainterPath path;
                double xc = params.value(1).toDouble();
                double yc = params.value(2).toDouble();
                double r = params.value(3).toDouble();
                path.addRect(xc-r, yc-r, r*2.0, r*2.0);
                feat.path = path;
                feat.widthFactor = sectionFlag == 1 ? 2 : 1;
                d->tmpFeatureList.append(feat);
            }
            else if (line.startsWith("RC")) { // rectangle record
                QStringList params = line.split(" ", Qt::SkipEmptyParts);
                PcbCamOdbFeatureInfo feat;
                feat.type = PcbCam::PathFeature;
                QPainterPath path;
                double xs = params.value(1).toDouble();
                double ys = params.value(2).toDouble();
                double w = params.value(3).toDouble();
                double h = params.value(4).toDouble();
                path.addRect(xs, ys, w, h);
                feat.path = path;
                feat.widthFactor = sectionFlag == 1 ? 2 : 1;
                d->tmpFeatureList.append(feat);
            }
            else if (line.startsWith("CT")) { // start contour record
                d->lastPath = QPainterPath();
                isInSurface = true;
            }
        }
    }
    if (!d->tmpFeatureList.isEmpty()) {
        d->packageList.append(d->tmpFeatureList);
    }
}

QList<QList<PcbCamOdbFeatureInfo> > PcbCamOdbEdaDataParser::packageFeatures()
{
    Q_D(const PcbCamOdbEdaDataParser);
    return d->packageList;
}

QStringList PcbCamOdbEdaDataParser::errors()
{
    Q_D(const PcbCamOdbEdaDataParser);
    return d->errorList;
}

void PcbCamOdbEdaDataParser::parseSurfaceLine(const QString &iLine)
{
    Q_D(PcbCamOdbEdaDataParser);
    QStringList param = iLine.split(" ", Qt::SkipEmptyParts);
    if (param.value(0) == "OB") {
        d->lastSurfacePx = param.value(1).toDouble();
        d->lastSurfacePy = param.value(2).toDouble();
        d->lastPath.moveTo(d->lastSurfacePx, d->lastSurfacePy);
    }
    else if (param.value(0) == "OS") {
        d->lastSurfacePx = param.value(1).toDouble();
        d->lastSurfacePy = param.value(2).toDouble();
        d->lastPath.lineTo(d->lastSurfacePx, d->lastSurfacePy);
    }
    else if (param.value(0) == "OC") {
        qreal sx = d->lastSurfacePx, sy = d->lastSurfacePy;
        qreal ex = param.value(1).toDouble(), ey = param.value(2).toDouble();
        qreal cx = param.value(3).toDouble(), cy = param.value(4).toDouble();
        bool cw = param.value(5) == "Y" ? true : false;

        qreal sax = sx - cx, say = sy - cy;
        qreal eax = ex - cx, eay = ey - cy;

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

        qreal sa = qAtan2(-say, sax);
        qreal ea = qAtan2(-eay, eax);
        if (cw) {
            if (ea <= sa) {
                ea += 2.0 * M_PI;
            }
        }
        else {
            if (sa <= ea) {
                sa += 2.0 * M_PI;
            }
        }
        d->lastPath.arcTo(QRectF(cx -r, cy -r, r *2, r *2), sa * (180.0 / M_PI), (ea - sa) * (180.0 / M_PI));

        d->lastSurfacePx = ex;
        d->lastSurfacePy = ey;
    }
    else if (param.value(0) == "OE") {
        d->lastPath.closeSubpath();
    }
}

