// Copyright (C) 2007  Davis E. King (davisking@users.sourceforge.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,
        bool checking
        >
    class config_reader_thread_safe_1 : public enumerable<config_reader_thread_safe_1<config_reader_base,map_string_void,checking> >
    {

        /*!                
            CONVENTION
                - get_mutex() == m
                - *cr == the config reader being extended
                - block_table[x] == (void*)&block(x)
                - cr->size == block_table.size()
                - 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;

        config_reader_thread_safe_1(
            std::istream& in
        );

        virtual ~config_reader_thread_safe_1(
        ); 

        void clear (
        );

        void load_from (
            std::istream& in
        );

        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;

        inline bool at_start (
        ) const ;

        inline void reset (
        ) const ;

        inline bool current_element_valid (
        ) const ;

        inline const this_type& element (
        ) const ;

        inline this_type& element (
        ) ;

        inline bool move_next (
        ) const ;

        inline unsigned long size (
        ) const ;

        inline const std::string& current_block_name (
        ) const;

        inline const rmutex& get_mutex (
        ) const;

    private:

        void fill_block_table (
        );
        /*!
            ensures
                - block_table.size() == cr->size()
                - 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&);

    };

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

    /* 
        This is a bunch of crap so we can enable and disable the DLIB_CASSERT statements
        without getting warnings about conditions always being true or false.
    */
    namespace config_reader_thread_safe_1_helpers
    {
        template <typename cr_type, bool do_check>
        struct helper;

        template <typename cr_type>
        struct helper<cr_type,false>
        {
            static void check_block_precondition (const cr_type&,  const std::string& ) {}
            static void check_current_block_name_precondition (const cr_type& cr) {} 
            static void check_element_precondition (const cr_type& cr) {}
        };

        template <typename cr_type>
        struct helper<cr_type,true>
        {
            static void check_block_precondition (const cr_type& cr, const std::string& name) 
            {
                DLIB_CASSERT ( cr.is_block_defined(name) == true ,
                          "\tconst this_type& config_reader_thread_safe::block(name)"
                          << "\n\tTo access a sub block in the config_reader the block must actually exist."
                          << "\n\tname == " << name 
                          << "\n\t&cr:   " << &cr 
                );
            }

            static void check_current_block_name_precondition (const cr_type& cr) 
            {
                DLIB_CASSERT ( cr.current_element_valid() == true ,
                          "\tconst std::string& config_reader_thread_safe::current_block_name()"
                          << "\n\tYou can't call current_block_name() if the current element isn't valid."
                          << "\n\t&cr: " << &cr 
                );
            }

            static void check_element_precondition (const cr_type& cr) 
            {
                DLIB_CASSERT ( cr.current_element_valid() == true ,
                          "\tthis_type& config_reader_thread_safe::element()"
                          << "\n\tYou can't call element() if the current element isn't valid."
                          << "\n\t&cr: " << &cr 
                );
            }
        };
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    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,
        bool checking
        >
    config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    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,
        bool checking
        >
    void config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    clear(
    )
    {
        auto_mutex M(*m);
        cr->clear();
        fill_block_table();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    void config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    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,
        bool checking
        >
    config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    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,
        bool checking
        >
    config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    ~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 reinterpret_cast<config_reader_thread_safe_1*>(block_table.element().value());
        }
        block_table.clear();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    bool config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    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 checking
        >
    bool config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    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,
        bool checking
        >
    const config_reader_thread_safe_1<config_reader_base,map_string_void,checking>& config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    block (
        const std::string& name
    ) const
    {
        auto_mutex M(*m);
        config_reader_thread_safe_1_helpers::helper<config_reader_thread_safe_1,checking>::
            check_block_precondition(*this,name);
        return *reinterpret_cast<config_reader_thread_safe_1*>(block_table[name]);
    }

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

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

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

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

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    bool config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    at_start (
    ) const 
    {
        auto_mutex M(*m);
        return block_table.at_start();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    void config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    reset (
    ) const 
    {
        auto_mutex M(*m);
        block_table.reset();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    bool config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    current_element_valid (
    ) const 
    {
        auto_mutex M(*m);
        return block_table.current_element_valid();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    const config_reader_thread_safe_1<config_reader_base,map_string_void,checking>& config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    element (
    ) const 
    {
        auto_mutex M(*m);
        config_reader_thread_safe_1_helpers::helper<config_reader_thread_safe_1,checking>::
            check_element_precondition(*this);
        return *reinterpret_cast<config_reader_thread_safe_1*>(block_table.element().value());
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    config_reader_thread_safe_1<config_reader_base,map_string_void,checking>& config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    element (
    ) 
    {
        auto_mutex M(*m);
        config_reader_thread_safe_1_helpers::helper<config_reader_thread_safe_1,checking>::
            check_element_precondition(*this);
        return *reinterpret_cast<config_reader_thread_safe_1*>(block_table.element().value());
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    bool config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    move_next (
    ) const 
    {
        auto_mutex M(*m);
        return block_table.move_next();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    unsigned long config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    size (
    ) const 
    {
        auto_mutex M(*m);
        return block_table.size();
    }

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

    template <
        typename config_reader_base,
        typename map_string_void,
        bool checking
        >
    const std::string& config_reader_thread_safe_1<config_reader_base,map_string_void,checking>::
    current_block_name (
    ) const
    {
        auto_mutex M(*m);
        config_reader_thread_safe_1_helpers::helper<config_reader_thread_safe_1,checking>::
            check_current_block_name_precondition(*this);
        return block_table.element().key();
    }

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

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

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

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

        // now fill the block table up to match what is in cr
        cr->reset();
        while (cr->move_next())
        {
            config_reader_thread_safe_1* block = new config_reader_thread_safe_1(&cr->element(),m);
            void* temp = block;
            std::string key(cr->current_block_name());
            block_table.add(key,temp);
        }
    }

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

}

#endif // DLIB_CONFIG_READER_THREAD_SAFe_