/******************************************************************************
**  libDXFrw - Library to read/write DXF files (ascii & binary)              **
**                                                                           **
**  Copyright (C) 2016-2022 A. Stebich (librecad@mail.lordofbikes.de)        **
**  Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com               **
**                                                                           **
**  This library is free software, licensed under the terms of the GNU       **
**  General Public License as published by the Free Software Foundation,     **
**  either version 2 of the License, or (at your option) any later version.  **
**  You should have received a copy of the GNU General Public License        **
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.    **
******************************************************************************/

#include "drw_entities.h"
#include "drw_reserve.h"
#include "intern/drw_dbg.h"
#include "intern/dxfreader.h"
#include <cstdlib>

//! Calculate arbitrary axis
/*!
 *   Calculate arbitrary axis for apply extrusions
 *  @author Rallaz
 */
void DRW_Entity::calculateAxis(DRW_Coord extPoint)
{
    // Follow the arbitrary DXF definitions for extrusion axes.
    if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625)
    {
        // If we get here, implement Ax = Wy x N where Wy is [0,1,0] per the DXF spec.
        // The cross product works out to Wy.y*N.z-Wy.z*N.y, Wy.z*N.x-Wy.x*N.z, Wy.x*N.y-Wy.y*N.x
        // Factoring in the fixed values for Wy gives N.z,0,-N.x
        extAxisX.x = extPoint.z;
        extAxisX.y = 0;
        extAxisX.z = -extPoint.x;
    }
    else
    {
        // Otherwise, implement Ax = Wz x N where Wz is [0,0,1] per the DXF spec.
        // The cross product works out to Wz.y*N.z-Wz.z*N.y, Wz.z*N.x-Wz.x*N.z, Wz.x*N.y-Wz.y*N.x
        // Factoring in the fixed values for Wz gives -N.y,N.x,0.
        extAxisX.x = -extPoint.y;
        extAxisX.y = extPoint.x;
        extAxisX.z = 0;
    }

    extAxisX.unitize();

    // Ay = N x Ax
    extAxisY.x = (extPoint.y * extAxisX.z) - (extAxisX.y * extPoint.z);
    extAxisY.y = (extPoint.z * extAxisX.x) - (extAxisX.z * extPoint.x);
    extAxisY.z = (extPoint.x * extAxisX.y) - (extAxisX.x * extPoint.y);

    extAxisY.unitize();
}

//! Extrude a point using arbitrary axis
/*!
 *   apply extrusion in a point using arbitrary axis (previous calculated)
 *  @author Rallaz
 */
void DRW_Entity::extrudePoint(DRW_Coord extPoint, DRW_Coord *point) const
{
    double px, py, pz;
    px = (extAxisX.x * point->x) + (extAxisY.x * point->y) + (extPoint.x * point->z);
    py = (extAxisX.y * point->x) + (extAxisY.y * point->y) + (extPoint.y * point->z);
    pz = (extAxisX.z * point->x) + (extAxisY.z * point->y) + (extPoint.z * point->z);

    point->x = px;
    point->y = py;
    point->z = pz;
}

auto DRW_Entity::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 5:
            handle = static_cast<quint32>(reader->getHandleString());
            break;
        case 330:
            parentHandle = static_cast<quint32>(reader->getHandleString());
            break;
        case 8:
            layer = reader->getUtf8String();
            break;
        case 6:
            lineType = reader->getUtf8String();
            break;
        case 62:
            color = reader->getInt32();
            break;
        case 370:
            lWeight = DRW_LW_Conv::dxfInt2lineWidth(reader->getInt32());
            break;
        case 48:
            ltypeScale = reader->getDouble();
            break;
        case 60:
            visible = reader->getBool();
            break;
        case 420:
            color24 = reader->getInt32();
            break;
        case 430:
            colorName = reader->getString();
            break;
        case 67:
            space = static_cast<DRW::Space>(reader->getInt32());
            break;
        case 102:
            parseDxfGroups(code, reader);
            break;
        case 1000:
        case 1001:
        case 1002:
        case 1003:
        case 1004:
        case 1005:
            extData.push_back(new DRW_Variant(code, reader->getString()));
            break;
        case 1010:
        case 1011:
        case 1012:
        case 1013:
            curr = new DRW_Variant();
            curr->addCoord();
            curr->setCoordX(reader->getDouble());
            curr->code = code;
            extData.push_back(curr);
            break;
        case 1020:
        case 1021:
        case 1022:
        case 1023:
            if (curr)
                curr->setCoordY(reader->getDouble());
            break;
        case 1030:
        case 1031:
        case 1032:
        case 1033:
            if (curr)
                curr->setCoordZ(reader->getDouble());
            curr = nullptr;
            break;
        case 1040:
        case 1041:
        case 1042:
            extData.push_back(new DRW_Variant(code, reader->getDouble()));
            break;
        case 1070:
        case 1071:
            extData.push_back(new DRW_Variant(code, reader->getInt32()));
            break;
        default:
            break;
    }
    return true;
}

