//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       bnreg.cpp
//
//--------------------------------------------------------------------------

//
//	BNREG.CPP: Use Registry to store persistent BN properties, etc.
//
#include <windows.h>
#include "bnreg.h"
#include "gmobj.h"


//
//	String constants for Registry handling
//
static const SZC szcBn				= "Software\\Microsoft\\DTAS\\BeliefNetworks";
static const SZC szcPropertyTypes	= "PropertyTypes";
static const SZC szcFlags			= "Flags";
static const SZC szcComment			= "Comment";
static const SZC szcChoices			= "Choices";
static const SZC szcCount			= "Count";


BNREG :: BNREG ()
{
	OpenOrCreate( HKEY_LOCAL_MACHINE, _rkBn, szcBn );
}


BNREG :: ~ BNREG ()
{
}

void BNREG :: OpenOrCreate ( HKEY hk, REGKEY & rk, SZC szcKeyName )
{
	LONG ec;
	ec = rk.Open( hk, szcKeyName );
	if ( ec != ERROR_SUCCESS )
		ec = rk.Create( hk, szcKeyName );
	if ( ec != ERROR_SUCCESS )
		throw GMException( EC_REGISTRY_ACCESS, "unable to open or create key" );
}

//
//  Store the property types from this network into the Registry.
//	If 'bStandard', force property types to be marked as "standard".
//
void BNREG :: StorePropertyTypes ( MBNET & mbnet, bool bStandard )
{
	REGKEY rkPtype;
	assert( _rkBn.HKey() != NULL );
	OpenOrCreate( _rkBn, rkPtype, szcPropertyTypes );

	MBNET::ITER mbnit( mbnet, GOBJMBN::EBNO_PROP_TYPE );
	GOBJMBN * pgmobj;
	SZC szcName;
	for ( ; pgmobj = *mbnit ; ++mbnit )
	{
		ZSREF zsrName = mbnit.ZsrCurrent();
		GOBJPROPTYPE * pbnpt;
		DynCastThrow( pgmobj, pbnpt );
		//  Get the name of the property type
		szcName = pbnpt->ZsrefName();
		//  See if it already exists
		LONG fPropType = FPropType( szcName );
		if ( fPropType >= 0 )
		{
			//  Property type already exists; guarantee that its "standard"
			//		flag is consistent
			bool bOldStandard = (fPropType & fPropStandard) > 0;
			//  It's standard if it was already or is now being forced to be
			bool bNewStandard = (pbnpt->FPropType() & fPropStandard) > 0 || bStandard;
			if ( bNewStandard ^ bOldStandard )
				throw GMException( EC_REGISTRY_ACCESS,
						"conflict between standard and non-standard property types" );

			//  Delete any older version of this property type
			rkPtype.RecurseDeleteKey( szcName );
		}
		CreatePropType( rkPtype, szcName, *pbnpt, bStandard );
	}
}

//
//	Load the property types from the Registry into this network.  If
//  'bStandard', load only the types marked "standard" if !bStandard,
//  load only the types NOT so marked.
//
void BNREG :: LoadPropertyTypes ( MBNET & mbnet, bool bStandard )
{
	REGKEY rkPtype;
	assert( _rkBn.HKey() != NULL );
	OpenOrCreate( _rkBn, rkPtype, szcPropertyTypes );

	FILETIME time;
	TCHAR szBuffer[256];
	DWORD dwSize = 256;
	ZSTR zsPt;
	DWORD dwKey = 0;

	for (;;)
	{
		dwSize = 256;
		if ( RegEnumKeyEx(rkPtype,
						 dwKey++,
						 szBuffer,
						 & dwSize,
						 NULL,
						 NULL,
						 NULL,
						 & time ) != ERROR_SUCCESS )
			break;

		zsPt = szBuffer;
		LONG fPropType = FPropType( zsPt );
		ASSERT_THROW( fPropType >= 0,
					  EC_REGISTRY_ACCESS,
					  "registry property type load enumeration failure" );

		//  Load this type if appropriate
		if ( ((fPropType & fPropStandard) > 0) == bStandard )		
			LoadPropertyType( mbnet, zsPt );
	}	
}

