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

// Foam header files.
#include "long.H"
#include "dictionary.H"
#include "wordList.H"
#include "stringList.H"
#include "fileNameList.H"
#include "IFstream.H"
#include "OStringStream.H"
#include "instantList.H"
#include "IOPtrList.H"
#include "OSspecific.H"

// FoamX header files.
#include "FoamXErrors.H"
#include "ICaseServerImpl.H"
#include "DictionaryWriter.H"
#include "RootDictionary.H"
#include "IGeometricFieldImpl.H"
#include "LogEntry.H"
#include "NameServer.H"
#include "PatchProperties.H"
#include "Orb.H"
#include "Paths.H"

// Namespaces
#include "FoamXNameSpaces.H"

// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //

// Define word for IOPtrList<entry>.
template<>
const Foam::word Foam::IOPtrList<Foam::entry>::typeName("polyBoundaryMesh");


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

Foam::Time& FoamX::ICaseServerImpl::time()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::time()";

    if (!dbPtr_)
    {
        throw FoamXError
        (
            E_FAIL,
            "database not yet constructed",
            functionName,
            __FILE__, __LINE__
        );
    }
    return *dbPtr_;
}


void FoamX::ICaseServerImpl::saveControlDict()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::saveControlDict()";

    LogEntry log(functionName, __FILE__, __LINE__);

    //----------------------------------------------------------------------
    //  Make sure the control dictionary is saved.
    IDictionaryEntry_var controlDict;      // Auto Release.
    getDictionary(Time::controlDictName.c_str(), false, controlDict.out());
    if (CORBA::is_nil(controlDict))
    {
        throw FoamXError
        (
            E_FAIL,
            "Failed to open control dictionary entry object",
            functionName,
            __FILE__, __LINE__
        );
    }

    // Make sure the control dictionary is saved.
    controlDict->save();
}


void FoamX::ICaseServerImpl::writePatchData()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::writePatchData()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Create the polyMesh directory if required.
        fileName polyMeshDir = rootDir_/caseName_/"constant"/"polyMesh";
        if (!dir(polyMeshDir) && !mkDir(polyMeshDir))
        {
            throw FoamXError
            (
                E_FAIL,
                "Mesh directory '" +  polyMeshDir + "' could not be created",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Write boundary file (fixed form).
        DictionaryWriter dict
        (
            rootDir_,
            caseName_,
            "constant/polyMesh",
            "boundary"
        );
        dict.writeHeader
        (
            "FoamX Mesh Description File",
            "polyBoundaryMesh"
        );

        // Start list.
        dict.startList(patchMap_.size());

        // Write patch boundary information.
        for
        (
            Dictionary<PatchProperties>::iterator iter = patchMap_.begin();
            iter != patchMap_.end();
            ++iter
        )
        {
            dict.writeEndl();
            iter().save(dict);
        }

        // End list.
        dict.endList();

        dict.writeEndl();
        dict.writeEndBar();
    }
    CATCH_ALL(functionName);
}


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// patchPhysicalType is the name of the boundary type name, not the definition key.

void FoamX::ICaseServerImpl::addPatch
(
    const char* patchName,
    const char* patchPhysicalType
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::addPatch"
        "(const char* patchName, const char* patchPhysicalType)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        log << "Patch = " << patchName
            << ", Boundary (Physical) Type = " << patchPhysicalType << endl;

        // Create a new patch descriptor object for this patch.
        PatchProperties* patchProps = new PatchProperties(patchName);

        if (patchProps == NULL)
        {
            throw FoamXError
            (
                E_FAIL,
                "Failed to create Patch Properties object",
                functionName,
                __FILE__, __LINE__
            );
        }

        IPatchPhysicalTypeDescriptor_var boundaryDescriptor;
        app_->getPatchPhysicalType(patchPhysicalType, boundaryDescriptor.out());
        patchProps->patchType(boundaryDescriptor->patchType());

        patchProps->physicalType(patchPhysicalType);

        // Append to patch descriptor list.
        addPatch(patchProps);
    }
    CATCH_ALL(functionName);
}

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