// parses dxf 102 groups to read entity
auto DRW_Entity::parseDxfGroups(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    std::list<DRW_Variant> ls;
    DRW_Variant c;
    int nc;
    if (std::string appName = reader->getString(); !appName.empty() && appName.at(0) == '{')
    {
        c.addString(appName.substr(1, static_cast<size_t>(appName.size() - 1)));
        c.code = code;
        ls.push_back(c);
        while (code != 102 && appName.at(0) == '}')
        {
            reader->readRec(&nc);
            c.code = code;
            if (code == 330 || code == 360)
                c.addInt(reader->getHandleString());
            else
            {
                switch (reader->type)
                {
                    case dxfReader::STRING:
                        c.addString(reader->getString());
                        break;
                    case dxfReader::INT32:
                    case dxfReader::INT64:
                        c.addInt(reader->getInt32());
                        break;
                    case dxfReader::DOUBLE:
                        c.addDouble(reader->getDouble());
                        break;
                    case dxfReader::BOOL:
                        c.addInt(reader->getInt32());
                        break;
                    default:
                        break;
                }
            }
            ls.push_back(c);
        }
    }

    appData.push_back(ls);
    return true;
}

auto DRW_Point::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 10:
            basePoint.x = reader->getDouble();
            break;
        case 20:
            basePoint.y = reader->getDouble();
            break;
        case 30:
            basePoint.z = reader->getDouble();
            break;
        case 39:
            thickness = reader->getDouble();
            break;
        case 210:
            haveExtrusion = true;
            extPoint.x = reader->getDouble();
            break;
        case 220:
            extPoint.y = reader->getDouble();
            break;
        case 230:
            extPoint.z = reader->getDouble();
            break;
        default:
            return DRW_Entity::parseCode(code, reader);
    }

    return true;
}

auto DRW_ASTMNotch::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 50:
            angle = reader->getDouble();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

auto DRW_Line::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 11:
            secPoint.x = reader->getDouble();
            break;
        case 21:
            secPoint.y = reader->getDouble();
            break;
        case 31:
            secPoint.z = reader->getDouble();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

void DRW_Circle::applyExtrusion()
{
    if (haveExtrusion)
    {
        // NOTE: Commenting these out causes the the arcs being tested to be located
        // on the other side of the y axis (all x dimensions are negated).
        calculateAxis(extPoint);
        extrudePoint(extPoint, &basePoint);
    }
}

auto DRW_Circle::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 40:
            radious = reader->getDouble();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

void DRW_Arc::applyExtrusion()
{
    DRW_Circle::applyExtrusion();

    // If the extrusion vector has a z value less than 0, the angles for the arc
    // have to be mirrored since DXF files use the right hand rule.
    // Note that the following code only handles the special case where there is a 2D
    // drawing with the z axis heading into the paper (or rather screen). An arbitrary
    // extrusion axis (with x and y values greater than 1/64) may still have issues.
    if (haveExtrusion && fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625 && extPoint.z < 0.0)
    {
        staangle = M_PI - staangle;
        endangle = M_PI - endangle;

        double const temp = staangle;
        staangle = endangle;
        endangle = temp;
    }
}

auto DRW_Arc::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 50:
            staangle = reader->getDouble() / ARAD;
            break;
        case 51:
            endangle = reader->getDouble() / ARAD;
            break;
        default:
            return DRW_Circle::parseCode(code, reader);
    }

    return true;
}

auto DRW_Ellipse::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 40:
            ratio = reader->getDouble();
            break;
        case 41:
            staparam = reader->getDouble();
            break;
        case 42:
            endparam = reader->getDouble();
            break;
        default:
            return DRW_Line::parseCode(code, reader);
    }

    return true;
}