//  Load a single property type from the Registry into the network
void BNREG :: LoadPropertyType ( MBNET & mbnet, SZC szcPropTypeName )
{
	REGKEY rkPtype;
	assert( _rkBn.HKey() != NULL );
	OpenOrCreate( _rkBn, rkPtype, szcPropertyTypes );

	TCHAR szValue [2000];
	DWORD dwCount;
	SZC szcError = NULL;
	GOBJPROPTYPE * pgobjPt = NULL;

	do  // false loop for error checking
	{
		//  Check that the belief network doesn't already have such a beast
		if ( mbnet.PgobjFind( szcPropTypeName ) != NULL )
		{
			szcError = "duplicate property type addition attempt";
			break;
		}
		REGKEY rkPt;
		if ( rkPt.Open( rkPtype, szcPropTypeName ) != ERROR_SUCCESS )
		{
			szcError = "property type key open failure";
			break;
		}

		LONG fPropType = FPropType( szcPropTypeName );
		if ( fPropType <  0 )
			throw GMException( EC_REGISTRY_ACCESS,
							  "property type flag query failure" );

		//  Create the new property type object
		GOBJPROPTYPE * pgobjPt = new GOBJPROPTYPE;
		//  Set its flags and mark it as "persistent" (imported)
		pgobjPt->_fType = fPropType | fPropPersist;

		//  Get the comment string
		dwCount = sizeof szValue;
		if ( rkPt.QueryValue( szValue, szcComment, & dwCount ) != ERROR_SUCCESS )
		{
			szcError = "property type key value query failure";
			break;
		}
		szValue[dwCount] = 0;
		pgobjPt->_zsrComment = mbnet.Mpsymtbl().intern( szValue );

		//  Is this a "choice" property type?
		if ( fPropType & fPropChoice )
		{
			REGKEY rkChoices;
			if ( rkChoices.Open( rkPt, szcChoices ) != ERROR_SUCCESS )
			{
				szcError = "choices key missing for property type";
				break;
			}
			//  Get the "Count" value
			if ( rkChoices.QueryValue( dwCount, szcCount ) != ERROR_SUCCESS )
			{
				szcError = "failure to create choice count value";
				break;
			}
			ZSTR zs;
			int cChoice = dwCount;
			for ( int i = 0; i < cChoice; i++ )
			{
				zs.Format("%d",i);
				dwCount = sizeof szValue;
				if ( rkChoices.QueryValue( szValue, zs, & dwCount ) != ERROR_SUCCESS )
				{
					szcError = "failure to query choice string";
					break;
				}
				szValue[dwCount] = 0;
				pgobjPt->_vzsrChoice.push_back( mbnet.Mpsymtbl().intern( szValue ) );
			}
			assert( i == cChoice );
		}

		if ( szcError )
			break;

		mbnet.AddElem( szcPropTypeName, pgobjPt );

	} while ( false );

	if ( szcError )
	{
		delete pgobjPt;
		throw GMException( EC_REGISTRY_ACCESS, szcError );
	}
}

//  Remove all property types from the Registry
void BNREG :: DeleteAllPropertyTypes ()
{
	assert( _rkBn.HKey() != NULL );
	_rkBn.RecurseDeleteKey( szcPropertyTypes );
}

//  Return the value of the property type flags or -1 if open failure
LONG BNREG :: FPropType ( SZC szcPropType )
{
	REGKEY rkPtype;
	assert( _rkBn.HKey() != NULL );
	if ( rkPtype.Open( _rkBn, szcPropertyTypes ) != ERROR_SUCCESS )
		return -1;
	REGKEY rkPt;
	if ( rkPt.Open( rkPtype, szcPropType ) != ERROR_SUCCESS )
		return -1;

	DWORD dwValue;
	if ( rkPt.QueryValue( dwValue, szcFlags ) != ERROR_SUCCESS )
		return -1;
	return dwValue;	
}

void BNREG :: CreatePropType (
	REGKEY & rkParent,
	SZC szcPropType,
	GOBJPROPTYPE & bnpt,
	bool bStandard )
{
	REGKEY rkPt;
	LONG ec = rkPt.Create( rkParent, szcPropType );
	if ( ec != ERROR_SUCCESS )
		throw GMException( EC_REGISTRY_ACCESS,
						   "property type key creation failure" );

	bool bOK = true;

	//  Add the "flags" value, clearing the "persistent" flag
	DWORD dwFlags = bnpt.FPropType();
	dwFlags &= ~ fPropPersist;
	if ( bStandard )
		dwFlags |= fPropStandard;
	bOK &= (rkPt.SetValue( dwFlags, szcFlags ) == ERROR_SUCCESS);

	//  Add the "comment" string
	bOK &= (rkPt.SetValue( bnpt.ZsrComment(), szcComment ) == ERROR_SUCCESS);

	//  Add the choices, if applicable
	if ( bnpt.VzsrChoice().size() > 0 )
	{
		// Add the "Choices" subkey
		REGKEY rkChoice;
		ZSTR zs;
		int cChoice = bnpt.VzsrChoice().size();
		ec = rkChoice.Create( rkPt, szcChoices );
		if ( ec != ERROR_SUCCESS )
			throw GMException( EC_REGISTRY_ACCESS,
							   "property type choices key creation failure" );

		bOK &= (rkChoice.SetValue( cChoice, szcCount ) == ERROR_SUCCESS);
		for ( int i = 0; i < cChoice; i++ )
		{
			zs.Format("%d",i);
			bOK &= (rkChoice.SetValue( bnpt.VzsrChoice()[i], zs ) == ERROR_SUCCESS);
		}
	}

	if ( ! bOK )
		throw GMException( EC_REGISTRY_ACCESS,
						  "property type value addition failure" );	
}