#include "odbfeatureparser.h"
#include <QTextStream>
#include <QStringList>
#include <QtMath>
#include "../core/util.h"


class PcbCamOdbFeatureParserPrivate
{
    Q_DECLARE_PUBLIC(PcbCamOdbFeatureParser)
public:
    PcbCamOdbFeatureParserPrivate(PcbCamOdbFeatureParser *qptr) : q_ptr(qptr) { }
    ~PcbCamOdbFeatureParserPrivate() { }

    QList<PcbCamOdbFeatureInfo> featureList;
    PcbCamOdbFeatureInfo lastSurfaceInfo;
    QStringList errorList;
    QByteArray featData;
    QPainterPath lastPath;
    qreal lastSurfacePx {0.0};
    qreal lastSurfacePy {0.0};

    QHash<int, QString> symbolsHash;
    QHash<int, QString> attrNameHash;
    QHash<int, QString> attrTextHash;

protected:
    PcbCamOdbFeatureParser * const q_ptr;
};


PcbCamOdbFeatureParser::PcbCamOdbFeatureParser(const QByteArray &iFeatData)
    : d_ptr(new PcbCamOdbFeatureParserPrivate(this))
{
    Q_D(PcbCamOdbFeatureParser);
    d->featData = iFeatData;
}

PcbCamOdbFeatureParser::~PcbCamOdbFeatureParser()
{

}

void PcbCamOdbFeatureParser::parse()
{
    Q_D(PcbCamOdbFeatureParser);
    d->symbolsHash.clear();
    d->attrNameHash.clear();
    d->attrTextHash.clear();
    d->featureList.clear();
    d->errorList.clear();

    bool isInSurface = false;

    QTextStream stream(&d->featData);
    QString line;
    while (stream.readLineInto(&line)) {

        if (line.startsWith("#") || line.length() == 0) { // comment
          continue;
        }

        if (isInSurface) {
          if (line.startsWith("SE")) {
            isInSurface = false;
            parseSurfaceEnd();
          } else {
            parseSurfaceLine(line);
          }
          continue;
        }

        if (line.startsWith("$")) { // symbol names
            parseSymbolName(line);
        } else if (line.startsWith("@")) { // attrib names
            parseAttrName(line);
        } else if (line.startsWith("&")) { // attrib text strings
           parseAttrText(line);
        } else if (line.startsWith("L")) { // line
            parseLine(line);
        } else if (line.startsWith("P")) { // pad
            parsePad(line);
        } else if (line.startsWith("A")) { // arc
            parseArc(line);
        } else if (line.startsWith("T")) { // text
            parseText(line);
        } else if (line.startsWith("B")) { // barcode
            parseBarcode(line);
        } else if (line.startsWith("S")) { // surface
            parseSurfaceStart(line);
            isInSurface = true;
        }
    }


}

QList<PcbCamOdbFeatureInfo> PcbCamOdbFeatureParser::features()
{
    Q_D(const PcbCamOdbFeatureParser);
    return d->featureList;
}

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

QStringList PcbCamOdbFeatureParser::symbols()
{
    Q_D(const PcbCamOdbFeatureParser);
    return d->symbolsHash.values();
}


void PcbCamOdbFeatureParser::parseSymbolName(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList tmp = iLine.split(" ", Qt::SkipEmptyParts);
    if (tmp.length() == 2) {
        d->symbolsHash.insert(tmp[0].mid(1).toInt(), tmp[1]);
    }
}

void PcbCamOdbFeatureParser::parseAttrName(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList tmp = iLine.split(" ", Qt::SkipEmptyParts);
    if (tmp.length() == 2) {
        d->attrNameHash.insert(tmp[0].mid(1).toInt(), tmp[1]);
    }
}


void PcbCamOdbFeatureParser::parseAttrText(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList tmp = iLine.split(" ", Qt::SkipEmptyParts);
    if (tmp.length() == 2) {
        d->attrTextHash.insert(tmp[0].mid(1).toInt(), tmp[1]);
    }
}