void DRW_Ellipse::applyExtrusion()
{
    if (haveExtrusion)
    {
        calculateAxis(extPoint);
        extrudePoint(extPoint, &secPoint);
        double const intialparam = staparam;
        if (extPoint.z < 0.)
        {
            staparam = M_PIx2 - endparam;
            endparam = M_PIx2 - intialparam;
        }
    }
}

// if ratio > 1 minor axis are greather than major axis, correct it
void DRW_Ellipse::correctAxis()
{
    bool complete = false;
    if (DRW_FuzzyComparePossibleNulls(staparam, endparam))
    {
        staparam = 0.0;
        endparam = M_PIx2; // 2*M_PI;
        complete = true;
    }
    if (ratio > 1)
    {
        if (fabs(endparam - staparam - M_PIx2) < 1.0e-10)
            complete = true;
        double const incX = secPoint.x;
        secPoint.x = -(secPoint.y * ratio);
        secPoint.y = incX * ratio;
        ratio = 1 / ratio;
        if (!complete)
        {
            if (staparam < M_PI_2)
                staparam += M_PI * 2;
            if (endparam < M_PI_2)
                endparam += M_PI * 2;
            endparam -= M_PI_2;
            staparam -= M_PI_2;
        }
    }
}

// parts are the number of vertex to split polyline, default 128
void DRW_Ellipse::toPolyline(DRW_Polyline *pol, int parts)
{
    double radMajor, radMinor, cosRot, sinRot, incAngle, curAngle;
    radMajor = sqrt(secPoint.x * secPoint.x + secPoint.y * secPoint.y);
    radMinor = radMajor * ratio;
    // calculate sin & cos of included angle
    incAngle = atan2(secPoint.y, secPoint.x);
    cosRot = cos(incAngle);
    sinRot = sin(incAngle);
    incAngle = M_PIx2 / parts;
    curAngle = staparam;
    auto i = static_cast<int>(curAngle / incAngle);
    do
    {
        if (curAngle > endparam)
        {
            curAngle = endparam;
            i = parts + 2;
        }
        double const cosCurr = cos(curAngle);
        double const sinCurr = sin(curAngle);
        double const x = basePoint.x + (cosCurr * cosRot * radMajor) - (sinCurr * sinRot * radMinor);
        double const y = basePoint.y + (cosCurr * sinRot * radMajor) + (sinCurr * cosRot * radMinor);
        pol->addVertex(DRW_Vertex(x, y, 0.0, 0.0));
        curAngle = (++i) * incAngle;
    } while (i < parts);
    if (fabs(endparam - staparam - M_PIx2) < 1.0e-10)
    {
        pol->flags = 1;
    }
    pol->layer = this->layer;
    pol->lineType = this->lineType;
    pol->color = this->color;
    pol->lWeight = this->lWeight;
    pol->extPoint = this->extPoint;
}

void DRW_Trace::applyExtrusion()
{
    if (haveExtrusion)
    {
        calculateAxis(extPoint);
        extrudePoint(extPoint, &basePoint);
        extrudePoint(extPoint, &secPoint);
        extrudePoint(extPoint, &thirdPoint);
        extrudePoint(extPoint, &fourPoint);
    }
}

auto DRW_Trace::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 12:
            thirdPoint.x = reader->getDouble();
            break;
        case 22:
            thirdPoint.y = reader->getDouble();
            break;
        case 32:
            thirdPoint.z = reader->getDouble();
            break;
        case 13:
            fourPoint.x = reader->getDouble();
            break;
        case 23:
            fourPoint.y = reader->getDouble();
            break;
        case 33:
            fourPoint.z = reader->getDouble();
            break;
        default:
            return DRW_Line::parseCode(code, reader);
    }

    return true;
}

auto DRW_3Dface::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 70:
            invisibleflag = reader->getInt32();
            break;
        default:
            return DRW_Trace::parseCode(code, reader);
    }

    return true;
}

auto DRW_Block::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 2:
            name = reader->getUtf8String();
            break;
        case 70:
            flags = reader->getInt32();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

auto DRW_Insert::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 2:
            name = reader->getUtf8String();
            break;
        case 41:
            xscale = reader->getDouble();
            break;
        case 42:
            yscale = reader->getDouble();
            break;
        case 43:
            zscale = reader->getDouble();
            break;
        case 50:
            angle = reader->getDouble();
            angle = angle / ARAD; // convert to radian
            break;
        case 70:
            colcount = reader->getInt32();
            break;
        case 71:
            rowcount = reader->getInt32();
            break;
        case 44:
            colspace = reader->getDouble();
            break;
        case 45:
            rowspace = reader->getDouble();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

