/*=============================================================================
    Copyright (c) 2010-2011 Daniel James

    Use, modification and distribution is subject to the Boost Software
    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

// An easy way to store data parsed for quickbook.

#if !defined(BOOST_SPIRIT_QUICKBOOK_VALUES_HPP)
#define BOOST_SPIRIT_QUICKBOOK_VALUES_HPP

#include <cassert>
#include <stdexcept>
#include <string>
#include <utility>
#include <boost/iterator/iterator_traits.hpp>
#include <boost/operators.hpp>
#include <boost/scoped_ptr.hpp>
#include "files.hpp"
#include "fwd.hpp"
#include "string_view.hpp"

namespace quickbook
{
    struct value;
    struct value_builder;
    struct value_error;

    namespace detail
    {
        ////////////////////////////////////////////////////////////////////////
        // Node

        struct value_node
        {
          private:
            value_node(value_node const&);
            value_node& operator=(value_node const&);

          public:
            typedef int tag_type;

          protected:
            explicit value_node(tag_type);
            virtual ~value_node();

          public:
            virtual char const* type_name() const = 0;
            virtual value_node* clone() const = 0;

            virtual file_ptr get_file() const;
            virtual string_iterator get_position() const;
            virtual quickbook::string_view get_quickbook() const;
            virtual std::string get_encoded() const;
            virtual int get_int() const;

            virtual bool check() const;
            virtual bool empty() const;
            virtual bool is_encoded() const;
            virtual bool is_list() const;
            virtual bool equals(value_node*) const;

            virtual value_node* get_list() const;

            int ref_count_;
            const tag_type tag_;
            value_node* next_;

            friend void intrusive_ptr_add_ref(value_node* ptr)
            {
                ++ptr->ref_count_;
            }
            friend void intrusive_ptr_release(value_node* ptr)
            {
                if (--ptr->ref_count_ == 0) delete ptr;
            }
        };

        ////////////////////////////////////////////////////////////////////////
        // Value base
        //
        // This defines most of the public methods for value.
        // 'begin' and 'end' are defined with the iterators later.

        struct value_base
        {
          public:
            struct iterator;

            typedef iterator const_iterator;
            typedef value_node::tag_type tag_type;
            enum
            {
                default_tag = 0
            };

          protected:
            explicit value_base(value_node* base) : value_(base)
            {
                assert(value_);
            }

            ~value_base() {}

            void swap(value_base& x) { std::swap(value_, x.value_); }

          public:
            bool check() const { return value_->check(); }
            bool empty() const { return value_->empty(); }
            bool is_encoded() const { return value_->is_encoded(); }
            bool is_list() const { return value_->is_list(); }

            iterator begin() const;
            iterator end() const;

            // Item accessors
            tag_type get_tag() const { return value_->tag_; }
            file_ptr get_file() const { return value_->get_file(); }
            string_iterator get_position() const
            {
                return value_->get_position();
            }
            quickbook::string_view get_quickbook() const
            {
                return value_->get_quickbook();
            }
            std::string get_encoded() const { return value_->get_encoded(); }
            int get_int() const { return value_->get_int(); }

            // Equality is pretty inefficient. Not really designed for anything
            // more than testing purposes.
            friend bool operator==(value_base const& x, value_base const& y)
            {
                return x.value_->equals(y.value_);
            }

          protected:
            value_node* value_;

            // value_builder needs to access 'value_' to get the node
            // from a value.
            friend struct quickbook::value_builder;
        };

        ////////////////////////////////////////////////////////////////////////
        // Reference and proxy values for use in iterators

        struct value_ref : public value_base
        {
          public:
            explicit value_ref(value_node* base) : value_base(base) {}
        };

        struct value_proxy : public value_base
        {
          public:
            explicit value_proxy(value_node* base) : value_base(base) {}
            value_proxy* operator->() { return this; }
            value_ref operator*() const { return value_ref(value_); }
        };

        ////////////////////////////////////////////////////////////////////////
        // Iterators

        struct value_base::iterator : public boost::forward_iterator_helper<
                                          iterator,
                                          value,
                                          int,
                                          value_proxy,
                                          value_ref>
        {
          public:
            iterator();
            explicit iterator(value_node* p) : ptr_(p) {}
            friend bool operator==(iterator x, iterator y)
            {
                return x.ptr_ == y.ptr_;
            }
            iterator& operator++()
            {
                ptr_ = ptr_->next_;
                return *this;
            }
            value_ref operator*() const { return value_ref(ptr_); }
            value_proxy operator->() const { return value_proxy(ptr_); }

          private:
            value_node* ptr_;
        };

        inline value_base::iterator value_base::begin() const
        {
            return iterator(value_->get_list());
        }

        inline value_base::iterator value_base::end() const
        {
            return iterator();
        }

        ////////////////////////////////////////////////////////////////////////
        // Reference counting for values

        struct value_counted : public value_base
        {
            value_counted& operator=(value_counted const&);

          protected:
            value_counted();
            value_counted(value_counted const&);
            value_counted(value_base const&);
            value_counted(value_node*);
            ~value_counted();
        };

        ////////////////////////////////////////////////////////////////////////
        // List builder
        //
        // Values are immutable, so this class is used to build a list of
        // value nodes before constructing the value.

        struct value_list_builder
        {
            value_list_builder(value_list_builder const&);
            value_list_builder& operator=(value_list_builder const&);

          public:
            value_list_builder();
            value_list_builder(value_node*);
            ~value_list_builder();
            void swap(value_list_builder& b);
            value_node* release();

            void append(value_node*);
            void sort();

            bool empty() const;

          private:
            value_node* head_;
            value_node** back_;
        };
    }

    ////////////////////////////////////////////////////////////////////////////
    // Value
    //
    // Most of the methods are in value_base.

    struct value : public detail::value_counted
    {
      public:
        value();
        value(value const&);
        value(detail::value_base const&);
        explicit value(detail::value_node*);
        value& operator=(value);
        void swap(value& x) { detail::value_counted::swap(x); }
    };

    // Empty
    value empty_value(value::tag_type = value::default_tag);

    // Integers
    value int_value(int, value::tag_type = value::default_tag);

    // String types

    // Quickbook strings contain a reference to the original quickbook source.
    value qbk_value(
        file_ptr const&,
        string_iterator,
        string_iterator,
        value::tag_type = value::default_tag);

    // Encoded strings are either plain text or boostbook.
    value encoded_value(
        std::string const&, value::tag_type = value::default_tag);

    // An encoded quickbook string is an encoded string that contains a
    // reference to the quickbook source it was generated from.
    value encoded_qbk_value(
        file_ptr const&,
        string_iterator,
        string_iterator,
        std::string const&,
        value::tag_type = value::default_tag);

    ////////////////////////////////////////////////////////////////////////////
    // Value Builder
    //
    // Used to incrementally build a valueeter tree.

    struct value_builder
    {
      public:
        value_builder();
        void swap(value_builder& b);

        void save();
        void restore();

        value release();

        void insert(value const&);
        void extend(value const&);

        void start_list(value::tag_type = value::default_tag);
        void finish_list();
        void clear_list();
        void sort_list();

        bool empty() const;

      private:
        detail::value_list_builder current;
        value::tag_type list_tag;
        boost::scoped_ptr<value_builder> saved;
    };

    ////////////////////////////////////////////////////////////////////////////
    // Value Error
    //

    struct value_error : public std::logic_error
    {
      public:
        explicit value_error(std::string const&);
    };

    ////////////////////////////////////////////////////////////////////////////
    // Value Consumer
    //
    // Convenience class for unpacking value values.

    struct value_consumer
    {
      public:
        struct iterator : public boost::input_iterator_helper<
                              iterator,
                              boost::iterator_value<value::iterator>::type,
                              boost::iterator_difference<value::iterator>::type,
                              boost::iterator_pointer<value::iterator>::type,
                              boost::iterator_reference<value::iterator>::type>
        {
          public:
            iterator();
            explicit iterator(value::iterator* p) : ptr_(p) {}
            friend bool operator==(iterator x, iterator y)
            {
                return *x.ptr_ == *y.ptr_;
            }
            iterator& operator++()
            {
                ++*ptr_;
                return *this;
            }
            reference operator*() const { return **ptr_; }
            pointer operator->() const { return ptr_->operator->(); }

          private:
            value::iterator* ptr_;
        };

        typedef iterator const_iterator;
        typedef iterator::reference reference;

        value_consumer(value const& x)
            : list_(x), pos_(x.begin()), end_(x.end())
        {
        }

        value_consumer(reference x) : list_(x), pos_(x.begin()), end_(x.end())
        {
        }

        reference consume()
        {
            assert_check();
            return *pos_++;
        }

        reference consume(value::tag_type t)
        {
            assert_check(t);
            return *pos_++;
        }

        value optional_consume()
        {
            if (check()) {
                return *pos_++;
            }
            else {
                return value();
            }
        }

        value optional_consume(value::tag_type t)
        {
            if (check(t)) {
                return *pos_++;
            }
            else {
                return value();
            }
        }

        bool check() const { return pos_ != end_; }

        bool check(value::tag_type t) const
        {
            return pos_ != end_ && t == pos_->get_tag();
        }

        void finish() const
        {
            if (pos_ != end_) throw value_error("Not all values handled.");
        }

        iterator begin() { return iterator(&pos_); }
        iterator end() { return iterator(&end_); }

      private:
        void assert_check() const
        {
            if (pos_ == end_)
                throw value_error("Attempt to read past end of value list.");
        }

        void assert_check(value::tag_type t) const
        {
            assert_check();
            if (t != pos_->get_tag()) throw value_error("Incorrect value tag.");
        }

        value list_;
        value::iterator pos_, end_;
    };
}

#endif