bool FoamX::ICaseServerImpl::fieldsMatchPatchPhysicalType(const word& patchName)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::fieldsMatchPatchPhysicalType"
        "(const char* patchName)";

    // Get properties read from boundary file.
    PatchProperties* patchProps = patchMap_.lookup(patchName);
    
    Info<< "Trying to match patch " << patchName << " physical type "
        << patchProps->physicalType() << endl;

    // Get the specified boundary type descriptor object (ultimately from the
    // application class.
    IPatchPhysicalTypeDescriptor_var boundaryDescriptor;
    app_->getPatchPhysicalType
    (
        patchProps->physicalType().c_str(), 
        boundaryDescriptor.out()
    );

    // Get the types for individual fields
    StringPairList_var patchFieldTypes =
        boundaryDescriptor->patchFieldTypes();

    // Try to see if all fields specified have the correct type
    for (unsigned int i = 0; i <patchFieldTypes->length(); i++)
    {
        word fieldName(patchFieldTypes[i].name);

        Info<< "    name:" << fieldName
            << "  type:" << patchFieldTypes[i].value << endl;


        if (fieldValueMap_.found(fieldName))
        {
            // Convert between the patch field type key and the Foam name.

            // Get the PatchFieldDescriptor object.
            ITypeDescriptor_var pfDescriptor;
            foamProperties_->getPatchFieldType
            (
                patchFieldTypes[i].value,
                pfDescriptor.out()
            );
            CORBA::String_var pfName = pfDescriptor->name();

            IGeometricFieldImpl* pFieldValue = fieldValueMap_[fieldName];

            IDictionaryEntry_var pfParams;
            pFieldValue->getPatchFieldParameters(patchName.c_str(), pfParams);

            DictionaryEntryList* subElemsPtr = pfParams->subElements();

            for
            (
                unsigned int elemI = 0;
                elemI < subElemsPtr->length();
                elemI++
            )
            {
                FoamXServer::FoamXAny* subValPtr =
                    (*subElemsPtr)[elemI]->value();

                void* nasty = &(subValPtr->value);

                Info<< "    " << elemI << "type:" << subValPtr->type
                    << "  value:" << long(nasty) << endl;
            }
        }
        else
        {
            WarningIn(functionName)
                << "Did not find field " << fieldName
                << " specified in physical patch type "
                << patchProps->physicalType()
                << endl;
        }
    }
    return true;
}

// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //

