//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project
//
//File: storage.cpp
//
//State: $State: Exp $
//
//Version: $Revision: 1.2 $
//
//Date: $Date: 1998/10/20 06:07:21 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Authors:
//      Jari Pusa
//	Vesa-Matti Puro
//
//Description:
//      See header file (storage.h) description.
//
//Copyright:    
//
//      
//Licence:
//     
//
//History:
//
//


#include "storage.h"

//
// Method: Class DataContainer constructors
//
// Description:
//
//

pfStorage :: DataContainer :: DataContainer(void)
    : _type(UNDEFINED),
      _integer(0),
      _frame(),
      _string(),
      _storage(0),
      _valuePresent(0)
{
    return;
}

pfStorage :: DataContainer 
    :: DataContainer(const DataContainer &other_)
{
    _type = other_._type;
    _frame = other_._frame;
    _integer = other_._integer;
    _string = other_._string;
    _storage = 0;
    if (other_._storage != 0)
    {
        _storage = new pfStorage(*other_._storage);
    }
    _valuePresent = other_._valuePresent;
    return;
}

pfStorage::DataContainer &
    pfStorage :: DataContainer
        :: operator=(const DataContainer &other_)
{
    if (this != &other_)
    {
        _type = other_._type;
        _frame = other_._frame;
        _integer = other_._integer;
        _string = other_._string;
        delete _storage;
        _storage = 0;
        if (other_._storage != 0)
        {
            _storage = new pfStorage(*other_._storage);
        }
        _valuePresent = other_._valuePresent;
    }
    return *this;
}


//
// Method: Class DataContainer destructor
//
// Description:
//      Classes must be deleted here.
//

pfStorage::DataContainer :: ~DataContainer(void)
{
    delete _storage;
    return;
}

//
// Method: create
//
// Description:
//      Creates a new container setting its type. These are
//      used to prevent exceptions during initialization in
//      constructors and to avoid public use of pfStorageDataType.
//

pfStorage::DataContainer
    pfStorage::DataContainer :: createBitString(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = BITSTRINGTYPE;
    return result;
}

pfStorage::DataContainer
    pfStorage::DataContainer :: createBoolean(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = BOOLEANTYPE;
    return result;
}

pfStorage::DataContainer
    pfStorage::DataContainer :: createFrame(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = FRAMETYPE;
    return result;
}

pfStorage::DataContainer
    pfStorage::DataContainer :: createHexString(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = HEXSTRINGTYPE;
    return result;
}

pfStorage::DataContainer
    pfStorage::DataContainer :: createInteger(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = INTTYPE;
    return result;
}

pfStorage::DataContainer
    pfStorage::DataContainer :: createString(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = STRINGTYPE;
    return result;
}

pfStorage::DataContainer
    pfStorage::DataContainer :: createStorage(void)
        throw (pfInvalidTypeException)
{
    DataContainer result;
    result._type = STORAGETYPE;
    result._storage = new pfStorage;
    return result;
}

//
// Method: Set
//
// Description:
//      Sets new value into the variable after checking the
//      type. pfInvalidTypeException is thrown if wrong type
//      is encountered.
//

