//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project
//
//File: storage.h 
//
//State: $State: Exp $
//
//Version: $Revision: 1.35 $
//
//Date: $Date: 1999/03/08 11:05:16 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Authors:
//      Jari Katajavuori
//	Vesa-Matti Puro
//      Sami Raatikainen
//
//Description:
//      pfStorage is an assosiative multivariable container.
//      At the moment it can contain variables of type integer
//      and pfFrame. All variables have an unique name given by
//      the method 'defineX', where X is a type of the variable
//      (i.e. Integer or Frame). C++ exceptions are used as
//      error messages.
//
//Copyright:    
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//      
//Licence:
//     
//
//History:
//
//

#ifndef __PF_STORAGE_H__
#define __PF_STORAGE_H__

class pfInvalidTypeException;
class pfNameUndefinedException;
class pfNameAlreadyDefinedException;

#include <typeinfo>
#include <string>
#include <multimap.h>
#include <autoptr.h>
#include "types.h"
#include "frame.h"
#include "bitstring.h"
#include "ie.h"
#include "exception.h"
#include "iecontainer.h"

//
// Class: pfStorage
//
// Description:
//      See file description.
//

class pfStorage : public pfIEcontainer
{
    public:
        enum pfStorageDataType { UNDEFINED,
                                 BOOLEANTYPE,	// BOOLEAN
                                 BITSTRINGTYPE,	// BIT STRING
				 FRAMETYPE,	// OCTET STRING
                                 HEXSTRINGTYPE,	// HEX STRING
				 INTTYPE,	// INTEGER
                                 STRINGTYPE,	// CHARACTER STRING
                                 STORAGETYPE,   // STORAGE (recursive)
                                 IETYPE };      // Information Element 
                            

    private:

//
// Class: pfStorage::DataContainer
//
// Description:
//      pfStorage uses this class as primitive data unit. It's type
//      is not fixed but limited: all valid data types have to have
//      a set method and a get method. They must be initiated into
//      an union in private section as well.
//
//      The type of data primitive is initialized only once (in
//      the constructor). pfInvalidTypeException class is thrown if
//      a parameter of wrong type is given later.
//

    class DataContainer {
        public:
            DataContainer(void);
            DataContainer(const DataContainer &other_);
            virtual ~DataContainer(void);

            DataContainer &
                operator=(const DataContainer &other_);

            // Creating methods. These set the type of container.
            // following methods may throw pfInvalidTypeException
            static DataContainer createBitString(void);
            static DataContainer createBoolean(void);
            static DataContainer createFrame(void);
            static DataContainer createHexString(void);
            static DataContainer createInteger(void);
            static DataContainer createString(void);
            static DataContainer createStorage(void);
            //static DataContainer createIE(void);
            
            // Set methods. These are for setting a new value into variable.
            // following methods may throw pfInvalidTypeException
            void setBitString(pfBitString &bitStringValue_);
            void setBooleanFalse(void);
            void setBooleanTrue(void);
            void setFrame(pfFrame &frameValue_);
            void setHexString(pfFrame &frameValue_);
            void setInteger(pfUlong integerValue_);
            void setString(const string &stringValue_);
            void setStorage(pfStorage &storageValue_);
            //void setIE(pfIE *ieValue_);
            //void setIE(auto_ptr<pfIE> &ieValue_);
            
            // Get methods. These return the current value of variable.
            // following methods may throw pfInvalidTypeException
            bool getBoolean(void) const;
            pfBitString getBitString(void) const;
            pfFrame getFrame(void) const;
            pfFrame getHexString(void) const;
            pfUlong getInteger(void) const;
            string getString(void) const;
            pfStorage &getStorage(void) const;
            //pfIE *getIE(void) const;
            //pfIE *adoptIE(void);
            
            
            // This method checks if the variable has a value.
            // Returns non-zero if one found.
            bool isValuePresent(void) const;

            // Returns a type of the variable.
            pfStorageDataType getType(void) const;

            // Sets variable to be multivariable (several variables of the
            // same name allowed)
            void setMultivariable(void);
            bool isMultivariable(void) const;

        private:
            // may throw pfInvalidTypeException
            void checkType(pfStorageDataType type_) const;

            pfStorageDataType _type;
            pfUlong _integer;
            pfBitString _bitString;
            pfFrame _frame;
            string _string;    
            pfStorage *_storage;
            //pfIE *_ie;
            bool _valuePresent;
            bool _multivariable;
    };