void DRW_LWPolyline::applyExtrusion()
{
    if (haveExtrusion)
    {
        calculateAxis(extPoint);
        for (unsigned int i = 0; i < vertlist.size(); i++)
        {
            DRW_Vertex2D *vert = vertlist.at(i);
            DRW_Coord v(vert->x, vert->y, elevation);
            extrudePoint(extPoint, &v);
            vert->x = v.x;
            vert->y = v.y;
        }
    }
}

auto DRW_LWPolyline::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 10:
        {
            vertex = new DRW_Vertex2D();
            vertlist.push_back(vertex);
            vertex->x = reader->getDouble();
            break;
        }
        case 20:
            if (vertex != nullptr)
                vertex->y = reader->getDouble();
            break;
        case 40:
            if (vertex != nullptr)
                vertex->stawidth = reader->getDouble();
            break;
        case 41:
            if (vertex != nullptr)
                vertex->endwidth = reader->getDouble();
            break;
        case 42:
            if (vertex != nullptr)
                vertex->bulge = reader->getDouble();
            break;
        case 38:
            elevation = reader->getDouble();
            break;
        case 39:
            thickness = reader->getDouble();
            break;
        case 43:
            width = reader->getDouble();
            break;
        case 70:
            flags = reader->getInt32();
            break;
        case 90:
            vertexnum = reader->getInt32();
            return DRW::reserve(vertlist, vertexnum);
        case 210:
            haveExtrusion = true;
            extPoint.x = reader->getDouble();
            break;
        case 220:
            extPoint.y = reader->getDouble();
            break;
        case 230:
            extPoint.z = reader->getDouble();
            break;
        default:
            return DRW_Entity::parseCode(code, reader);
    }

    return true;
}

auto DRW_Text::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 40:
            height = reader->getDouble();
            break;
        case 41:
            widthscale = reader->getDouble();
            break;
        case 50:
            angle = reader->getDouble();
            break;
        case 51:
            oblique = reader->getDouble();
            break;
        case 71:
            textgen = reader->getInt32();
            break;
        case 72:
            alignH = static_cast<HAlign>(reader->getInt32());
            break;
        case 73:
            alignV = static_cast<VAlign>(reader->getInt32());
            break;
        case 1:
            text = reader->getUtf8String();
            break;
        case 7:
            style = reader->getUtf8String();
            break;
        default:
            return DRW_Line::parseCode(code, reader);
    }

    return true;
}

auto DRW_MText::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 1:
            text += reader->getString();
            text = reader->toUtf8String(text);
            break;
        case 11:
            hasXAxisVec = true;
            return DRW_Text::parseCode(code, reader);
        case 3:
            text += reader->getString();
            break;
        case 44:
            interlin = reader->getDouble();
            break;
        case 71:
        {
            // Attachment point
            auto a = static_cast<Attach>(reader->getInt32());

            QT_WARNING_PUSH
            QT_WARNING_DISABLE_GCC("-Wswitch-default")
            QT_WARNING_DISABLE_CLANG("-Wswitch-default")

            switch (a)
            {
                case TopLeft:
                    alignV = VTop;
                    alignH = HLeft;
                    break;
                case TopCenter:
                    alignV = VTop;
                    alignH = HCenter;
                    break;
                case TopRight:
                    alignV = VTop;
                    alignH = HRight;
                    break;
                case MiddleLeft:
                    alignV = VMiddle;
                    alignH = HLeft;
                    break;
                case MiddleCenter:
                    alignV = VMiddle;
                    alignH = HCenter;
                    break;
                case MiddleRight:
                    alignV = VMiddle;
                    alignH = HRight;
                    break;
                case BottomLeft:
                    alignV = VBottom;
                    alignH = HLeft;
                    break;
                case BottomCenter:
                    alignV = VBottom;
                    alignH = HCenter;
                    break;
                case BottomRight:
                    alignV = VBottom;
                    alignH = HRight;
                    break;
            }

            QT_WARNING_POP
        }
        break;
        case 72:
            // To prevent redirection to DRW_Text::parseCode.
            // This code meaning is different for MTEXT.
            // Actually: Drawing direction
            break;
        case 73:
            // To prevent redirection to DRW_Text::parseCode.
            // This code meaning is different for MTEXT.
            // Actually: Mtext line spacing style
            break;
        default:
            return DRW_Text::parseCode(code, reader);
    }

    return true;
}