FoamX::ICaseServerImpl::ICaseServerImpl
(
    Orb& orb,
    const fileName& rootDir,
    const fileName& caseName,
    const word& mode,
    const word& app
)
:
    rootDir_(rootDir),
    caseName_(caseName),
    hostContext_(hostName()),
    userContext_(userName()),
    objectName_(hostContext_/userContext_/string::validate<word>(rootDir_)/caseName_),
    caseDictName_(rootDir_/caseName_/"system/controlDict"),
    appName_(app),
    orb_(orb),
    dbPtr_(NULL),
    caseBrowser_(NULL),
    foamProperties_(NULL),
    app_(NULL),
    managed_(false),
    procControl_(dotFoam("apps/FoamX/FoamX.cfg")),
    pid_(-1)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::ICaseServerImpl"
        "(Orb& orb, const fileName& rootDir, const fileName& caseName, "
        "const word& mode, const word& app)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Connect to name server.
        NameServer fxNameServer(orb_.orbPtr_);

        // Case browser should have registered itself with the name server under
        // a "hostName/"FoamXCaseBrowser"" key.
        fileName caseBrowserKey = hostName()/"FoamXCaseBrowser";

        if (fxNameServer.isObjectBound(caseBrowserKey))
        {
            // Return server reference.
            caseBrowser_ = 
                fxNameServer.resolve<FoamXServer::CaseBrowser::ICaseBrowser>
                (caseBrowserKey);
        }
        else
        {
            throw FoamXError
            (
                E_FAIL,
                "Server '" + caseBrowserKey + "' not found on name server. "
                "getServerReference call timed out",
                functionName,
                __FILE__, __LINE__
            );
        }

        foamProperties_ = caseBrowser_->foamProperties();


        //----------------------------------------------------------------------
        // See if we are opening an existing case or creating/importing
        // a new case.
        if (mode == "open")
        {
            // Control dictionary must exist.
            if (!exists(caseDictName_))
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Case control dictionary '" + caseDictName_ + "' not found",
                    functionName,
                    __FILE__, __LINE__
                );
            }

            // Open the case configuration dictionary and get the
            // application class name.
            dictionary caseDict((IFstream(caseDictName_)()));

            if (caseDict.found("application"))
            {
                caseDict.lookup("application")>> appName_;
            }
            else if (caseDict.found("applicationClass"))
            {
                caseDict.lookup("applicationClass")>> appName_;
            }
            else
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Application class entry not found in '" + caseDictName_
                  + "'",
                    functionName,
                    __FILE__, __LINE__
                );
            }
        }
        else if (mode == "import")
        {
            // Control dictionary must exist.
            if (!exists(caseDictName_))
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Case control dictionary '" + caseDictName_
                  + "' not found",
                    functionName,
                    __FILE__, __LINE__
               );
            }
        
            // Open the case configuration dictionary and get the
            // application class name.
            dictionary caseDict((IFstream(caseDictName_)()));
        
            // If not found, use the command line argument.
            if (caseDict.found("application"))
            {
                caseDict.lookup("application") >> appName_;
            }
        }
        else if (mode == "create")
        {
            // Control dictionary must not exist.
            if (exists(caseDictName_))
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Case control dictionary '" + caseDictName_
                  + "' exists. Cannot create new case",
                    functionName,
                    __FILE__, __LINE__
                );
            }
            // Create the case directory if required.
            fileName caseDir = rootDir_/caseName_;
            if (!dir(caseDir) && !mkDir(caseDir))
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Case directory '" +  caseDir + "' could not be created",
                    functionName,
                    __FILE__, __LINE__
                );
            }

            // Create the constant directory if required.
            fileName constDir = caseDir/"constant";
            if (!dir(constDir ) && !mkDir(constDir ) )
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Case directory '" +  constDir + "' could not be created",
                    functionName,
                    __FILE__, __LINE__
                );
            }

            // Create the polyMesh directory if required.
            fileName meshDir = constDir/"polyMesh";
            if (!dir(meshDir) && !mkDir(meshDir))
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Case directory '" +  meshDir + "' could not be created",
                    functionName,
                    __FILE__, __LINE__ 
                );
            }
        }
        else
        {
            throw FoamXError
            (
                E_FAIL,
                "Invalid argument '" +  mode + "'",
                functionName,
                __FILE__, __LINE__
            );
        }

        //----------------------------------------------------------------------
        // Create the Application and FoamProperties objects.
        if (appName_.size() == 0)
        {
            throw FoamXError
            (
                E_FAIL,
                "Invalid application class name '" + appName_ + "'",
                functionName,
                __FILE__, __LINE__
            );
        }

        foamProperties_->getApplication(appName_.c_str(), app_);

        //----------------------------------------------------------------------
        // Create the FieldValues objects.
        StringList_var fieldNames = app_->fields();

        for (unsigned int i = 0; i <fieldNames->length(); i++)
        {
            word fieldName(fieldNames[i]);

            // Get reference to field descriptor.
            IGeometricFieldDescriptor_var fieldDescriptorRef; // Auto-release.
            app_->getField(fieldName.c_str(), fieldDescriptorRef.out());

            // Create default FieldValues object for this field.
            IGeometricFieldImpl* pFieldValues = new IGeometricFieldImpl
            (
                fieldDescriptorRef,
                foamProperties_
            );

            if (pFieldValues == NULL)
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Couldn't create field value object",
                    functionName,
                    __FILE__, __LINE__
                );
            }
            fieldValueMap_.insert(fieldName, pFieldValues);
        }

        //----------------------------------------------------------------------
        // Register object with the naming service. Will throw an exception
        // if any errors occurs.

        // Do not fail if context already exists.
        fxNameServer.createContext(hostContext_, false);

        // Do not fail if context already exists.
        fxNameServer.createContext(hostContext_/userContext_, false);

        // Do not fail if context already exists.
        fxNameServer.createContext
        (
            hostContext_/userContext_/string::validate<word>(rootDir_),
            false
        );

        // Bind object under key hostName/userName/caseroot/casename.
        FoamXServer::CaseServer::ICaseServer_var caseRef = _this();

        // Throw exception if already bound.
        fxNameServer.bindObject(objectName_, caseRef, false);

        // Decrement the reference count of the object implementation, so
        // that it will be properly cleaned up when the POA has determined
        // that it is no longer needed.
        _remove_ref();

        if (mode == "import" || mode == "create")
        {
            // Make valid control dict so we can instantiate a database
            saveControlDict();

            dbPtr_ = new Time(Time::controlDictName, rootDir_, caseName_);

            // Make valid set of files
            save();

            caseBrowser_->addCase
            (
                rootDir_.c_str(),
                rootDir_.c_str(),
                caseName_.c_str(),
                appName_.c_str()
            );
        }
        else
        {
            dbPtr_ = new Time(Time::controlDictName, rootDir_, caseName_);

            caseBrowser_->caseOpen
            (
                rootDir_.c_str(),
                caseName_.c_str()
            );
        }
    }
    CATCH_ALL(functionName);
}


// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //

FoamX::ICaseServerImpl::~ICaseServerImpl()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::~ICaseServerImpl()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Delete database
    if (dbPtr_)
    {
        delete dbPtr_;
        dbPtr_ = NULL;
    }
}


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

CORBA::Boolean FoamX::ICaseServerImpl::managed()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::managed()";

    LogEntry log(functionName, __FILE__, __LINE__);

    return managed_;
}

void FoamX::ICaseServerImpl::managed(CORBA::Boolean managed)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::managed(CORBA::Boolean managed)";

    LogEntry log(functionName, __FILE__, __LINE__);

    managed_ = managed;
}

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

char* FoamX::ICaseServerImpl::caseRoot()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::caseRoot()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Duplicate and return.
    return CORBA::string_dup(rootDir_.c_str());
}

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

char* FoamX::ICaseServerImpl::caseName()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::caseName()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Duplicate and return.
    return CORBA::string_dup(caseName_.c_str());
}

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

