// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2012, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: Manuel Holtgrewe <manuel.holtgrewe@fu-berlin.de>
// ==========================================================================
// Metaprogramming control structures.
//
// This header defines metaprogramming control structure such as conditionals
// and loops.
// ==========================================================================

#ifndef SEQAN_CORE_INCLUDE_SEQAN_BASIC_METAPROGRAMMING_CONTROL_H_
#define SEQAN_CORE_INCLUDE_SEQAN_BASIC_METAPROGRAMMING_CONTROL_H_

namespace seqan {

// ============================================================================
// Forwards
// ============================================================================

// ============================================================================
// Tags, Classes, Enums
// ============================================================================

// ============================================================================
// Metafunctions
// ============================================================================

// ----------------------------------------------------------------------------
// Metafunction Switch;  Supporting Tags Case, NilCase.
// ----------------------------------------------------------------------------

/**
.Tag.NilCase
..cat:Metaprogramming
..summary:Metaprogramming $default:$ case expression.
..signature:NilCase
..remarks:$NilCase$ is returned by metafunction @Metafunction.Switch@ if no case matched.
..include:seqan/basic.h
..remarks:The documentation of @Metafunction.Switch@ gives an example.
..see:Tag.Case
..see:Metafunction.Switch

.Tag.Case
..cat:Metaprogramming
..summary:Metaprogramming $case$ expression.
..signature:Case<TAG[, NextCase]>
..param.TAG:
..param.NextCase:Optional next case of type @Tag.Case@, defaults to @Tag.NilCase@.
...type:Tag.NilCase
...type:Tag.Case
..include:seqan/basic.h
..remarks:The documentation of @Metafunction.Switch@ gives an example.
..see:Tag.NilCase
..see:Metafunction.Switch

.Metafunction.Switch
..cat:Metaprogramming
..summary:Metaprogramming $switch$ expression.
..signature:Switch<TAG, Case>::Type
..param.TAG:The case label.
...type:nolink:$int$
..param.Case:Cascade of $Case$ tags.
...type:Tag.Case
...type:Tag.NilCase
..returns:The selected type from the @Tag.Case@ cascade or @Tag.NilCase@.
..include:seqan/basic.h
..see:Tag.NilCase
..see:Tag.Case
..example.code:
int switchTest(Nothing const &) { return -1; }
int switchTest(False const &) { return 0; }
int switchTest(True const &) { return 1; }
int switchTest(NilCase const &) { return 2; }

template <int X>
struct SwitchTest
{
    typedef typename Switch<
        X,
        Case<-1, Nothing,
        Case<0, False,
        Case<1, True
        > > > >::Type Type;
};

SEQAN_DEFINE_TEST(test_metaprogramming_switch)
{
    typedef typename SwitchTest<-1>::Type T1;
    typedef typename SwitchTest< 0>::Type T2;
    typedef typename SwitchTest< 1>::Type T3;
    typedef typename SwitchTest< 2>::Type T4;

    std::cout << switchTest(T1()) << std::endl;  // => -1
    std::cout << switchTest(T2()) << std::endl;  // =>  0
    std::cout << switchTest(T3()) << std::endl;  // =>  1
    std::cout << switchTest(T4()) << std::endl;  // =>  2
 }
 */

const int DEFAULT_ = ~(~0u >> 1); // initialize with the smallest int

struct NilCase {};

template <int TAG, typename Type_, typename Next_ = NilCase>
struct Case
{
    enum { TAG_ = TAG };
    typedef Type_ Type;
    typedef Next_ Next;
};

template <int TAG, typename Case_>
struct Switch
{
    typedef typename Case_::Next NextCase_;
    enum
    {
        CASE_TAG_ = Case_::TAG_,
        FOUND_    = (CASE_TAG_ == TAG || CASE_TAG_ == DEFAULT_)
    };

    typedef typename IfC<FOUND_,
                         typename Case_::Type,
                         typename Switch<TAG, NextCase_>::Type
                         >::Type Type;
};

template <int TAG>
struct Switch<TAG, NilCase>
{
    typedef NilCase Type;
};

// ----------------------------------------------------------------------------
// Metafunction Loop
// ----------------------------------------------------------------------------

/**
.Metafunction.Loop:
..cat:Metaprogramming
..summary:Metafunction returning a function that iterates over a static integer range.
..signature:Loop<Worker, I>::run(Arg & arg)
..param.Worker:A worker $struct$. It has to implement the static (and preferably finline/inline) function $body$ that accepts two parameters. The first one will be a reference to $arg$, as given to $run()$.  The second will be the current value of the iterating variable.
..param.I:The upper limit for the iteration.
..param.arg:The argument to be passed into the workers' $body()$ function.
..remarks:The loop will go from 1 up to and including $I$.
..see:Metafunction.LoopReverse
..include:seqan/basic.h
..example.text:Print the values 1, 2, ..., $I-1$, $I$.
..example.code:
struct PrintWorker
{
    static inline void body(Nothing & arg, int I)
    {
        (void)arg;  // ignored
        printf("%d\n", I);
    }
};

Loop<PrintWorker, 10>::run(Nothing());
// This will print the numbers 1, 2, ..., 9, 10.
 */

// Example of a loop Worker class.  Could be removed, serves no
// purpose.
struct WorkerNothing
{
    template <typename Arg>
    static inline void body(Arg & /*arg*/, int /*I*/)
    {}
};

template <typename Worker, int I>
class Loop {
public:
    template <typename Arg>
    static inline void run(Arg & arg)
    {
        Loop<Worker, I - 1>::run(arg);
        Worker::body(arg, I);
    }
};

template <typename Worker>
class Loop<Worker, 0>
{
public:
    // end of loop
    template <typename Arg>
    static inline void run(Arg &)
    {}
};

// ----------------------------------------------------------------------------
// Metafunction LoopReverse
// ----------------------------------------------------------------------------

/**
.Metafunction.LoopReverse:
..cat:Metaprogramming
..summary:Metafunction returning a function that iterates over a static integer range in reverse order.
..signature:LoopReverse<Worker, I>::run(Arg & arg)
..param.Worker:A worker $struct$. It has to implement the static (and preferably finline/inline) function $body$ that accepts two parameters. The first one will be a reference to $arg$, as given to $run()$.  The second will be the current value of the iterating variable.
..param.I:The upper limit for the iteration.
..param.arg:The argument to be passed into the workers' $body()$ function.
..remarks:The loop will go from $I$ down to and including 1.
..include:seqan/basic.h
..see:Metafunction.Loop
..example.text:Print the values $I$, $I - 1$, ..., 2, 1.
..example.code:

struct PrintWorker
{
    static inline body(Nothing & arg, int I)
    {
        (void)arg;  // ignored
        printf("%d\n", I);
    }
};

Loop<PrintWorker, 10>::run(Nothing());
// This will print the numbers 1, 2, ..., 9, 10.
 */

template <typename Worker, int I>
class LoopReverse {
public:
    template <typename Arg>
    static inline void run(Arg & arg)
    {
        Worker::body(arg, I);
        LoopReverse<Worker, I - 1>::run(arg);
    }
};

template <typename Worker>
class LoopReverse<Worker, 0> {
  public:
    // end of loop
    template <typename Arg>
    static inline void run(Arg &) {}
};

// ============================================================================
// Functions
// ============================================================================

}  // namespace seqan

#endif  // #ifndef SEQAN_CORE_INCLUDE_SEQAN_BASIC_METAPROGRAMMING_CONTROL_H_