    public:
        pfStorage(void);
        pfStorage(const pfStorage &other_);
        virtual ~pfStorage(void);
        
        // Definition methods. These must be called before
        // using the variables. They initialize a new assosiative
        // variable.
        // following methods may throw pfNameAlreadyDefinedException
        void defineBitString(const string name_);
        void defineBoolean(const string name_);
        void defineFrame(const string name_);
        void defineHexString(const string name_);
        void defineInteger(const string name_);
        void defineString(const string name_);
        void defineStorage(const string name_);
        //void defineIE(const string name_);
        //void defineMultiIE(const string name_);

        // Undefine method.
        void undefine(const string name_);
        

        // Set methods. These methods set a new value.
        // following methods may throw pfNameUndefinedException /
        // pfInvalidTypeException
        void setBitString(const string name_, pfBitString &value_);
        void setBooleanFalse(const string name_);
        void setBooleanTrue(const string name_);
        void setFrame(const string name_, pfFrame &value_);
        void setHexString(const string name_, pfFrame &value_);
        void setInteger(const string name_, pfUlong value_);
        void setString(const string name_, const string &value_);
        void setStorage(const string name_, pfStorage &value_);
        //void setIE(const string name_, pfIE *value_);
        //void setIE(const string name_, auto_ptr<pfIE> &value_);
        
        // Get methods. These methods return the current value.
        // following methods may throw pfNameUndefinedException /
        // pfInvalidTypeException
        pfBitString getBitString(const string name_) const;
        bool getBoolean(const string name_) const;
        pfFrame getFrame(const string name_) const;
        pfFrame getHexString(const string name_) const;
        pfUlong getInteger(const string name_) const;
        string getString(const string name_) const;
        pfStorage &getStorage(const string name_) const;
        //pfIE *getIE(const string name_) const;
        //pfIE *adoptIE(const string name_);

            
        // This method checks if the variable has a value.
        // Returns non-zero if one found.
        // method may throw pfNameUndefinedException
        bool isValuePresent(const string name_) const;

        // This method checks if the variable of name name_
        // is initialized. Returns non-zero if found.
        bool isVariableDefined(const string name_) const;

        // This method checks if the variable of name name_
        // is initialized and has a value. 
        bool isAvailable(const string name_) const;

        // Returns the number of variable instances of name name_
        pfUlong numberOfVariables(const string name_) const;
        
        // Substitutes storage contents for other
        pfStorage &operator=(const pfStorage &other_);
        void fetch(const pfStorage &other_);

        // Get values of existing variables from given storage
        void fetchValues(const pfStorage &other_);
        
        // Extends storage with those variables found at given storage
        // but not included in current one.
        void extend(const pfStorage &other_);

        // Copies a variable, may throw pfNameUndefinedException
        void copyVariable(const pfStorage &other_, const string variable_);
        
        // Copies a variable if it is available (exists and has value)
        void copyIfAvailable(const pfStorage &other_, const string variable_);
        void copyIfPresent(const pfStorage &other_, const string variable_);

        // methods may throw pfNameUndefinedException /pfInvalidTypeException 
        pfStorage &operator[](const string name_);
        pfStorage &operator[](const string name_) const;


        // Iterator interface, see Gamma page 257
        void first(void);
        void first(const string name_);
        void next(void);
        bool isDone(void) const;
        string currentItem(void) const;
        //pfIE &getCurrentIE(void) const;
        //void setCurrentIE(pfIE &ie_);
        
        // Returns a type of the variable.
        pfStorageDataType getType(const string name_) const;
 
        void print(void) const;

    private:

        typedef multimap<string, DataContainer, less<string> > mapType;
        typedef mapType::iterator mapIterType;
        typedef mapType::const_iterator mapConstIterType;
        typedef mapType::value_type mapValueType;

        // following six methods may throw pfNameUndefinedException
        void checkName(const string name_) const;
        void checkName2(const string name_) const;
        void checkMultiNameAndType(const string name_,
                                   pfStorageDataType type_) const;
        
        const DataContainer &getDataRef(const string name) const;
        DataContainer &getDataRef(const string name);
        void setData(const string name_, const DataContainer &datac_);
        
        // This map contains multitype data members (DataContainers)
        mapType _data;
        mapIterType _iter;
        mapIterType _iterUpper;
};

#endif // __PF_STORAGE_H__
