// Similar to cueTo(), but throws error if cmd not found
Foam::string Foam::fileFormats::AC3DsurfaceFormatCore::cueToOrDie
(
    IFstream& is,
    const string& cmd,
    const string& errorMsg
)
{
    string args;
    if (!cueTo(is, cmd, args))
    {
        FatalErrorInFunction
            << "Cannot find command " << cmd
            << " " << errorMsg
            << exit(FatalError);
    }

    return args;
}
bool Foam::fileFormats::AC3DsurfaceFormat<Face>::read
(
    const fileName& filename
)
{
    const bool mustTriangulate = this->isTri();
    this->clear();

    IFstream is(filename);
    if (!is.good())
    {
        FatalErrorIn
        (
            "fileFormats::AC3DsurfaceFormat::read(const fileName&)"
        )
                << "Cannot read file " << filename
                << exit(FatalError);
    }

    string line, cmd, args;

    is.getLine(line);

    string version = line.substr(4);

    if (version != "b")
    {
        WarningIn
        (
            "fileFormats::AC3DsurfaceFormat::read(const fileName&)"
        )
                << "When reading AC3D file " << filename
                << " read header " << line << " with version "
                << version << endl
                << "Only tested reading with version 'b'."
                << " This might give problems" << endl;
    }


    if (!cueTo(is, "OBJECT", args) || (args != "world"))
    {
        FatalErrorIn
        (
            "fileFormats::AC3DsurfaceFormat::read(const fileName&)"
        )
                << "Cannot find \"OBJECT world\" in file " << filename
                << exit(FatalError);
    }

    // # of kids is the # of zones
    args = cueToOrDie(is, "kids");
    label nZones = parse<int>(args);

    // Start of vertices for object/zones
    label vertexOffset = 0;

    DynamicList<point> dynPoints;
    DynamicList<Face>  dynFaces;
    List<word>         names(nZones);
    List<label>        sizes(nZones, 0);

    for (label zoneI = 0; zoneI < nZones; ++zoneI)
    {
        names[zoneI] = word("zone") + Foam::name(zoneI);

        args = cueToOrDie(is, "OBJECT", "while reading " + names[zoneI]);

        // number of vertices for this zone
        label  nZonePoints = 0;
        vector location(pTraits<vector>::zero);
        // tensor rotation(I);

        // Read all info for current zone
        while (is.good())
        {
            // Read line and get first word. If end of file break since
            // zone should always end with 'kids' command ?not sure.
            if (!readCmd(is, cmd, args))
            {
                FatalErrorIn
                (
                    "fileFormats::AC3DsurfaceFormat::read(const fileName&)"
                )
                        << "Did not read up to \"kids 0\" while reading zone "
                        << zoneI << " from file " << filename
                        << exit(FatalError);
            }

            if (cmd == "name")
            {
                // name %s
                string str = parse<string>(args);
                string::stripInvalid<word>(str);

                names[zoneI] = str;
            }
            else if (cmd == "rot")
            {
                // rot  %f %f %f  %f %f %f  %f %f %f

                // IStringStream lineStream(args);
                //
                // lineStream
                //     >> rotation.xx() >> rotation.xy() >> rotation.xz()
                //     >> rotation.yx() >> rotation.yy() >> rotation.yz()
                //     >> rotation.zx() >> rotation.zy() >> rotation.zz();

                WarningIn
                (
                    "fileFormats::AC3DsurfaceFormat::read"
                    "(const fileName&)"
                )
                        << "rot (rotation tensor) command not implemented"
                        << "Line:" << cmd << ' ' << args << endl
                        << "while reading zone " << zoneI << endl;
            }
            else if (cmd == "loc")
            {
                // loc  %f %f %f
                IStringStream lineStream(args);

                lineStream
                        >> location.x()
                        >> location.y()
                        >> location.z();
            }
            else if (cmd == "numvert")
            {
                // numvert  %d
                nZonePoints = parse<int>(args);

                for (label vertI = 0; vertI < nZonePoints; ++vertI)
                {
                    is.getLine(line);
                    IStringStream lineStream(line);

                    point pt;
                    lineStream
                            >> pt.x() >> pt.y() >> pt.z();

                    // Offset with current translation vector
                    dynPoints.append(location + pt);
                }
            }