void DRW_MText::updateAngle()
{
    if (hasXAxisVec)
    {
        angle = atan2(secPoint.y, secPoint.x) * 180 / M_PI;
    }
}

auto DRW_Polyline::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 70:
            flags = reader->getInt32();
            break;
        case 40:
            defstawidth = reader->getDouble();
            break;
        case 41:
            defendwidth = reader->getDouble();
            break;
        case 71:
            vertexcount = reader->getInt32();
            break;
        case 72:
            facecount = reader->getInt32();
            break;
        case 73:
            smoothM = reader->getInt32();
            break;
        case 74:
            smoothN = reader->getInt32();
            break;
        case 75:
            curvetype = reader->getInt32();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

auto DRW_Vertex::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 70:
            flags = reader->getInt32();
            break;
        case 40:
            stawidth = reader->getDouble();
            break;
        case 41:
            endwidth = reader->getDouble();
            break;
        case 42:
            bulge = reader->getDouble();
            break;
        case 50:
            tgdir = reader->getDouble();
            break;
        case 71:
            vindex1 = reader->getInt32();
            break;
        case 72:
            vindex2 = reader->getInt32();
            break;
        case 73:
            vindex3 = reader->getInt32();
            break;
        case 74:
            vindex4 = reader->getInt32();
            break;
        case 91:
            identifier = reader->getInt32();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

auto DRW_Hatch::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 2:
            name = reader->getUtf8String();
            break;
        case 70:
            solid = reader->getInt32();
            break;
        case 71:
            associative = reader->getInt32();
            break;
        case 72: /*edge type*/
            if (ispol)
            { // if is polyline is a as_bulge flag
                break;
            }
            else if (reader->getInt32() == 1)
            { // line
                addLine();
            }
            else if (reader->getInt32() == 2)
            { // arc
                addArc();
            }
            else if (reader->getInt32() == 3)
            { // elliptic arc
                addEllipse();
            }
            else if (reader->getInt32() == 4)
            { // spline
                addSpline();
            }
            break;
        case 10:
            if (pt)
                pt->basePoint.x = reader->getDouble();
            else if (pline)
            {
                plvert = pline->addVertex();
                plvert->x = reader->getDouble();
            }
            break;
        case 20:
            if (pt)
                pt->basePoint.y = reader->getDouble();
            else if (plvert)
                plvert->y = reader->getDouble();
            break;
        case 11:
            if (line)
                line->secPoint.x = reader->getDouble();
            else if (ellipse)
                ellipse->secPoint.x = reader->getDouble();
            break;
        case 21:
            if (line)
                line->secPoint.y = reader->getDouble();
            else if (ellipse)
                ellipse->secPoint.y = reader->getDouble();
            break;
        case 40:
            if (arc)
                arc->radious = reader->getDouble();
            else if (ellipse)
                ellipse->ratio = reader->getDouble();
            break;
        case 41:
            scale = reader->getDouble();
            break;
        case 42:
            if (plvert)
                plvert->bulge = reader->getDouble();
            break;
        case 50:
            if (arc)
                arc->staangle = reader->getDouble() / ARAD;
            else if (ellipse)
                ellipse->staparam = reader->getDouble() / ARAD;
            break;
        case 51:
            if (arc)
                arc->endangle = reader->getDouble() / ARAD;
            else if (ellipse)
                ellipse->endparam = reader->getDouble() / ARAD;
            break;
        case 52:
            angle = reader->getDouble();
            break;
        case 73:
            if (arc)
                arc->isccw = reader->getInt32();
            else if (pline)
                pline->flags = reader->getInt32();
            break;
        case 75:
            hstyle = reader->getInt32();
            break;
        case 76:
            hpattern = reader->getInt32();
            break;
        case 77:
            doubleflag = reader->getInt32();
            break;
        case 78:
            deflines = reader->getInt32();
            break;
        case 91:
            loopsnum = reader->getInt32();
            return DRW::reserve(looplist, loopsnum);
        case 92:
            loop = new DRW_HatchLoop(reader->getInt32());
            looplist.push_back(loop);
            if (reader->getInt32() & 2)
            {
                ispol = true;
                clearEntities();
                pline = new DRW_LWPolyline;
                loop->objlist.push_back(pline);
            }
            else
                ispol = false;
            break;
        case 93:
            if (pline)
                pline->vertexnum = reader->getInt32();
            else if (loop)
                loop->numedges = reader->getInt32(); // aqui reserve
            break;
        case 98: // seed points ??
            clearEntities();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