FoamX::IFoamProperties_ptr FoamX::ICaseServerImpl::foamProperties()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::foamProperties()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Return a reference to the TypeDescriptor object.
    IFoamProperties_ptr pProperties = IFoamProperties::_nil();

    if (!CORBA::is_nil(foamProperties_))
    {
        pProperties = IFoamProperties::_duplicate(foamProperties_);
    }

    return pProperties;
}

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

FoamX::IApplication_ptr FoamX::ICaseServerImpl::application()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::application()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Return a reference to the Application object.
    IApplication_ptr pAppClass = IApplication::_nil();

    if (static_cast<FoamXServer::CaseServer::IApplication*>(app_) != NULL)
    {
        pAppClass = IApplication::_duplicate(app_);
    }

    return pAppClass;
}

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

FoamXServer::StringList* FoamX::ICaseServerImpl::availableTimeSteps()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::availableTimeSteps()";

    LogEntry log(functionName, __FILE__, __LINE__);

    StringList* timeStepList = NULL;

    try
    {
        // Get list of time directories.
        instantList times = time().times();

        // Copy time steps into a new string list and return.
        timeStepList = new StringList();
        timeStepList->length(times.size());
        forAll(times, i)
        {
            (*timeStepList)[i] = times[i].name().c_str();
        }
    }
    CATCH_ALL(functionName);

    return timeStepList;
}


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

char* FoamX::ICaseServerImpl::getTime()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::getTime()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Duplicate and return.
        return CORBA::string_dup(time().timeName().c_str());
    }
    CATCH_ALL(functionName);

    return CORBA::string_dup("");
}


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

void FoamX::ICaseServerImpl::setTime
(
    const char* timeName,
    const CORBA::Long timeIndex
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::setTime(const char*, const CORBA::Long)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        scalar timeValue(readScalar(IStringStream(timeName)()));

        time().setTime(timeValue, timeIndex);
    }
    CATCH_ALL(functionName);
}


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

CORBA::Boolean FoamX::ICaseServerImpl::meshDefined()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::meshDefined()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Return true if a mesh description dictionary
    fileName meshDict =
        rootDir_/caseName_/fileName("constant/polyMesh/boundary");

    return exists(meshDict);
}

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

void FoamX::ICaseServerImpl::readMesh()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::readMesh()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // See if the mesh has been defined.
        if (!meshDefined())
        {
            throw FoamXError
            (
                E_FAIL,
                "Mesh description dictionary not found",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Create a list of entries from the boundary file.
        IOPtrList<entry> patchEntries
        (
            IOobject
            (
                "boundary",
                time().constant(),
                "polyMesh",
                time(),
                IOobject::MUST_READ,
                IOobject::NO_WRITE,
                false
            )
        );

        log << "Deleting all patches" << endl;

        // Delete all previous patch information.
        deleteAllPatches();

        log << "Adding new patches.." << endl;

        // Add patch objects for each patch defined in the mesh.
        forAll(patchEntries, i)
        {
            // Read boundary type for this patch from the
            // mesh description dictionary.
            word patchName(patchEntries[i].keyword());
            log << "Adding patch " << patchName << ".." << endl;

            // Create a new patch descriptor object for this patch
            // and load patch data from the dictionary.
            PatchProperties* patchProps = new PatchProperties(patchName);

            if (patchProps == NULL)
            {
                throw FoamXError
                (
                    E_INVALID_ARG,
                    "Failed to create PatchProperties object",
                    functionName,
                    __FILE__, __LINE__
                );
            }

            patchProps->load(patchEntries[i].dict());

            // Add new patch to list.
            addPatch(patchProps);
        }

        // Read the field values from the field dictionaries.
        StringList_var fieldNames = app_->fields(); // Auto-release.
        for (unsigned int i = 0; i < fieldNames->length(); i++)
        {
            word fieldName(fieldNames[i]);

            // See if the field dictionary exists.
            fileName fieldDictName = time().timePath()/fieldName;

            if (exists(fieldDictName))
            {
                // Get the field object.
                IGeometricFieldImpl* pFieldValue = fieldValueMap_[fieldName];

                // Read the values from the field dictionary.
                dictionary fieldDict((IFstream(fieldDictName)()));
                pFieldValue->load(fieldDict);
            }
        }


        // Correct all the field boundary conditions for changes in mesh
        for
        (
            Dictionary<PatchProperties>::iterator iter = patchMap_.begin();
            iter != patchMap_.end();
            ++iter
        )
        {
            ///Info<< "Trying to match fields to " << iter().physicalType()
            //    << endl;
            //bool matches = fieldsMatchPatchPhysicalType(iter().physicalType());
            //Info<< "Matches:" << matches << endl;

            setPatchPhysicalType
            (
                iter().patchName().c_str(),
                iter().physicalType().c_str()
            );

            // In sync with file
            iter().modified(false);
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::importMesh
(
    const char* hostName,
    const char* rootDir,
    const char* caseName
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::importMesh(const char* hostName, "
        "const char* rootDir, const char* caseName)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        fileName importMeshDir = fileName(rootDir)/caseName/"constant/polyMesh";
        fileName destConstDir = rootDir_/caseName_/"constant";

        if (hostName == Foam::hostName())
        {
            if (dir(importMeshDir))
            {
                cp(importMeshDir, destConstDir);
            }
            else
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Source directory " + importMeshDir
                  + " does not exist or is unreadable",
                    functionName,
                    __FILE__, __LINE__
                );
            }
        }
        else
        {
            stringList argsList = procControl_.remoteCpArgs
            (
                userName(),
                hostName,
                importMeshDir,
                destConstDir
            );

            if (procControl_.system(argsList) != 0)
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Error while copying directory " + importMeshDir + " to " 
                   + destConstDir + " using remote copy command "
                   + procControl_.commandString(argsList),
                    functionName,
                    __FILE__, __LINE__
                );
            }
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::getFieldValues
(
    const char* fieldName,
    IGeometricField_out fieldValues
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::getFieldValues"
        "(const char* fieldName, IGeometricField_out fieldValues)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Check for a valid field name.
        if (!fieldValueMap_.found(fieldName))
        {
            throw FoamXError
            (
                E_INVALID_ARG,
                "Invalid field name",
                functionName,
                __FILE__, __LINE__
            );
        }

        fieldValues = fieldValueMap_[fieldName]->_this();
    }
    CATCH_ALL(functionName);
}

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

FoamXServer::StringList* FoamX::ICaseServerImpl::patchNames()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::patchNames()";

    LogEntry log(functionName, __FILE__, __LINE__);

    // Duplicate and return the patch name list.
    return new StringList
    (
        FoamXWordList(static_cast<const wordList&>(patchMap_.toc()))
    );
}


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