void pfStorage::DataContainer :: setBitString(pfFrame &frameValue_)
    throw (pfInvalidTypeException)
{
    checkType(BITSTRINGTYPE);
    _frame = frameValue_;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setBooleanFalse(void)
    throw (pfInvalidTypeException)
{
    checkType(BOOLEANTYPE);
    _integer = 0;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setBooleanTrue(void)
    throw (pfInvalidTypeException)
{
    checkType(BOOLEANTYPE);
    _integer = 1;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setFrame(pfFrame &frameValue_)
    throw (pfInvalidTypeException)
{
    checkType(FRAMETYPE);
    _frame = frameValue_;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setHexString(pfFrame &frameValue_)
    throw (pfInvalidTypeException)
{
    checkType(HEXSTRINGTYPE);
    _frame = frameValue_;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setInteger(pfUlong integerValue_)
    throw (pfInvalidTypeException)
{
    checkType(INTTYPE);
    _integer = integerValue_;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setString(string &stringValue_)
    throw (pfInvalidTypeException)
{
    checkType(STRINGTYPE);
    _string = stringValue_;
    _valuePresent = 1;
    return;
}

void pfStorage::DataContainer :: setStorage(pfStorage &storageValue_)
    throw (pfInvalidTypeException)
{
    checkType(STORAGETYPE);
    delete _storage;
    _storage = new pfStorage(storageValue_);
    _valuePresent = 1;
    return;
}

//
// Method: Get
//
// Description:
//      Gets data. pfInvalidTypeException is thrown if
//      encounters wrong data type.
//

pfFrame pfStorage::DataContainer :: getBitString(void) const
    throw (pfInvalidTypeException)
{
    checkType(BITSTRINGTYPE);
    return _frame;
}

bool pfStorage::DataContainer :: getBoolean(void) const
    throw (pfInvalidTypeException)
{
    checkType(BOOLEANTYPE);
    return _integer ? 1 : 0;
}

pfFrame pfStorage::DataContainer :: getFrame(void) const
    throw (pfInvalidTypeException)
{
    checkType(FRAMETYPE);
    return _frame;
}

pfFrame pfStorage::DataContainer :: getHexString(void) const
    throw (pfInvalidTypeException)
{
    checkType(HEXSTRINGTYPE);
    return _frame;
}

pfUlong pfStorage::DataContainer :: getInteger(void) const
    throw (pfInvalidTypeException)
{
    checkType(INTTYPE);
    return _integer;
}

string pfStorage::DataContainer :: getString(void) const
    throw (pfInvalidTypeException)
{
    checkType(STRINGTYPE);
    return _string;
}

// does this work?

pfStorage &pfStorage::DataContainer :: getStorage(void) const
    throw (pfInvalidTypeException)
{
    checkType(STORAGETYPE);
    return *_storage;
}

//
// Method: isValuePresent
//
// Description:
//      Returns non-zero if the variable has a value.
//

bool pfStorage :: DataContainer :: isValuePresent(void) const
{
    return _valuePresent;
}

//
// Method: getType
//
// Description:
//      Returns the type of the data
//

pfStorage::pfStorageDataType 
pfStorage :: DataContainer :: getType(void) const
{
    return _type;
}

void pfStorage::DataContainer
    :: checkType(pfStorageDataType type_) const
        throw (pfInvalidTypeException)
{
    if (_type != type_)
    {
        throw pfInvalidTypeException(0, type_, _type, PF_EX_INFO);
    }
    return;
}

//
// Method: Class pfStorage constructors
//
// Description:
//      

pfStorage :: pfStorage(void)
    : _data(),
      _iter(_data.begin())
{
    return;
}

pfStorage :: pfStorage(const pfStorage &other_)
{
    _data = other_._data;
    _iter = other_._iter;
    return;
}

pfStorage :: ~pfStorage(void)
{
    return;
}

//
// Method: define
//
// Description:
//      Initializes a variable named 'name_' into a map. After calling
//      this method the name is assosiated with corresponding type and
//      can't be used as a variable of some other type. If the name
//      is already reserved, the pfNameAlreadyDefinedException is
//      thrown.
//

// does this data.insert work?

void pfStorage :: defineBitString(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createBitString();
    return;
}

void pfStorage :: defineBoolean(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createBoolean();
    return;
}

void pfStorage :: defineFrame(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createFrame();
    return;
}

void pfStorage :: defineHexString(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createHexString();
    return;
}

void pfStorage :: defineInteger(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createInteger();
    return;
}

void pfStorage :: defineString(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createString();
    return;
}

void pfStorage :: defineStorage(string name_)
    throw (pfNameAlreadyDefinedException)
{
    checkName2(name_);
    _data[name_] = DataContainer::createStorage();
    return;
}

//
// Method: set
//
// Description:
//      Sets a new value. If name_ is not defined, the
//      pfNameUndefinedException is thrown. 
//

void pfStorage :: setBitString(string name_, pfFrame &value_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setBitString(value_);
    return;
}

void pfStorage :: setBooleanFalse(string name_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setBooleanFalse();
    return;
}

void pfStorage :: setBooleanTrue(string name_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setBooleanTrue();
    return;
}

void pfStorage :: setFrame(string name_, pfFrame &value_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setFrame(value_);
    return;
}

void pfStorage :: setHexString(string name_, pfFrame &value_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setHexString(value_);
    return;
}

void pfStorage :: setInteger(string name_, pfUlong value_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setInteger(value_);
    return;
}

void pfStorage :: setString(string name_, string &value_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setString(value_);
    return;
}

void pfStorage :: setStorage(string name_, pfStorage &value_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    _data[name_].setStorage(value_);
    return;
}

//
// Method: Get
//
// Description:
//      Gets a current value of the data member. If the given
//      variable (name_) is not defined, the
//      pfNameUndefinedException is thrown.
//

pfFrame pfStorage :: getBitString(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    pfFrame result;
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    result = (*iter).second.getBitString();
    return result;
}

bool pfStorage :: getBoolean(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    bool result = 0;
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    result = (*iter).second.getBoolean();
    return result;
}

pfFrame pfStorage :: getFrame(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    pfFrame result;
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    result = (*iter).second.getFrame();
    return result;
}

pfFrame pfStorage :: getHexString(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    pfFrame result;
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    result = (*iter).second.getHexString();
    return result;
}

pfUlong pfStorage :: getInteger(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    pfUlong result = 0;
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    result = (*iter).second.getInteger();
    return result;
}

string pfStorage :: getString(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    string result;
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    result = (*iter).second.getString();
    return result;
}

pfStorage &pfStorage :: getStorage(string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    pfStorage &result = (*iter).second.getStorage();
    return result;
}

//
// Method: isValuePresent
//
// Description:
//      Returns non-zero if the variable named name_ has a
//      value. If the variable is not defined, the
//      pfNameUndefinedException is thrown.
//

bool pfStorage :: isValuePresent(string name_) const
    throw (pfNameUndefinedException)
{
    mapConstIterType iter = _data.find(name_);
    checkName(name_); 
    bool result = (*iter).second.isValuePresent();
    return result;
}

//
// Method: isVariableDefined
//
// Description:
//      Returns non-zero if a variable named 'name_' is defined.
//

bool pfStorage :: isVariableDefined(string name_) const
{
    mapConstIterType iter = _data.find(name_);
    bool result = (iter != _data.end());
    return result;
}

//
// Method: operator=
//
// Description:
//

pfStorage &pfStorage :: operator=(const pfStorage &other_)
{
    fetch(other_);
    return *this;
}

//
// Method: fetch
//
// Description:
//      Replaces current storage values with values on the
//      other storage.
//

void pfStorage :: fetch(const pfStorage &other_)
{
    if (this != &other_)
    {
        _data = other_._data;
    }
    return;
}

//
// Method: operator[]
//
// Description:
//      Returns a reference to a storage typed variable
//

pfStorage &pfStorage :: operator[](string name_)
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    pfStorage &result = _data[name_].getStorage();
    return result;
}


pfStorage &pfStorage :: operator[](string name_) const
    throw (pfNameUndefinedException,
           pfInvalidTypeException)
{
    checkName(name_); 
    mapConstIterType iter = _data.find(name_);
    return (*iter).second.getStorage();
}

// Iterator interface, see Gamma page 257

void pfStorage :: first(void)
{
    _iter = _data.begin();
    return;
}

void pfStorage :: next(void)
{
    ++_iter;
    return;
}

bool pfStorage :: isDone(void) const
{
    bool result;
    result = (_iter == _data.end()) ? 1 : 0;
    return result;
}

string pfStorage :: currentItem(void) const
{
    string result; 
    result = (*_iter).first;
    return result; 
}

//
// Method: getType
//
// Description:
//      Returns the type of the data
//

pfStorage::pfStorageDataType 
pfStorage :: getType(string name_) const
{
    pfStorageDataType result;
    mapConstIterType iter = _data.find(name_);
    checkName(name_); 
    result = (*iter).second.getType();
    return result;
}

//
// Method: print
//
// Description:
//      Prints some information concerning the contents of the storage. 
// 

void pfStorage :: print(ostream &out_) const
{
    pfFrame frame;
    mapConstIterType iter = _data.begin();
    out_ << "Contents of a property" << endl;
    while (iter != _data.end())
    {
        out_ << "ID = " << (*iter).first << endl;
        switch((*iter).second.getType())
        {
            case BOOLEANTYPE:
            {
                string trueStr("TRUE");
                string falseStr("FALSE");
                out_ << "TYPE = BOOLEAN" << endl;
                out_ << "VALUE = "
                     << (((*iter).second.getBoolean())
                      ? trueStr : falseStr)
                     << endl;
                break;
            }
            case BITSTRINGTYPE:
                out_ << "TYPE = BIT STRING" << endl;
                frame = (*iter).second.getBitString();
                frame.useBinary();
                out_ << "VALUE = " << frame.toString() << endl;
                break;
            case FRAMETYPE:
                out_ << "TYPE = OCTET STRING" << endl;
                frame = (*iter).second.getFrame();
                frame.useHex();
                out_ << "VALUE = " << frame.toString() << endl;
                break;
            case HEXSTRINGTYPE:
                out_ << "TYPE = HEX STRING" << endl;
                frame = (*iter).second.getHexString();
                frame.useHex();
                out_ << "VALUE = " << frame.toString() << endl;
                break;
            case INTTYPE:
                out_ << "TYPE = INTEGER" << endl;
                out_ << "VALUE = " << (*iter).second.getInteger() << endl;
                break;
            case STRINGTYPE:
                out_ << "TYPE = CHARACTER STRING" << endl;
                out_ << "VALUE = " << (*iter).second.getString() << endl;
                break;
            case STORAGETYPE:
                out_ << "TYPE = SEQUENCE" << endl;
                break;
            default: ;
                break;
        }
        ++iter;
    }
    return;
}

void pfStorage :: checkName2(string name_) const
    throw (pfNameAlreadyDefinedException)
{
    // The second paramater of pair 'result' tells if the insertion
    // succeed or failed. Failure implicates that the name was already
    // assosiated.
    mapConstIterType iter = _data.find(name_);
    if (iter != _data.end())
    {
        throw pfNameAlreadyDefinedException(name_, PF_EX_INFO);
    }
    return;
}

void pfStorage :: checkName(string name_) const
    throw (pfNameUndefinedException)
{
    // If the name_ was not found, iterator is set into an
    // end position.
    mapConstIterType iter = _data.find(name_);
    if (iter == _data.end())
    {
        throw pfNameUndefinedException(name_, PF_EX_INFO);
    }
    return;
}

#ifdef STORAGETEST

#include <typeinfo>
#include <iostream.h>
#include "frame.h"

int main (int argc, char *argv[])
{
    int result = 0;
    pfStorage properties;
    pfStorage subproperties;
    pfFrame frame;
    pfFrame frame2;

    frame.fromInteger(129);
#if 0
    frame.putFirst(129);
    frame.putFirst(00);
    frame.putFirst(00);
    frame.putFirst(00);
#endif
    
    properties.defineInteger("PROP_A");
    properties.defineInteger("PROP_B");
    properties.defineInteger("PROP_C");
    properties.defineInteger("PROP_D");
    properties.defineStorage("PROP_E");
    properties.defineString("PROP_F");
    properties.defineBoolean("PROP_G");
    properties.defineBoolean("PROP_H");
    properties.defineFrame("PROP_I");

    properties.setInteger("PROP_A", 1);
    properties.setInteger("PROP_B", 2);
    properties.setInteger("PROP_C", 3);
    properties.setInteger("PROP_D", 4);
    properties.setString("PROP_F", "hello, world");
    properties.setBooleanFalse("PROP_G");
    properties.setBooleanTrue("PROP_H");
    properties.setFrame("PROP_I", frame);

    subproperties.defineInteger("PROP_A");
    subproperties.defineInteger("PROP_B");
    subproperties.defineInteger("PROP_C");
    subproperties.defineInteger("PROP_D");

    subproperties.setInteger("PROP_A", 11);
    subproperties.setInteger("PROP_B", 12);
    subproperties.setInteger("PROP_C", 13);
    subproperties.setInteger("PROP_D", 14);

    properties["PROP_E"] = subproperties;

#if 0
    cout << "PROP_A " << properties.getInteger("PROP_A") << endl;
    cout << "PROP_B " << properties.getInteger("PROP_B") << endl;
    cout << "PROP_C " << properties.getInteger("PROP_C") << endl;
    cout << "PROP_D " << properties.getInteger("PROP_D") << endl;
    cout << "PROP_E/PROP_A " << properties["PROP_E"].getInteger("PROP_A") << endl;
    cout << "PROP_E/PROP_B " << properties["PROP_E"].getInteger("PROP_B") << endl;
    cout << "PROP_E/PROP_C " << properties["PROP_E"].getInteger("PROP_C") << endl;
    cout << "PROP_E/PROP_D " << properties["PROP_E"].getInteger("PROP_D") << endl;
    cout << "PROP_F " << properties.getString("PROP_F") << endl;
    cout << "PROP_G " << (long)properties.getBoolean("PROP_G") << endl;
    cout << "PROP_H " << (long)properties.getBoolean("PROP_H") << endl;
    frame = properties.getFrame("PROP_I");
    frame.useHex();
    cout << "PROP_I " << frame.toString() << endl;
    frame.useBinary();
    cout << "PROP_I " << frame.toString() << endl;
    cout << "PROP_I " << frame.toInteger() << endl;
#endif

    properties.print();
    cout << "-----------------------------------------------" << endl;
    properties.first();
    while(properties.isDone() == 0)
    {
        cout << properties.currentItem() << endl;
        properties.next();
    }

    return result;
}
#endif // STORAGETEST

