    // Copyright (C) 2007  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_CONFIG_READER_THREAD_SAFe_
    #define DLIB_CONFIG_READER_THREAD_SAFe_

    #include "config_reader_kernel_abstract.h"
    #include <string>
    #include <iostream>
    #include <sstream>
    #include "../algs.h"
    #include "../interfaces/enumerable.h"
    #include "../threads.h"
    #include "config_reader_thread_safe_abstract.h"

    namespace dlib
    {

        template <
            typename config_reader_base,
            typename map_string_void
            >
        class config_reader_thread_safe_1
        {

            /*!
                CONVENTION
                    - get_mutex() == *m
                    - *cr == the config reader being extended
                    - block_table[x] == (void*)&block(x)
                    - block_table.size() == the number of blocks in *cr
                    - block_table[key] == a config_reader_thread_safe_1 that contains &cr.block(key)
                    - if (own_pointers) then
                        - this object owns the m and cr pointers and should delete them when destructed
            !*/

        public:

            config_reader_thread_safe_1 (
                const config_reader_base* base,
                rmutex* m_
            );

            config_reader_thread_safe_1();

            typedef typename config_reader_base::config_reader_error config_reader_error;
            typedef typename config_reader_base::config_reader_access_error config_reader_access_error;

            config_reader_thread_safe_1(
                std::istream& in
            );

            config_reader_thread_safe_1(
                const std::string& config_file
            );

            virtual ~config_reader_thread_safe_1(
            );

            void clear (
            );

            void load_from (
                std::istream& in
            );

            void load_from (
                const std::string& config_file
            );

            bool is_key_defined (
                const std::string& key
            ) const;

            bool is_block_defined (
                const std::string& name
            ) const;

            typedef config_reader_thread_safe_1 this_type;
            const this_type& block (
                const std::string& name
            ) const;

            const std::string& operator[] (
                const std::string& key
            ) const;

            template <
                typename queue_of_strings
                >
            void get_keys (
                queue_of_strings& keys
            ) const;

            template <
                typename queue_of_strings
                >
            void get_blocks (
                queue_of_strings& blocks
            ) const;

            inline const rmutex& get_mutex (
            ) const;

        private:

            void fill_block_table (
            );
            /*!
                ensures
                    - block_table.size() == the number of blocks in cr
                    - block_table[key] == a config_reader_thread_safe_1 that contains &cr.block(key)
            !*/

            rmutex* m;
            config_reader_base* cr;
            map_string_void block_table;
            const bool own_pointers;

            // restricted functions
            config_reader_thread_safe_1(config_reader_thread_safe_1&);
            config_reader_thread_safe_1& operator=(config_reader_thread_safe_1&);

        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        config_reader_thread_safe_1<config_reader_base,map_string_void>::
        config_reader_thread_safe_1(
            const config_reader_base* base,
            rmutex* m_
        ) :
            m(m_),
            cr(const_cast<config_reader_base*>(base)),
            own_pointers(false)
        {
            fill_block_table();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        config_reader_thread_safe_1<config_reader_base,map_string_void>::
        config_reader_thread_safe_1(
        ) :
            m(0),
            cr(0),
            own_pointers(true)
        {
            try
            {
                m = new rmutex;
                cr = new config_reader_base;
            }
            catch (...)
            {
                if (m) delete m;
                if (cr) delete cr;
                throw;
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        void config_reader_thread_safe_1<config_reader_base,map_string_void>::
        clear(
        )
        {
            auto_mutex M(*m);
            cr->clear();
            fill_block_table();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        void config_reader_thread_safe_1<config_reader_base,map_string_void>::
        load_from(
            std::istream& in
        )
        {
            auto_mutex M(*m);
            cr->load_from(in);
            fill_block_table();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        void config_reader_thread_safe_1<config_reader_base,map_string_void>::
        load_from(
            const std::string& config_file
        )
        {
            auto_mutex M(*m);
            cr->load_from(config_file);
            fill_block_table();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        config_reader_thread_safe_1<config_reader_base,map_string_void>::
        config_reader_thread_safe_1(
            std::istream& in
        ) :
            m(0),
            cr(0),
            own_pointers(true)
        {
            try
            {
                m = new rmutex;
                cr = new config_reader_base(in);
                fill_block_table();
            }
            catch (...)
            {
                if (m) delete m;
                if (cr) delete cr;
                throw;
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        config_reader_thread_safe_1<config_reader_base,map_string_void>::
        config_reader_thread_safe_1(
            const std::string& config_file
        ) :
            m(0),
            cr(0),
            own_pointers(true)
        {
            try
            {
                m = new rmutex;
                cr = new config_reader_base(config_file);
                fill_block_table();
            }
            catch (...)
            {
                if (m) delete m;
                if (cr) delete cr;
                throw;
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        config_reader_thread_safe_1<config_reader_base,map_string_void>::
        ~config_reader_thread_safe_1(
        )
        {
            if (own_pointers)
            {
                delete m;
                delete cr;
            }

            // clear out the block table
            block_table.reset();
            while (block_table.move_next())
            {
                delete static_cast<config_reader_thread_safe_1*>(block_table.element().value());
            }
            block_table.clear();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        bool config_reader_thread_safe_1<config_reader_base,map_string_void>::
        is_key_defined (
            const std::string& key
        ) const
        {
            auto_mutex M(*m);
            return cr->is_key_defined(key);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        bool config_reader_thread_safe_1<config_reader_base,map_string_void>::
        is_block_defined (
            const std::string& name
        ) const
        {
            auto_mutex M(*m);
            return cr->is_block_defined(name);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        const config_reader_thread_safe_1<config_reader_base,map_string_void>& config_reader_thread_safe_1<config_reader_base,map_string_void>::
        block (
            const std::string& name
        ) const
        {
            auto_mutex M(*m);
            if (block_table.is_in_domain(name) == false)
            {
                throw config_reader_access_error(name,"");
            }

            return *static_cast<config_reader_thread_safe_1*>(block_table[name]);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        const std::string& config_reader_thread_safe_1<config_reader_base,map_string_void>::
        operator[] (
            const std::string& key
        ) const
        {
            auto_mutex M(*m);
            return (*cr)[key];
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        template <
            typename queue_of_strings
            >
        void config_reader_thread_safe_1<config_reader_base,map_string_void>::
        get_keys (
            queue_of_strings& keys
        ) const
        {
            auto_mutex M(*m);
            cr->get_keys(keys);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        template <
            typename queue_of_strings
            >
        void config_reader_thread_safe_1<config_reader_base,map_string_void>::
        get_blocks (
            queue_of_strings& blocks
        ) const
        {
            auto_mutex M(*m);
            cr->get_blocks(blocks);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        const rmutex& config_reader_thread_safe_1<config_reader_base,map_string_void>::
        get_mutex (
        ) const
        {
            return *m;
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    //      private member functions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename config_reader_base,
            typename map_string_void
            >
        void config_reader_thread_safe_1<config_reader_base,map_string_void>::
        fill_block_table (
        )
        {
            using namespace std;
            // first empty out the block table
            block_table.reset();
            while (block_table.move_next())
            {
                delete static_cast<config_reader_thread_safe_1*>(block_table.element().value());
            }
            block_table.clear();

            std::vector<std::string> blocks;
            cr->get_blocks(blocks);

            // now fill the block table up to match what is in cr
            for (unsigned long i = 0; i < blocks.size(); ++i)
            {
                config_reader_thread_safe_1* block = new config_reader_thread_safe_1(&cr->block(blocks[i]),m);
                void* temp = block;
                block_table.add(blocks[i],temp);
            }
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_CONFIG_READER_THREAD_SAFe_