auto DRW_Spline::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 210:
            normalVec.x = reader->getDouble();
            break;
        case 220:
            normalVec.y = reader->getDouble();
            break;
        case 230:
            normalVec.z = reader->getDouble();
            break;
        case 12:
            tgStart.x = reader->getDouble();
            break;
        case 22:
            tgStart.y = reader->getDouble();
            break;
        case 32:
            tgStart.z = reader->getDouble();
            break;
        case 13:
            tgEnd.x = reader->getDouble();
            break;
        case 23:
            tgEnd.y = reader->getDouble();
            break;
        case 33:
            tgEnd.z = reader->getDouble();
            break;
        case 70:
            flags = reader->getInt32();
            break;
        case 71:
            degree = reader->getInt32();
            break;
        case 72:
            nknots = reader->getInt32();
            break;
        case 73:
            ncontrol = reader->getInt32();
            break;
        case 74:
            nfit = reader->getInt32();
            break;
        case 42:
            tolknot = reader->getDouble();
            break;
        case 43:
            tolcontrol = reader->getDouble();
            break;
        case 44:
            tolfit = reader->getDouble();
            break;
        case 10:
        {
            controlpoint = new DRW_Coord();
            controllist.push_back(controlpoint);
            controlpoint->x = reader->getDouble();
            break;
        }
        case 20:
            if (controlpoint != nullptr)
                controlpoint->y = reader->getDouble();
            break;
        case 30:
            if (controlpoint != nullptr)
                controlpoint->z = reader->getDouble();
            break;
        case 11:
        {
            fitpoint = new DRW_Coord();
            fitlist.push_back(fitpoint);
            fitpoint->x = reader->getDouble();
            break;
        }
        case 21:
            if (fitpoint != nullptr)
                fitpoint->y = reader->getDouble();
            break;
        case 31:
            if (fitpoint != nullptr)
                fitpoint->z = reader->getDouble();
            break;
        case 40:
            knotslist.push_back(reader->getDouble());
            break;
        case 41:
            weightlist.push_back(reader->getDouble());
            break;
        default:
            return DRW_Entity::parseCode(code, reader);
    }

    return true;
}

auto DRW_Image::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 12:
            vVector.x = reader->getDouble();
            break;
        case 22:
            vVector.y = reader->getDouble();
            break;
        case 32:
            vVector.z = reader->getDouble();
            break;
        case 13:
            sizeu = reader->getDouble();
            break;
        case 23:
            sizev = reader->getDouble();
            break;
        case 340:
            ref = static_cast<duint32>(reader->getHandleString());
            break;
        case 280:
            clip = reader->getInt32();
            break;
        case 281:
            brightness = reader->getInt32();
            break;
        case 282:
            contrast = reader->getInt32();
            break;
        case 283:
            fade = reader->getInt32();
            break;
        default:
            return DRW_Line::parseCode(code, reader);
    }

    return true;
}