void PcbCamOdbFeatureParser::parseLine(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList param;
    QVariantMap attrmap;
    parseAttributes(iLine, &param, &attrmap);

    PcbCamOdbFeatureInfo feat;
    feat.type = PcbCam::LineFeature;
    feat.ps = QPointF(param.value(1).toDouble(), param.value(2).toDouble());
    feat.pe = QPointF(param.value(3).toDouble(), param.value(4).toDouble());
    feat.symbol = d->symbolsHash.value(param.value(5).toInt());
    feat.polarity = param.value(6) == "P" ? PcbCam::Positive : PcbCam::Negative;
    feat.decode = param.value(7).toInt();
    feat.attributes = attrmap;
    d->featureList.append(feat);

}

void PcbCamOdbFeatureParser::parsePad(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList param;
    QVariantMap attrmap;
    parseAttributes(iLine, &param, &attrmap);

    PcbCamOdbFeatureInfo feat;
    feat.type = PcbCam::PadFeature;
    feat.ps = QPointF(param.value(1).toDouble(), param.value(2).toDouble());
    feat.symbol = d->symbolsHash.value(param.value(3).toInt());
    feat.polarity = param.value(4) == "P" ? PcbCam::Positive : PcbCam::Negative;
    feat.decode = param.value(5).toInt();
    feat.orient = (PcbCam::Orient)param.value(6).toInt();
    feat.attributes = attrmap;
    d->featureList.append(feat);

}

void PcbCamOdbFeatureParser::parseArc(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList param;
    QVariantMap attrmap;
    parseAttributes(iLine, &param, &attrmap);

    PcbCamOdbFeatureInfo feat;
    feat.type = PcbCam::ArcFeature;
    feat.ps = QPointF(param.value(1).toDouble(), param.value(2).toDouble());
    feat.pe = QPointF(param.value(3).toDouble(), param.value(4).toDouble());
    feat.pc = QPointF(param.value(5).toDouble(), param.value(6).toDouble());
    feat.cw = param.value(10) == "Y" ? true : false;
    feat.symbol = d->symbolsHash.value(param.value(7).toInt());
    feat.polarity = param.value(8) == "P" ? PcbCam::Positive : PcbCam::Negative;
    feat.decode = param.value(9).toInt();
    feat.attributes = attrmap;

    d->featureList.append(feat);
}

void PcbCamOdbFeatureParser::parseText(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList param;
    QVariantMap attrmap;
    parseAttributes(iLine, &param, &attrmap);

    PcbCamOdbFeatureInfo feat;
    feat.type = PcbCam::TextFeature;
    feat.ps = QPointF(param.value(1).toDouble(), param.value(2).toDouble());
    feat.font = param.value(3);
    feat.polarity = param.value(4) == "P" ? PcbCam::Positive : PcbCam::Negative;
    feat.orient = (PcbCam::Orient)param.value(5).toInt();
    feat.size = QSizeF(param.value(6).toDouble(), param.value(7).toDouble());
    feat.widthFactor = param.value(8).toDouble();
    feat.text = param.value(9);
    feat.version = param.value(10).toInt();
    feat.attributes = attrmap;

    d->featureList.append(feat);

}

