//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / pf
//
//File: naming.cpp
//
//Version: $Revision: 1.1 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/10/05 07:09:49 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Pasi Nummisalo
//      Timo Pärnänen
//	Vesa-Matti Puro
//
//Description:
//      See corresponding header file.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 
//
//

#include "naming.h"
#include "system.h"
#include "exception.h"
#include "debug.h"

pfNaming *pfNaming::_only = 0;

pfNaming * pfNaming :: instance(void)
{
    if (_only == 0)
    {
        _only = new pfNaming;
    }
    return _only;
}

pfNaming :: pfNaming(void)
    : _namingContext(),
      _colum(0)
{
    CORBA_Object_var obj;
    try
    {
        obj = pfSystem::instance()->getORB()->
            resolve_initial_references("NameService");
        _namingContext = CosNaming_NamingContext::_narrow(obj);
    }
    catch (const CORBA_ORB::InvalidName &)
    {
        debugUser("can't resolve `NameService'");
        throw pfException(41);
    }
    

    if(CORBA_is_nil(_namingContext))
    {
        debugUser(": `NameService' is not a NamingContext object reference");
        exit(1);
    }
    
    return;
}

pfNaming :: ~pfNaming(void)
{
    _only = 0;
    return;
}

//
//Function: bind
//
//Description:
//    Bind a given CORBA object with a given name structure
//    to the Name Service.
//

void pfNaming :: bind(CosNaming_Name &name_,
                              CORBA_Object_ptr object_)
{
    debugUser("Binding");
    try
    {
        // Bind names with the Naming Service
        _namingContext->bind(name_, object_);
        debugUser("Bind OK");
    }
    catch(const CosNaming_NamingContext::NotFound &ex)
    {
        debugUser("Not Found: ");
	switch(ex.why)
	{
            case CosNaming_NamingContext::missing_node:
                debugUser("missing node");
                contextNotFound(name_, object_);
                break;
                            
            case CosNaming_NamingContext::not_object:
                throw pfMethodFailed(
                    "CosNaming Not Found (not object) Exception",
                     PF_EX_INFO);
                break;

            case CosNaming_NamingContext::not_context:
                debugUser("not context");
                contextNotFound(name_, object_);
                break;        
        }   
    }
    catch(const CosNaming_NamingContext::AlreadyBound &)
    {
        debugUser("Already Bound. Rebind");
        // All ready bound, rebind
        _namingContext->rebind(name_, object_);
        debugUser("Rebind OK");
    }
    catch(const CosNaming_NamingContext::CannotProceed&)
    {
        throw pfMethodFailed("CosNaming Cannot Proceed Exception",PF_EX_INFO);
    }
    catch(const CosNaming_NamingContext::InvalidName&)
    {
        throw pfMethodFailed("CosNaming Invalid Name Exception", PF_EX_INFO);
    }
    catch(const CosNaming_NamingContext::NotEmpty&)
    {
        throw pfMethodFailed("CosNaming Not Empty Exception",PF_EX_INFO);
    }
#ifdef __GNUG__
    catch(CORBA_COMM_FAILURE& ex)
#else
    catch(CORBA_SystemException& ex)
#endif
    {
	OBPrintException(ex);
	throw pfMethodFailed("CORBA System Exception", PF_EX_INFO);
    }
    
    return;
}

void pfNaming :: contextNotFound(CosNaming_Name &name_,
                                         CORBA_Object_ptr object_)
{
    // length()-1 = number of contexts
    unsigned int len = name_.length()-1;
    CosNaming_Name tmpName;
    
    for (unsigned int i = 0; i < len; i++)
    {
        tmpName = name_;
        tmpName.length(i+1);
        
        try
        {
            debugUser("Bind_new_context");
            // Bind contexts one at time
            _namingContext->bind_new_context(tmpName);
        }
        catch(const CosNaming_NamingContext::NotFound &ex)
        {
            throw pfMethodFailed("CORBA Not Found Exception",PF_EX_INFO);
        }
        catch(const CosNaming_NamingContext::AlreadyBound &)
        {
            debugUser("Context Already Bound");
        }
        catch(const CosNaming_NamingContext::CannotProceed&)
        {
            throw pfMethodFailed("CosNaming Cannot Proceed Exception",
                                 PF_EX_INFO);
        }
        catch(const CosNaming_NamingContext::InvalidName&)
        {
            throw pfMethodFailed("CosNaming Invalind Name Exception",
                                 PF_EX_INFO);
        }
        catch(const CosNaming_NamingContext::NotEmpty&)
        {
            throw pfMethodFailed("CosNaming Not Empty Exception",
                                 PF_EX_INFO);
        }    
    }
    try
    {
        // Try bind again
        _namingContext->bind(name_, object_);
        debugUser("Bind OK");
    }
    catch(CORBA_UserException &)
    {
        throw pfMethodFailed("CORBA User Exception", PF_EX_INFO);        
    }
    
    return;
}

//
//Function: resolve
//
//Description:
//  
//
//

CORBA_Object_ptr pfNaming :: resolve(CosNaming_Name &name_)
{
   
    CORBA_Object_ptr aObj = 0; // Should var type be used?
    
    try
    {
	aObj = _namingContext->resolve(name_);
        
        if (CORBA_is_nil(aObj) != 0)
        {
            throw pfMethodFailed("CORBA: is nil object", PF_EX_INFO);
        }
    }
    catch(const CosNaming_NamingContext::NotFound &)
    {
        throw pfMethodFailed("CosNaming Not Found Exception", PF_EX_INFO);
    }
    catch(const CosNaming_NamingContext::CannotProceed &)
    {
	throw pfMethodFailed("CosNaming Cannot Proceed Exception", PF_EX_INFO);
    }
    catch(const CosNaming_NamingContext::InvalidName &)
    {
        throw pfMethodFailed("CosNaming Invalid Name Exception", PF_EX_INFO);
    }   
#ifdef __GNUG__
    catch(CORBA_COMM_FAILURE& ex)
#else
    catch(CORBA_SystemException& ex)
#endif
    {
	OBPrintException(ex);
        throw pfMethodFailed("CORBA System Exception", PF_EX_INFO);
    }    
    return aObj;
}

