/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | Copyright (C) 1991-2005 OpenCFD Ltd.
     \\/     M anipulation  |
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM is free software; you can redistribute it and/or modify it
    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.

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with OpenFOAM; if not, write to the Free Software Foundation,
    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Description
    Plot3d mesh (ascii format) converter.

    Work in progress! Handles ascii multiblock (and optionally singleBlock)
    format.
    By default expects blanking. Use -noBlank if none.
    Niklas Nordin has experienced a problem with lefthandedness of the blocks.
    The code should detect this automatically - see hexBlock::readPoints but
    if this goes wrong just set the blockHandedness_ variable to 'right'
    always.

\*---------------------------------------------------------------------------*/

#include "argList.H"
#include "Time.H"
#include "IFstream.H"
#include "hexBlock.H"
#include "polyMesh.H"
#include "wallPolyPatch.H"
#include "symmetryPolyPatch.H"
#include "preservePatchTypes.H"
#include "cellShape.H"
#include "cellModeller.H"
#include "mergePoints.H"

using namespace Foam;

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:

int main(int argc, char *argv[])
{
    argList::noParallel();
    argList::validArgs.append("PLOT3D geom file");
    argList::validOptions.insert("scale", "scale factor");
    argList::validOptions.insert("noBlank", "");
    argList::validOptions.insert("singleBlock", "");

    argList args(argc, argv);

    if (!args.check())
    {
         FatalError.exit();
    }

    scalar scaleFactor = 1.0;
    if (args.options().found("scale"))
    {
        scaleFactor = atof(args.options()["scale"].c_str());
    }

    bool readBlank = !args.options().found("noBlank");
    bool singleBlock = args.options().found("singleBlock");


#   include "createTime.H"

    IFstream plot3dFile(args.args()[3]);

    // Read the plot3d information using a fixed format reader.
    // Comments in the file are in C++ style, so the stream parser will remove
    // them with no intervention
    label nblock;

    if (singleBlock)
    {
        nblock = 1;
    }
    else
    {
        plot3dFile >> nblock;
    }

    Info << "Reading " << nblock << " blocks" << endl;

    PtrList<hexBlock> blocks(nblock);

    {
        label nx, ny, nz;

        forAll (blocks, blockI)
        {
            plot3dFile >> nx >> ny >> nz;

            blocks.hook(new hexBlock(nx, ny, nz));
        }
    }

    Info << "Reading block points" << endl;
    label sumPoints(0);
    label nMeshCells(0);

    forAll (blocks, blockI)
    {
        Info << "block " << blockI << ":" << nl;
        blocks[blockI].readPoints(readBlank, plot3dFile);
        sumPoints += blocks[blockI].nBlockPoints();
        nMeshCells += blocks[blockI].nBlockCells();
        Info<< nl;
    }

    pointField points(sumPoints);
    labelList blockOffsets(blocks.size());
    sumPoints = 0;
    forAll (blocks, blockI)
    {
        const pointField& blockPoints = blocks[blockI].points();
        blockOffsets[blockI] = sumPoints;
        forAll (blockPoints, i)
        {
            points[sumPoints++] = blockPoints[i];
        }
    }


    // From old to new master point
    labelList oldToNew;
    pointField newPoints;

    // Merge points
    mergePoints
    (
        points,
        SMALL,
        false,
        oldToNew,
        newPoints
    );

    // Scale the points
    if (scaleFactor > 1.0 + SMALL || scaleFactor < 1.0 - SMALL)
    {
        newPoints *= scaleFactor;
    }

    Info << "Creating cells" << endl;

    cellShapeList cellShapes(nMeshCells);

    const cellModel& hex = *(cellModeller::lookup("hex"));

    label nCreatedCells = 0;

    forAll (blocks, blockI)
    {
        labelListList curBlockCells = blocks[blockI].blockCells();

        forAll (curBlockCells, blockCellI)
        {
            labelList cellPoints(curBlockCells[blockCellI].size());

            forAll (cellPoints, pointI)
            {
                cellPoints[pointI] =
                    oldToNew
                    [
                        curBlockCells[blockCellI][pointI]
                      + blockOffsets[blockI]
                    ];
            }

            // Do automatic collapse from hex.
            cellShapes[nCreatedCells] = cellShape(hex, cellPoints, true);

            nCreatedCells++;
        }
    }

    Info << "Creating boundary patches" << endl;

    faceListList boundary(0);
    wordList patchNames(0);
    wordList patchTypes(0);
    word defaultFacesType = wallPolyPatch::typeName;
    wordList patchPhysicalTypes(0);

    polyMesh pShapeMesh
    (
        IOobject
        (
            polyMesh::defaultRegion,
            runTime.constant(),
            runTime
        ),
        newPoints,
        cellShapes,
        boundary,
        patchNames,
        patchTypes,
        defaultFacesType,
        patchPhysicalTypes
    );

    Info << "Checking mesh" << endl;
    pShapeMesh.checkMesh();

    // Set the precision of the points data to 10
    IOstream::defaultPrecision(10);

    Info << "Writing polyMesh" << endl;
    pShapeMesh.write();

    Info << "End\n" << endl;

    return 0;
}


// ************************************************************************* //