void FoamX::ICaseServerImpl::addPatch(PatchProperties* patchProps)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::addPatch(PatchProperties* patchProps)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Get the patch name and the boundary type.
        // patchPhysicalType is the name of the boundary type name, not the
        // definition key.
        const word& patchName    = patchProps->patchName();
        const word& patchPhysicalType = patchProps->physicalType();

        log << "Patch = " << patchName << endl;

        // Check patch name.
        if (patchMap_.found(patchName))
        {
            throw FoamXError
            (
                E_INVALID_ARG,
                "Invalid patch name",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Get the specified boundary type descriptor object.
        // If the boundary type name is not valid, an exception will be thrown.
        IPatchPhysicalTypeDescriptor_var boundaryDescriptor;
        app_->findPatchPhysicalType
        (
            patchPhysicalType.c_str(),
            boundaryDescriptor.out()
        );

        // Make sure that the boundary type name is valid.
        if (CORBA::is_nil(boundaryDescriptor))
        {
            throw FoamXError
            (
                E_FAIL,
                "Invalid boundary type name '" + patchPhysicalType + "'",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Append patch descriptor to list.
        patchMap_.append(patchName, patchProps);

        // Get patch field types from boundary descriptor.
        StringPairList_var patchFieldTypes =
            boundaryDescriptor->patchFieldTypes();

        // Add this patch to all field value objects.
        for (unsigned int i = 0; i <patchFieldTypes->length(); i++)
        {
            word fieldName(patchFieldTypes[i].name);

            if (fieldValueMap_.found(fieldName))
            {
                IGeometricFieldImpl* pFieldValue = fieldValueMap_[fieldName];

                // Convert between the patch field type key and the Foam name.

                // Get the PatchFieldDescriptor object.
                ITypeDescriptor_var pfDescriptor;
                foamProperties_->getPatchFieldType
                (
                    patchFieldTypes[i].value,
                    pfDescriptor.out()
                );

                // Make sure that the patch field type name is valid.
                if (CORBA::is_nil(pfDescriptor))
                {
                    throw FoamXError
                    (
                        E_FAIL,
                        "Invalid patch field type key '" 
                      + word(patchFieldTypes[i].value)
                      + "' for boundary type '" + patchPhysicalType + "'",
                        functionName,
                        __FILE__, __LINE__
                    );
                }

                // Get the Foam name and set the patch field type.
                CORBA::String_var patchFieldType = pfDescriptor->name();
                pFieldValue->addPatch(patchName.c_str(), patchFieldType);
            }
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::deletePatch(const char* patchName)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::deletePatch(const char* patchName)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Check patch name.
        if (!patchMap_.found(patchName))
        {
            throw FoamXError
            (
                E_INVALID_ARG,
                "Invalid patch name",
                functionName,
                __FILE__, __LINE__
            );
        }
        patchMap_.remove(patchName);

        // Remove this patch from all field value objects.
        StringList_var fieldNames = app_->fields();  // Auto-release.
        for (unsigned int i=0; i <fieldNames->length(); i++)
        {
            word fieldName(fieldNames[i]);
            if (fieldValueMap_.found(fieldName))
            {
                IGeometricFieldImpl* pFieldValue = fieldValueMap_[fieldName];
                pFieldValue->deletePatch(patchName);
            }
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::deleteAllPatches()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::deleteAllPatches()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Delete all currently defined patch information.
        wordList patchNames(patchMap_.toc());
        forAll(patchNames, i)
        {
            deletePatch(patchNames[i].c_str());
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::setPatchPhysicalType
(
    const char* patchName,
    const char* patchPhysicalType
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::setPatchPhysicalType"
        "(const char* patchName, const char* patchPhysicalType)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Check patch name.
        if (!patchMap_.found(patchName))
        {
            throw FoamXError
            (
                E_INVALID_ARG,
                "Invalid patch name",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Get the specified boundary type descriptor object.
        // If the boundary type name is not valid, an exception will be thrown.
        IPatchPhysicalTypeDescriptor_var boundaryDescriptor;
        app_->getPatchPhysicalType(patchPhysicalType, boundaryDescriptor.out());

        // Make sure that the boundary type name is valid.
        if (CORBA::is_nil(boundaryDescriptor))
        {
            throw FoamXError
            (
                E_FAIL,
                "Invalid boundary type name '" + word(patchPhysicalType) + "'",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Update the patch properties object.
        PatchProperties* patchProps = patchMap_.lookup(patchName);

        patchProps->patchType(boundaryDescriptor->patchType());
        patchProps->physicalType(patchPhysicalType);

        // Get patch field types from boundary descriptor.
        StringPairList_var patchFieldTypes =
            boundaryDescriptor->patchFieldTypes();

        // Loop over all fields and update the field value objects.
        for (unsigned int i = 0; i <patchFieldTypes->length(); i++)
        {
            word fieldName(patchFieldTypes[i].name);

            if (fieldValueMap_.found(fieldName))
            {
                // Get the patch field type descriptor.
                ITypeDescriptor_var pfDescriptor;
                foamProperties_->getPatchFieldType
                (
                    patchFieldTypes[i].value,
                    pfDescriptor.out()
                );

                // Need to pass the foam name, not the key,
                // for the patch field type.
                CORBA::String_var pfName = pfDescriptor->name();

                // Get the field object.
                IGeometricFieldImpl* pFieldValue = fieldValueMap_[fieldName];

                pFieldValue->setPatchFieldType(patchName, pfName);
            }
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::getPatchPhysicalType
(
    const char* patchName,
    CORBA::String_out patchPhysicalType
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::getPatchPhysicalType"
        "(const char* patchName, CORBA::String_out patchPhysicalType)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        log << "Patch = " << patchName << endl;

        // Check for a valid patch name.
        if (!patchMap_.found(patchName))
        {
            throw FoamXError
            (
                E_INVALID_ARG,
                "Invalid patch name",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Return the boundary type for this patch.
        PatchProperties* patchProps = patchMap_.lookup(patchName);

        patchPhysicalType = CORBA::string_dup(patchProps->physicalType().c_str());
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::getDictionary
(
    const char* dictionaryName,
    CORBA::Boolean forceRead,
    IDictionaryEntry_out dictRoot
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::getDictionary"
        "(const char*, CORBA::Boolean, IDictionaryEntry_out)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        RootDictionary* pDictRoot = NULL;

        // Force rereading just by clearing the cache.
        if (forceRead)
        {
            ObjRefHashTable<RootDictionary*>::iterator iter =
                dictionaryMap_.find(dictionaryName);

            dictionaryMap_.erase(iter);
        }

        // See if we have this dictionary entry object cached.
        if (dictionaryMap_.found(dictionaryName))
        {
            log << "Dictionary " << dictionaryName << " existing" << endl;

            pDictRoot = dictionaryMap_[dictionaryName];
        }
        else
        {
            log << "Creating new dictionary " << dictionaryName << endl;

            // Create and initialise a new dictionary entry object.
            ITypeDescriptor_var typeDescRef; // Auto release.
            app_->getDictionary(dictionaryName, typeDescRef.out());

            // Create a default RootDictionary object for this dictionary.
            pDictRoot = new RootDictionary
            (
                typeDescRef,
                rootDir_,
                caseName_
            );
            if (pDictRoot == NULL)
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Couldn't create dictionary entry object",
                    functionName,
                    __FILE__, __LINE__
                );
            }

            // Load the values from the file if it exists
            pDictRoot->load();

            // Add to dictionary map.
            dictionaryMap_.insert(dictionaryName, pDictRoot);
        }

        // Return a reference to the dictionary entry object.
        dictRoot = pDictRoot->_this();
    }
    CATCH_ALL(functionName);
}

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

CORBA::Long FoamX::ICaseServerImpl::fileModificationDate
(
    const char* dictionaryName
)
{
    fileName fName(rootDir_/caseName_/dictionaryName);

    if (exists(fName))
    {
        return label(lastModified(fName));
    }
    else
    {
        // Ugly. Let's hope not many dates with time_t==0
        return 0;
    }
}

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

void FoamX::ICaseServerImpl::readFile
(
    const char* fName,
    CORBA::String_out contents
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::readRawDictionary"
        "(const char* fName, CORBA::String_out contents)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        fileName dictName = rootDir_/caseName_/fName;

        log << "File = " << dictName << endl;

        // If file exists, return its contents.
        if (exists(dictName))
        {
            IFstream ifFile(dictName);

            if (ifFile)
            {
                OStringStream fileString;

                char ch;
                while (ifFile.get(ch))
                {
                    fileString << ch;
                }

                // Must duplicate string to return.
                contents = CORBA::string_dup(fileString.str().c_str());
            }
            else
            {
                throw FoamXError
                (
                    E_FAIL,
                    "Failed to read file '" + dictName + "'",
                    functionName,
                    __FILE__, __LINE__
                );
            }
        }
        else
        {
            throw FoamXError
            (
                E_FAIL,
                "Failed to open file '" + dictName + "'",
                functionName,
                __FILE__, __LINE__
            );
        }
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::writeFile
(
    const char* fName,
    const char* contents
)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::writeRawDictionary"
        "(const char* fName, const char* contents)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        fileName dictName = rootDir_/caseName_/fName;

        log << "File = " << dictName << endl;

        // Check if dictionary already exists, and if so move to .bak
        if (file(dictName))
        {
            mv(dictName, dictName + ".bak");
        }

        // Make sure path exists.
        if (!dir(dictName.path()) && !mkDir(dictName.path()))
        {
            throw FoamXError
            (
                E_FAIL,
                "Failed to create directory '" + dictName.path() + "'",
                functionName,
                __FILE__, __LINE__
            );
        }

        // Write dictionary contents.
        OFstream ofFile(dictName);
        if (ofFile)
        {
            ofFile << contents;
        }
        else
        {
            throw FoamXError
            (
                E_FAIL,
                "Failed to write file '" + dictName + "'",
                functionName,
                __FILE__, __LINE__
            );
        }
    }
    CATCH_ALL(functionName);
}

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

CORBA::Long FoamX::ICaseServerImpl::runCase(const char* arguments)
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::runCase(const char* arguments)";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Convert the arguments into a stringList and start
        // the calculation process.

        string stringArgs(arguments);

        int nArgs = 2;
        if (stringArgs.size() != 0)
        {
            nArgs += 1 + stringArgs.count(' ');
        }
        stringList args(nArgs + 1);
        args[0] = word(app_->name());

        int argi = 1;
        args[argi++] = rootDir_;
        args[argi++] = caseName_;

        size_t startPos = 0;
        int endPos;
        while ((endPos = stringArgs.find(" ", startPos)) != -1)
        {
            string oneArg(stringArgs(startPos, (endPos - startPos)));
            args[argi++] = oneArg;
            startPos = endPos + 1;
        }

        if ((stringArgs.size() != 0) && (startPos != (stringArgs.size() - 1)))
        {
            string oneArg
            (
                stringArgs
                (
                    startPos, 
                    stringArgs.size() - 1 - startPos
                )
            );
            args[argi++] = oneArg;
        }

        pid_ = procControl_.fork(args, "");

        if (pid_ == -1)
        {
            throw FoamXError
            (
                E_FAIL,
                "Error executing foam calculation process",
                functionName,
                __FILE__, __LINE__
            );
        }
    }
    CATCH_ALL(functionName);

    return pid_;
}

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

void FoamX::ICaseServerImpl::killCase()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::killCase()";

    LogEntry log(functionName, __FILE__, __LINE__);

    if (pid_ > 0)
    {
        if (procControl_.kill(pid_) != 0)
        {
            throw FoamXError
            (
                E_FAIL,
                string("Error killing job with PID ") + name(pid_),
                functionName,
                __FILE__, __LINE__
            );
        }
    }
}

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

void FoamX::ICaseServerImpl::validate()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::validate()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
    }
    CATCH_ALL(functionName);
}

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

CORBA::Boolean FoamX::ICaseServerImpl::modified()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::modified()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        for
        (
            ObjRefHashTable<IGeometricFieldImpl*>::iterator iter =
                fieldValueMap_.begin();
            iter != fieldValueMap_.end();
            ++iter
        )
        {
            if (iter()->modified()) return true;
        }

        for
        (
            ObjRefHashTable<RootDictionary*>::iterator iter =
                dictionaryMap_.begin();
            iter != dictionaryMap_.end();
            ++iter
        )
        {
            if (iter()->modified()) return true;
        }

        for
        (
            Dictionary<PatchProperties>::iterator iter = patchMap_.begin();
            iter != patchMap_.end();
            ++iter
        )
        {
            if (iter().modified()) return true;
        }
    }
    CATCH_ALL(functionName);

    return false;
}

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

void FoamX::ICaseServerImpl::save()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::save()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        // Save (default) controlDict
        saveControlDict();

        // Delete time and reconstruct to force reading of startTime
        // setting (Time::read does not reread startTime)
        if (dbPtr_)
        {
            delete dbPtr_;
            dbPtr_ = NULL;
        }
        dbPtr_ = new Time(Time::controlDictName, rootDir_, caseName_);


        // Make sure that the time directory exists.
        if (!dir(time().timePath()))
        {
            log << "Creating time directory " << time().timePath() << endl;

            if (!mkDir(time().timePath()))
            {
                // Throw a wobbler.
                throw FoamXError
                (
                    E_FAIL,
                    "Failed to create time directory",
                    functionName,
                    __FILE__, __LINE__
                );
            }
        }

        // For dictionaries that do not exist, make sure that a default
        // dictionary is cached.
        StringList_var dictNames = app_->dictionaries();
        for (unsigned int i = 0; i <dictNames->length(); i++)
        {
            word dictName(dictNames[i]);

            // Get dictionary type descriptor.
            ITypeDescriptor_var typeDescRef; // Auto release.
            app_->getDictionary(dictName.c_str(), typeDescRef.out());

            // Get dictionary full file name
            // (using the path information in the type descriptor).
            CORBA::String_var name = typeDescRef->name();

            fileName dictPathName = RootDictionary::path
            (
                rootDir_,
                caseName_,
                typeDescRef->dictionaryPath()
            )/fileName(name);

            if (!exists(dictPathName))
            {
                IDictionaryEntry_var dict; // Auto Release.
                getDictionary(name, false, dict.out());
            }
        }

        // Now save the case's dictionaries. 
        // Only need to save the dictionaries in the cache.
        for
        (
            HashTable<RootDictionary*>::iterator iter =
                dictionaryMap_.begin();
            iter != dictionaryMap_.end();
            ++iter
        )
        {
            // Validate the dictionary before we save.
            try
            {
                iter()->validate();
                iter()->save();
            }
            catch (ValidationError vex)
            {
                log << "Dictionary validation failed : "
                    << vex.errorMessage << " at path '" << vex.itemPath
                    << "'" << endl;
            }
        }


        // Save the patch and boundary condition information.
        writePatchData();

        // Get an ordered list of the patch names
        // Used to ensure the patchFields are written in the same order
        wordList patchNames(patchMap_.size());
        label i = 0;
        for
        (
            Dictionary<PatchProperties>::iterator iter = patchMap_.begin();
            iter != patchMap_.end();
            ++iter
        )
        {
            patchNames[i++] = iter().patchName();
        }

        // Save field dictionaries.
        StringList_var fieldNames = app_->fields();   // Auto-release.
        for (unsigned int i = 0; i <fieldNames->length(); i++)
        {
            word fieldName(fieldNames[i]);

            // Get field value object.
            IGeometricFieldImpl* pFieldValue = fieldValueMap_[fieldName];

            // Create dictionary writer object.
            DictionaryWriter dictWriter
            (
                rootDir_,
                caseName_,
                time().timeName(),
                fieldName
            );

            pFieldValue->save(dictWriter, patchNames);
        }

        log << "Case " << time().path() << " saved successfully" << endl;
    }
    CATCH_ALL(functionName);
}

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

void FoamX::ICaseServerImpl::close()
{
    static const char* functionName =
        "FoamX::ICaseServerImpl::close()";

    LogEntry log(functionName, __FILE__, __LINE__);

    try
    {
        caseBrowser_->unlockCase(rootDir_.c_str(), caseName_.c_str());

        // Unregister object from the naming service.
        NameServer fxNameServer(orb_.orbPtr_);
        fxNameServer.unbindObject(objectName_);

        // Remove case context
        fxNameServer.removeContext
        (
            hostContext_/userContext_/string::validate<word>(rootDir_)
        );

        // Remove user context
        fxNameServer.removeContext(hostContext_/userContext_);

        fxNameServer.disconnect();

        // Delete database
        if (dbPtr_)
        {
            delete dbPtr_;
            dbPtr_ = NULL;
        }

        // Shutdown th'Orb.
        // Removed because it screws-up Java 1.4 CORBA connection
        orb_.orbPtr_->shutdown(false);       // Do not wait for completion.
    }
    CATCH_ALL(functionName);
}


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