//
//Function: unbind
//
//Description:
//      
//
//

void pfNaming :: unbind(CosNaming_Name &name_)    
{
    try
    {
        _namingContext->unbind(name_);
    }
    catch(const CosNaming_NamingContext::NotFound &)
    {
        throw pfMethodFailed("CosNaming Not Found Exception",PF_EX_INFO);
    }
    catch(const CosNaming_NamingContext::CannotProceed &)
    {
	throw pfMethodFailed("CosNaming Cannot Proceed Exception", PF_EX_INFO);
    }
    catch(const CosNaming_NamingContext::InvalidName &)
    {
        throw pfMethodFailed("CosNaming Invalid Name Exception", PF_EX_INFO);
    }       
#ifdef __GNUG__
    catch(CORBA_COMM_FAILURE &ex)
#else
    catch(CORBA_SystemException &ex)
#endif
    {
	OBPrintException(ex);
        throw pfMethodFailed("CORBA System Exception", PF_EX_INFO);
    }   
    return;
}

//
//Function: printObject/Context
//
//Description:
//      
//
//

void pfNaming :: printObject(const CosNaming_Binding& b)
{
    CORBA_ULong i;

    for(i = 0 ; i < b.binding_name.length() ; i++)
    {
	if(i > 0) cout << " ; ";

        unsigned int j;
        for(j=0; j < _colum; j++)
        {
            cout << "\t";
        }
	cout << b.binding_name[i].id << "-";
        cout << b.binding_name[i].kind << " (Object)" << endl;
    }
}

void pfNaming :: printContext(const CosNaming_Binding& b)
{
    CORBA_ULong i;

    for(i = 0 ; i < b.binding_name.length() ; i++)
    {
	if(i > 0) cout << " ; ";

        unsigned int j;
        for(j=0; j < _colum; j++)
        {
            cout << "\t";
        } 
	cout << b.binding_name[i].id << "-";
        cout << b.binding_name[i].kind << " (Context)" << endl;
    }    
}

//
//Function: listAllBindings
//
//Description:
// 
//

void pfNaming :: listAllBindings()
{
    _colum = 0;
    listContextBindings(_namingContext);
    return;
}

void pfNaming :: listContextBindings(CosNaming_NamingContext_ptr
                                             context_)
{
    CosNaming_BindingList_var bList;
    CosNaming_BindingIterator_var bIter;
    
    const CORBA_ULong batchSize = 999999;
    
    try
    {
        context_->list(batchSize, bList, bIter );    
    }
    catch(CORBA_UserException &)
    {
        throw pfMethodFailed("CORBA User Exception", PF_EX_INFO);        
    }
#ifdef __GNUG__
    catch(CORBA_COMM_FAILURE &ex)
#else
    catch(CORBA_SystemException &ex)
#endif
    {
	OBPrintException(ex);
        throw pfMethodFailed("CORBA System Exception", PF_EX_INFO);
    }   
    
    CORBA_ULong i;
    for(i=0; i < bList->length(); i++)
    {
        if (bList[i].binding_type == CosNaming_ncontext)
        {
            printContext(bList[i]);
            
            // Get inner context
            CosNaming_Name ncName;
            ncName.length(1);
            ncName[0].id = bList[i].binding_name[0].id;
            ncName[0].kind = bList[i].binding_name[0].kind;
            CORBA_Object_var ncObj = context_->resolve(ncName);
            
            CosNaming_NamingContext_var nc =
                CosNaming_NamingContext::_narrow(ncObj);
            assert(!CORBA_is_nil(nc));

            _colum++;
            listContextBindings(nc);
            _colum--;
        }
        else
        {
            printObject(bList[i]);
        }
        
    }
}

//
//Function: parseName
//
//Description:
//      Makes CosNaming_Name from string given in aaaa/bbb/cccc/dd format.
//      "/" means that there is two id and both are empty,
//	/aaa meand that there is two id "" and "aaa" and
//	aaa//bbb means that there is 3 id "aaa", "" and "bbb".
//      If name_ length is 0 then name.length comes also 0.
//

CosNaming_Name pfNaming :: parseName(const string &name_)
{
    CosNaming_Name name;
    pfUlong IDsInName = 0;
    pfUlong nameIndex = 0;
    pfUlong strNameLength = name_.length();
    string id;
    pfUlong i = 0;

    if(strNameLength > 0)
    {
        // There is at least one id.
        ++IDsInName;

        // Checks that how many IDs is in name_
        while(i < strNameLength)
        {
            if(name_[i] == '/')
            {
                ++IDsInName;
            }
            ++i;
        }
        name.length(IDsInName);

        // Getting all id's in string.
        for(i = 0; i < IDsInName; ++i)
        {
            id = "";

            // Copy id in name_ to id. 
            while(name_[nameIndex] != '/' && nameIndex < strNameLength)
            {
                id = id.append(1, name_[nameIndex]);
                ++nameIndex;
            }
            // Skip '/'
            ++nameIndex;

            name[i].id = CORBA_string_dup(id.c_str());
            name[i].kind = CORBA_string_dup("");
        }
    }

    return name;
}

