/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  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

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

#include "blockMesh.H"
#include "Time.H"
#include "preservePatchTypes.H"
#include "emptyPolyPatch.H"

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

bool Foam::blockMesh::blockLabelsOK
(
    const label blockLabel,
    const pointField& points,
    const cellShape& blockShape
)
{
    bool ok = true;

    for (int i=0; i<blockShape.size(); i++)
    {
        if (blockShape[i] < 0)
        {
            ok = false;

            WarningIn
            (
                "bool Foam::blockMesh::blockLabelsOK"
                "(const label blockLabel, const pointField& points, "
                "const cellShape& blockShape)"
            )   << "block " << blockLabel
                << " point label " << blockShape[i]
                << " less than zero" << endl;
        }
        else if (blockShape[i] >= points.size())
        {
            ok = false;

            WarningIn
            (
                "bool Foam::blockMesh::blockLabelsOK"
                "(const label blockLabel, const pointField& points, "
                "const cellShape& blockShape)"
            )   << "block " << blockLabel
                << " point label " << blockShape[i]
                << " larger than " << points.size() - 1
                << " the largest defined point label" << endl;
        }
    }

    return ok;
}


bool Foam::blockMesh::patchLabelsOK
(
    const label patchLabel,
    const pointField& points,
    const faceList& patchFaces
)
{
    bool ok = true;

    for (label facei=0; facei<patchFaces.size(); facei++)
    {
        const labelList& labels = patchFaces[facei];

        for (int i=0; i<labels.size(); i++)
        {
            if (labels[i] < 0)
            {
                ok = false;

                WarningIn
                (
                    "bool Foam::blockMesh::patchLabelsOK"
                    "(const label patchLabel, const pointField& points, "
                    "const faceList& patchFaces)"
                )   << "patch " << patchLabel
                    << " face " << facei
                    << " point label " << labels[i]
                    << " less than zero" << endl;
            }
            else if (labels[i] >= points.size())
            {
                ok = false;

                WarningIn
                (
                    "bool Foam::blockMesh::patchLabelsOK"
                    "(const label patchLabel, const pointField& points, "
                    "const faceList& patchFaces)"
                )   << "patch " << patchLabel
                    << " face " << facei
                    << " point label " << labels[i]
                    << " larger than " << points.size() - 1
                    << " the largest defined point label" << endl;
            }
        }
    }

    return ok;
}


// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //

Foam::polyMesh* Foam::blockMesh::createTopology(IOdictionary& meshDescription)
{
    bool topologyOK = true;

    blockMesh& blocks = *this;

    Info<< nl << "Creating blockCorners" << endl;

    // create blockCorners
    pointField tmpBlockPoints(meshDescription.lookup("vertices"));


    if (meshDescription.found("edges"))
    {
        // read number of non-linear edges in mesh
        Info<< nl << "Creating curved edges" << endl;

        ITstream& edgesStream(meshDescription.lookup("edges"));

        label nEdges = 0;

        token firstToken(edgesStream);

        if (firstToken.isLabel())
        {
            nEdges = firstToken.labelToken();
            edges_.setSize(nEdges);
        }
        else
        {
            edgesStream.putBack(firstToken);
        }

        // Read beginning of edges
        edgesStream.readBegin("edges");

        nEdges = 0;

        token lastToken(edgesStream);
        while
        (
            !(
                lastToken.isPunctuation()
                && lastToken.pToken() == token::END_LIST
            )
        )
        {
            if (edges_.size() <= nEdges)
            {
                edges_.setSize(nEdges + 1);
            }

            edgesStream.putBack(lastToken);

            edges_.hook
            (
                curvedEdge::New(tmpBlockPoints, edgesStream)
            );
            
            nEdges++;

            edgesStream >> lastToken;
        }
        edgesStream.putBack(lastToken);

        // Read end of edges
        edgesStream.readEnd("edges");
    }
    else
    {
        Info<< nl << "There are no non-linear edges" << endl;
    }


    Info<< nl << "Creating blocks" << endl;
    {
        ITstream& blockDescriptorStream(meshDescription.lookup("blocks"));

        // read number of blocks in mesh
        label nBlocks = 0;

        token firstToken(blockDescriptorStream);
        
        if (firstToken.isLabel())
        {
            nBlocks = firstToken.labelToken();
            blocks.setSize(nBlocks);
        }
        else
        {
            blockDescriptorStream.putBack(firstToken);
        }

        // Read beginning of blocks
        blockDescriptorStream.readBegin("blocks");

        nBlocks = 0;

        token lastToken(blockDescriptorStream);
        while
        (
            !(
                lastToken.isPunctuation()
                && lastToken.pToken() == token::END_LIST
            )
        )
        {
            if (blocks.size() <= nBlocks)
            {
                blocks.setSize(nBlocks + 1);
            }

            blockDescriptorStream.putBack(lastToken);

            blocks.hook
            (
                new block
                (
                    blockDescriptor
                    (
                        tmpBlockPoints,
                        edges_,
                        blockDescriptorStream
                    )
                )
            );

            topologyOK = topologyOK && blockLabelsOK
            (
                nBlocks,
                tmpBlockPoints,
                blocks[nBlocks].blockDef().blockShape()
            );

            nBlocks++;

            blockDescriptorStream >> lastToken;
        }
        blockDescriptorStream.putBack(lastToken);

        // Read end of blocks
        blockDescriptorStream.readEnd("blocks");
    }


    Info<< nl << "Creating patches" << endl;

    faceListList tmpBlocksPatches;
    wordList patchNames;
    wordList patchTypes;

    {
        ITstream& patchStream(meshDescription.lookup("patches"));

        // read number of patches in mesh
        label nPatches = 0;

        token firstToken(patchStream);

        if (firstToken.isLabel())
        {
            nPatches = firstToken.labelToken();

            tmpBlocksPatches.setSize(nPatches);
            patchNames.setSize(nPatches);
            patchTypes.setSize(nPatches);
        }
        else
        {
            patchStream.putBack(firstToken);
        }

        // Read beginning of blocks
        patchStream.readBegin("patches");
        
        nPatches = 0;

        token lastToken(patchStream);
        while
        (
            !(
                lastToken.isPunctuation()
                && lastToken.pToken() == token::END_LIST
            )
        )
        {
            if (tmpBlocksPatches.size() <= nPatches)
            {
                tmpBlocksPatches.setSize(nPatches + 1);
                patchNames.setSize(nPatches + 1);
                patchTypes.setSize(nPatches + 1);
            }

            patchStream.putBack(lastToken);

            patchStream
                >> patchTypes[nPatches]
                >> patchNames[nPatches]
                >> tmpBlocksPatches[nPatches];

            topologyOK = topologyOK && patchLabelsOK
            (
                nPatches,
                tmpBlockPoints,
                tmpBlocksPatches[nPatches]
            );

            nPatches++;

            patchStream >> lastToken;
        }
        patchStream.putBack(lastToken);

        // Read end of blocks
        patchStream.readEnd("patches");
    }


    if (!topologyOK)
    {
        FatalErrorIn("blockMesh::createTopology(IOdictionary& meshDescription)")
            << "Cannot create mesh due to errors in topology, exiting !" << nl
            << exit(FatalError);
    }


    Info<< nl << "Creating block mesh topology" << endl;

    PtrList<cellShape> tmpBlockCells(blocks.size());
    forAll(blocks, blockLabel)
    {
        tmpBlockCells.hook
        (
            new cellShape(blocks[blockLabel].blockDef().blockShape())
        );

        if (tmpBlockCells[blockLabel].mag(tmpBlockPoints) < 0.0)
        {
            WarningIn
            (
                "blockMesh::createTopology(IOdictionary& meshDescription)"
            )   << "negative volume block : " << blockLabel
                << ", probably defined inside-out" << endl;
        }
    }

    word defaultFacesType = emptyPolyPatch::typeName;
    wordList patchPhysicalTypes(tmpBlocksPatches.size());

    preservePatchTypes
    (
        meshDescription.time(),
        meshDescription.time().constant(),
        polyMesh::meshSubDir,
        patchNames,
        patchTypes,
        defaultFacesType,
        patchPhysicalTypes
    );

    polyMesh* blockMeshPtr = new polyMesh
    (
        IOobject
        (
            "blockMesh",
            meshDescription.time().constant(),
            meshDescription.time(),
            IOobject::NO_READ,
            IOobject::NO_WRITE,
            false
        ),
        tmpBlockPoints,
        tmpBlockCells,
        tmpBlocksPatches,
        patchNames,
        patchTypes,
        defaultFacesType,
        patchPhysicalTypes
    );

    checkBlockMesh(*blockMeshPtr);

    return blockMeshPtr;
}


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

