//          Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under 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)

#ifndef SNOWHOUSE_CONSTRAINTLIST_H
#define SNOWHOUSE_CONSTRAINTLIST_H

#include <stack>

namespace snowhouse
{
    struct ConstraintOperator;
    using ResultStack = std::stack<bool>;
    using OperatorStack = std::stack<ConstraintOperator*>;

    template <typename HT, typename TT>
    struct ConstraintList
    {
        using HeadType = HT;
        using TailType = TT;

        ConstraintList(const HeadType& head, const TailType& tail):
            m_head(head),
            m_tail(tail)
        {
        }

        HeadType m_head;
        TailType m_tail;
    };

    struct Nil
    {
        Nil() {}

        Nil(const Nil&) {}
    };

    // ---- These structs defines the resulting types of list concatenation
    // operations
    template <typename L1, typename L2>
    struct type_concat
    {
        using t = ConstraintList<typename L1::HeadType,
            typename type_concat<typename L1::TailType, L2>::t>;
    };

    template <typename L2>
    struct type_concat<Nil, L2>
    {
        using t = L2;
    };

    template <typename L3>
    inline L3 tr_concat(const Nil&, const Nil&)
    {
        return Nil();
    }

    // ---- These structs define the concatenation operations.

    template <typename LeftList, typename RightList, typename ResultList>
    struct ListConcat
    {
        static ResultList Concatenate(
            const LeftList& left, const RightList& right)
        {
            return ResultList(left.m_head,
                ListConcat<typename LeftList::TailType,
                    RightList,
                    typename type_concat<typename LeftList::TailType,
                        RightList>::t>::Concatenate(left.m_tail, right));
        }
    };

    // Concatenating an empty list with a second list yields the second list
    template <typename RightList, typename ResultList>
    struct ListConcat<Nil, RightList, ResultList>
    {
        static ResultList Concatenate(const Nil&, const RightList& right)
        {
            return right;
        }
    };

    // Concatenating two empty lists yields an empty list
    template <typename ResultList>
    struct ListConcat<Nil, Nil, ResultList>
    {
        static ResultList Concatenate(const Nil&, const Nil&)
        {
            return Nil();
        }
    };

    // ---- The concatenation operation

    template <typename L1, typename L2>
    inline typename type_concat<L1, L2>::t Concatenate(
        const L1& list1, const L2& list2)
    {
        return ListConcat<L1, L2, typename type_concat<L1, L2>::t>::Concatenate(
            list1, list2);
    }
}

#endif
