#include "dbxml/DbXml.hpp"
#include "db_cxx.h"

//Forward declare
class myDbEnv;

class myXmlContainer
{
    public:
        //Constructor that accepts a reference to a myDbEnv object. Use this if 
        //you want to open a container in a database environment.
        myXmlContainer( const std::string &containerName, myDbEnv &myDbEnv ) 
                : containerName_( containerName ), 
                myDbEnvPtr_(&myDbEnv),
                cFlags_(DB_CREATE), 
                container_( &myDbEnv.getDbEnv(), containerName )
        {
            openContainer();
        }

        //Constructor that opens a container without a db environment.
        myXmlContainer( const std::string &containerName ) 
                : containerName_( containerName ), 
                 myDbEnvPtr_( 0 ),
                 cFlags_(DB_CREATE), 
                 container_( 0, containerName )
        {
            openContainer();
        }


        //get the container managed by this class
        inline DbXml::XmlContainer &getContainer() { return container_; }

        //get this container's name
        inline const std::string &getContainerName() { return containerName_; }

        //get the database environment in which this container was opened.
        //it is an error to call this if myXmlContainer( const std::string &) was used.
        inline DbEnv &getDbEnv()
        {
            if (! myDbEnvPtr_ ) 
            {
                //should throw here
                std::cerr << "Error: Container " << containerName_ << ":" << std::endl;
                std::cerr << "\tAttempted to access a database environment when one is not in use." << std::endl;
                std::cerr << "Giving up." << std::endl;
                exit( -1 );
            }
            return myDbEnvPtr_->getDbEnv();
        }

        ~myXmlContainer()
        {
            if ( container_.isOpen() )
            {
                try
                {
                    container_.close();
                } 
                //XmlException is derived from std::exception, so this catches XmlException
                catch(std::exception &e)
                {
                    std::cerr << "Error closing container: " << containerName_ << std::endl;
                    std::cerr << e.what() << std::endl;
                }
            }
        }

    private:
        myDbEnv *myDbEnvPtr_;
        DbXml::XmlContainer container_;
        std::string containerName_;
        u_int32_t cFlags_;

        void openContainer()
        {
            if ( containerName_.empty() )
            {
                std::cerr << "Error opening container. Container name is null." << std::endl;
            }

            DbTxn *txn;
            //open the container.
            try
            {
                if ( myDbEnvPtr_ != 0 )
                {
                    //create a transaction and open the container
                    myDbEnvPtr_->getDbEnv().txn_begin(0, &txn, 0);
                    container_.open(txn, cFlags_, 0);
                    txn->commit(0);
                } else {
                    //We aren't using a db environment, so don't use a transaction.
                    container_.open(0, cFlags_, 0);
                }
            }
            //XmlException is derived from std::exception, so this catches XmlException
            catch(std::exception &e)
            {
                std::cerr << "Error opening container: " << containerName_ << std::endl;
                std::cerr << e.what() << std::endl;

                //Abort the transaction. All changes made to the database since the start
                //of this transaction are discarded.
                txn->abort();
               
                exit(-1);
            }
            //DbException can be thrown by the txn code in the try block.
            //  DbException is not derived from std::exception, so it must be
            //  caught seperately
            catch(DbException &e)
            {
                std::cerr << "Error obtaining transaction for open of container: " << containerName_ << std::endl;
                std::cerr << e.what() << std::endl;

                //Abort the transaction. All changes made to the database since the start
                //of this transaction are discarded.
                txn->abort();
               
                exit(-1);
            }
        }
};

        