auto DRW_Dimension::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 1:
            text = reader->getUtf8String();
            break;
        case 2:
            name = reader->getString();
            break;
        case 3:
            style = reader->getUtf8String();
            break;
        case 70:
            type = reader->getInt32();
            break;
        case 71:
            align = reader->getInt32();
            break;
        case 72:
            linesty = reader->getInt32();
            break;
        case 10:
            defPoint.x = reader->getDouble();
            break;
        case 20:
            defPoint.y = reader->getDouble();
            break;
        case 30:
            defPoint.z = reader->getDouble();
            break;
        case 11:
            textPoint.x = reader->getDouble();
            break;
        case 21:
            textPoint.y = reader->getDouble();
            break;
        case 31:
            textPoint.z = reader->getDouble();
            break;
        case 12:
            clonePoint.x = reader->getDouble();
            break;
        case 22:
            clonePoint.y = reader->getDouble();
            break;
        case 32:
            clonePoint.z = reader->getDouble();
            break;
        case 13:
            def1.x = reader->getDouble();
            break;
        case 23:
            def1.y = reader->getDouble();
            break;
        case 33:
            def1.z = reader->getDouble();
            break;
        case 14:
            def2.x = reader->getDouble();
            break;
        case 24:
            def2.y = reader->getDouble();
            break;
        case 34:
            def2.z = reader->getDouble();
            break;
        case 15:
            circlePoint.x = reader->getDouble();
            break;
        case 25:
            circlePoint.y = reader->getDouble();
            break;
        case 35:
            circlePoint.z = reader->getDouble();
            break;
        case 16:
            arcPoint.x = reader->getDouble();
            break;
        case 26:
            arcPoint.y = reader->getDouble();
            break;
        case 36:
            arcPoint.z = reader->getDouble();
            break;
        case 41:
            linefactor = reader->getDouble();
            break;
        case 42:
            actual = reader->getDouble();
            hasActual = not qFuzzyIsNull(actual);
            break;
        case 53:
            rot = reader->getDouble();
            break;
        case 50:
            angle = reader->getDouble();
            break;
        case 52:
            oblique = reader->getDouble();
            break;
        case 40:
            length = reader->getDouble();
            break;
        case 51:
            hdir = reader->getDouble();
            break;
        case 210:
            extPoint.x = reader->getDouble();
            break;
        case 220:
            extPoint.y = reader->getDouble();
            break;
        case 230:
            extPoint.z = reader->getDouble();
            break;
        default:
            return DRW_Entity::parseCode(code, reader);
    }

    return true;
}

auto DRW_Leader::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 3:
            style = reader->getUtf8String();
            break;
        case 71:
            arrow = reader->getInt32();
            break;
        case 72:
            leadertype = reader->getInt32();
            break;
        case 73:
            flag = reader->getInt32();
            break;
        case 74:
            hookline = reader->getInt32();
            break;
        case 75:
            hookflag = reader->getInt32();
            break;
        case 76:
            vertnum = reader->getInt32();
            break;
        case 77:
            coloruse = reader->getInt32();
            break;
        case 40:
            textheight = reader->getDouble();
            break;
        case 41:
            textwidth = reader->getDouble();
            break;
        case 10:
        {
            vertexpoint = new DRW_Coord();
            vertexlist.push_back(vertexpoint);
            vertexpoint->x = reader->getDouble();
            break;
        }
        case 20:
            if (vertexpoint != nullptr)
                vertexpoint->y = reader->getDouble();
            break;
        case 30:
            if (vertexpoint != nullptr)
                vertexpoint->z = reader->getDouble();
            break;
        case 340:
            annotHandle = static_cast<duint32>(reader->getHandleString());
            break;
        case 210:
            extrusionPoint.x = reader->getDouble();
            break;
        case 220:
            extrusionPoint.y = reader->getDouble();
            break;
        case 230:
            extrusionPoint.z = reader->getDouble();
            break;
        case 211:
            horizdir.x = reader->getDouble();
            break;
        case 221:
            horizdir.y = reader->getDouble();
            break;
        case 231:
            horizdir.z = reader->getDouble();
            break;
        case 212:
            offsetblock.x = reader->getDouble();
            break;
        case 222:
            offsetblock.y = reader->getDouble();
            break;
        case 232:
            offsetblock.z = reader->getDouble();
            break;
        case 213:
            offsettext.x = reader->getDouble();
            break;
        case 223:
            offsettext.y = reader->getDouble();
            break;
        case 233:
            offsettext.z = reader->getDouble();
            break;
        default:
            return DRW_Entity::parseCode(code, reader);
    }

    return true;
}

auto DRW_Viewport::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 40:
            pswidth = reader->getDouble();
            break;
        case 41:
            psheight = reader->getDouble();
            break;
        case 68:
            vpstatus = reader->getInt32();
            break;
        case 69:
            vpID = reader->getInt32();
            break;
        case 12:
            centerPX = reader->getDouble();
            break;
        case 22:
            centerPY = reader->getDouble();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }

    return true;
}

auto DRW_ATTDEF::parseCode(int code, const std::unique_ptr<dxfReader> &reader) -> bool
{
    switch (code)
    {
        case 40:
            height = reader->getDouble();
            break;
        case 1:
            text = reader->getString();
            break;
        case 2:
            name = reader->getString();
            break;
        case 3:
            promptString = reader->getString();
            break;
        case 70:
            flags = reader->getInt32();
            break;
        case 73:
            horizontalAdjustment = reader->getInt32();
            break;
        default:
            return DRW_Point::parseCode(code, reader);
    }
    return true;
}