void PcbCamOdbFeatureParser::parseBarcode(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList param;
    QVariantMap attrmap;
    parseAttributes(iLine, &param, &attrmap);

    PcbCamOdbFeatureInfo feat;
    feat.type = PcbCam::BarcodeFeature;
    feat.ps = QPointF(param.value(1).toDouble(), param.value(2).toDouble());
    feat.barcode = param.value(3);
    feat.font = param.value(4);
    feat.polarity = param.value(5) == "P" ? PcbCam::Positive : PcbCam::Negative;
    feat.orient = (PcbCam::Orient)param.value(6).toInt();
    feat.size = QSize(param.value(8).toDouble(), param.value(9).toDouble());
    feat.fullAscii = param.value(10) == "Y" ? true : false;
    feat.checkSum = param.value(11) == "Y" ? true : false;
    feat.background = param.value(12) == "Y" ? true : false;
    feat.additionStr = param.value(13) == "Y" ? true : false;
    feat.additionStrPos = param.value(14) == "T" ? PcbCam::BarcodeTextPos_Top : PcbCam::BarcodeTextPos_Bot;
    feat.text = param.value(15);
    feat.attributes = attrmap;

    d->featureList.append(feat);
}

void PcbCamOdbFeatureParser::parseSurfaceStart(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    QStringList param;
    QVariantMap attrmap;
    parseAttributes(iLine, &param, &attrmap);

    d->lastSurfaceInfo = PcbCamOdbFeatureInfo();
    d->lastSurfaceInfo.type = PcbCam::SurfaceFeature;
    d->lastPath = QPainterPath();
    d->lastSurfaceInfo.polarity = param.value(1) == "P" ? PcbCam::Positive : PcbCam::Negative;
    d->lastSurfaceInfo.decode = param.value(2).toInt();
    d->lastSurfaceInfo.attributes = attrmap;
}

void PcbCamOdbFeatureParser::parseSurfaceLine(const QString &iLine)
{
    Q_D(PcbCamOdbFeatureParser);
    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;
        QVariantMap calcData = PcbCamUtil::calcPathArc(QPointF(sx, sy), QPointF(ex, ey), QPointF(cx, cy), cw);
        double startAngle = calcData.value("start_angle").toDouble();
        double sweepLength = calcData.value("sweep_length").toDouble();
        QRectF arcrect = calcData.value("rect").toRectF();

        d->lastPath.arcTo(arcrect, startAngle, sweepLength);

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

void PcbCamOdbFeatureParser::parseSurfaceEnd()
{
    Q_D(PcbCamOdbFeatureParser);
    d->lastSurfaceInfo.path = d->lastPath;
    d->featureList.append(d->lastSurfaceInfo);
}

void PcbCamOdbFeatureParser::parseAttributes(const QString &iLine, QStringList *oParams, QVariantMap *oAttrMap) const
{
    Q_D(const PcbCamOdbFeatureParser);
    //拆分属性
    //P 20.31496063 4.86220472 11 N 0 0;2=62
    int loc = iLine.lastIndexOf(";");
    QString record = iLine.left(loc).trimmed();
    QString attr;
    if (loc != -1) {
        attr = iLine.right(iLine.length() - loc - 1).trimmed();
    }

    //拆分文字的单引号
    //T -1.1023622 1.10629921 standard P 3 0.08661417 0.10031496 0.82021 'X=0.000 Y=545.900 D=545.900' 1;0
    if (record.indexOf("'") != -1) {
        int loc = record.indexOf("'");
        int loc2 = record.indexOf("'", loc + 1);
        QString left = record.left(loc);
        QString middle = record.mid(loc + 1, loc2 - loc - 1);
        QString right = record.right(record.length() - loc2 - 1);
        *oParams = left.split(" ", Qt::SkipEmptyParts);
        *oParams << middle;
        *oParams += right.split(" ", Qt::SkipEmptyParts);
    } else {
        *oParams = record.split(" ", Qt::SkipEmptyParts);
    }

    if (!attr.isEmpty()) {
        QStringList terms = attr.split(',');
        for (int i = 0; i < terms.size(); ++i) {
            QStringList v = terms[i].split('=');
            QString key = d->attrNameHash.value(v[0].toInt());
            if (v.size() == 1) {
                oAttrMap->insert(key, QVariant(true));
            } else {
                oAttrMap->insert(key, d->attrTextHash.value(v[1].toInt()));
            }
        }
    }
}

