// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Template.h>
#include <OB/TemplateI.h>
#include <OB/Declarations.h>
#include <OB/Any.h>
#include <OB/Context.h>
#include <OB/NamedValue.h>

// ----------------------------------------------------------------------
// External, non-inline duplicate/release for templates
// ----------------------------------------------------------------------

void
OBDuplicate(CORBA_Context_ptr p)
{
    if(p)
	p -> _OB_incRef();
}

void
OBRelease(CORBA_Context_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

// ----------------------------------------------------------------------
// Template instantiations
// ----------------------------------------------------------------------

#ifndef HAVE_NO_EXPLICIT_TEMPLATES
template class OBObjVar< CORBA_Context >;
template class OBObjForSeq< CORBA_Context >;
//template class OBObjSeq< CORBA_Context >;
//template class OBSeqVar< OBObjSeq< CORBA_Context > >;
#else
#ifdef HAVE_PRAGMA_DEFINE
#pragma define(OBObjVar< CORBA_Context >)
#pragma define(OBObjForSeq< CORBA_Context >)
//#pragma define(OBObjSeq< CORBA_Context >)
//#pragma define(OBSeqVar< OBObjSeq< CORBA_Context > >)
#endif
#endif

// ----------------------------------------------------------------------
// Context constructor and destructor
// ----------------------------------------------------------------------

CORBA_Context::CORBA_Context(const char* name)
    : name_(name),
      values_(new OBStrSeq)
{
}

CORBA_Context::CORBA_Context(const char* name, CORBA_Context_ptr parent)
    : name_(name),
      parent_(CORBA_Context::_duplicate(parent)),
      values_(new OBStrSeq)
{
}

// ----------------------------------------------------------------------
// Context public member implementation
// ----------------------------------------------------------------------

CORBA_Status
CORBA_Context::create_child(const char* child_ctx_name, CORBA_Context_ptr& child_ctx)
{
    if(!child_ctx_name)
	throw CORBA_BAD_PARAM();

    child_ctx = new CORBA_Context(child_ctx_name, this);
}

CORBA_Status
CORBA_Context::set_one_value(const char* propname, const CORBA_Any& propvalue)
{
    if(!propname)
	throw CORBA_BAD_PARAM();

    char* s;
    if(!(propvalue >>= s))
	throw CORBA_BAD_TYPECODE();

    //
    // Search propname
    //
    for(CORBA_ULong i = 0 ; i < values_ -> length() ; i += 2)
    {
	if(strcmp(propname, values_[i]) == 0)
	{
	    values_[i + 1] = CORBA_string_dup(s);
	    return;
	}
    }

    //
    // Not found, add new propname
    //
    CORBA_ULong len = values_ -> length();
    values_ -> length(len + 2);
    values_[len++] = propname;
    values_[len++] = CORBA_string_dup(s);
}

CORBA_Status
CORBA_Context::set_values(CORBA_NVList_ptr values)
{
    //
    // Create new, empty list
    //
    values_ = new OBStrSeq(values -> count() * 2);
    values_ -> length(values -> count() * 2);
    
    //
    // Copy named values
    //
    for(CORBA_ULong i = 0 ; i < values -> count() ; i++)
    {
	CORBA_NamedValue_ptr nv = values -> item(i);
	
	char* s;
	if(!(*(nv -> value()) >>= s))
	    throw CORBA_BAD_TYPECODE();
	
	if(nv -> flags() != 0)
	    throw CORBA_INV_FLAG();
	
	values_[i * 2] = nv -> name();
	values_[i * 2 + 1] = CORBA_string_dup(s);
    }
}

CORBA_Status
CORBA_Context::delete_values(const char* pattern)
{
    if(!pattern)
	throw CORBA_BAD_PARAM();
    
    //
    // Match the pattern
    //
    char last = 0;
    if(strlen(pattern) > 0)
	last = pattern[strlen(pattern) - 1];

    bool found = false;

    for(CORBA_ULong i = 0 ; i < values_ -> length() ; i += 2)
    {
	bool match = false;

	if(last == '*')
	{
	    //
	    // Wildcard match ?
	    //
	    if(strncmp(pattern, values_[i], strlen(pattern) - 1) == 0)
		match = true;
	}
	else
	{
	    //
	    // Regular match ?
	    //
	    if(strcmp(pattern, values_[i]) == 0)
		match = true;
	}

	if(match)
	{
	    values_ -> remove(i);
	    values_ -> remove(i);
	    i -= 2;
	    found = true;
	}
    }

    if(!found)
	throw CORBA_BAD_CONTEXT();
}

CORBA_Status
CORBA_Context::get_values(const char* start_scope, CORBA_Flags op_flags,
			  const char* pattern, CORBA_NVList_ptr& values)
{
    if(!start_scope)
	throw CORBA_BAD_PARAM();

    if(!pattern)
	throw CORBA_BAD_PARAM();

    OBStrSeq seq;
    _OB_getValues(start_scope, op_flags, pattern, seq);

    if(seq.length() == 0)
	throw CORBA_BAD_CONTEXT();

    values = new CORBA_NVList;

    for(CORBA_ULong i = 0 ; i < seq.length() ; i += 2)
    {
	CORBA_Any* any = new CORBA_Any;
	(*any) <<= seq[i + 1];
	values -> add_value_consume(seq[i]._retn(), any, 0);
    }
}

CORBA_Status
CORBA_Context::_OB_getValues(const char* start_scope, CORBA_Flags op_flags,
			     const char* pattern, OBStrSeq& seq)
{
    //
    // Don't do anything in this context if start_scope doesn't match name_
    //
    if(strlen(start_scope) && strcmp(start_scope, name_) != 0)
    {
	if(CORBA_is_nil(parent_))
	    throw CORBA_BAD_CONTEXT();
	
	parent_ -> _OB_getValues(start_scope, op_flags, pattern, seq);
	return;
    }

    //
    // If there is a parent and scope is not restricted, get values
    // from parent. Else create new value list
    //
    if(op_flags != CORBA_CTX_RESTRICT_SCOPE && !CORBA_is_nil(parent_))
	parent_ -> _OB_getValues("", op_flags, pattern, seq);

    //
    // Match the pattern
    //
    char last = 0;
    if(strlen(pattern) > 0)
	last = pattern[strlen(pattern) - 1];
    
    for(CORBA_ULong i = 0 ; i < values_ -> length() ; i += 2)
    {
	bool match = false;
	
	if(last == '*')
	{
	    //
	    // Wildcard match ?
	    //
	    if(strncmp(pattern, values_[i], strlen(pattern) - 1) == 0)
		match = true;
	}
	else
	{
	    //
	    // Regular match ?
	    //
	    if(strcmp(pattern, values_[i]) == 0)
		match = true;
	}
	
	if(match)
	{
	    //
	    // First try to put replace value
	    //
	    CORBA_ULong j;
	    for(j = 0 ; j < seq.length() ; j += 2)
		if(strcmp(seq[j], values_[i]) == 0)
		{
		    seq[j + 1] = values_[i + 1];
		    break;
		}

	    //
	    // Value not found, create new
	    //
	    if(j == seq.length())
	    {
		seq.length(j + 2);
		seq[j] = values_[i];
		seq[j + 1] = values_[i + 1];
	    }
	}
    }
}

CORBA_Context*
CORBA_Context::_OB_create(OBStrSeq* seq)
{
    CORBA_Context* ctx = new CORBA_Context("");
    ctx -> values_ = seq;
    return ctx;
}
