// Copyright (C) 2012 Massachusetts Institute of Technology, Lincoln Laboratory
// License: Boost Software License   See LICENSE.txt for the full license.
// Authors: Davis E. King (davis@dlib.net)
#ifndef MIT_LL_CALL_MATLAB_H__
#define MIT_LL_CALL_MATLAB_H__

#include <string>

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

template <typename T> 
struct output_decorator
{
    output_decorator(T& item_):item(item_){}
    T& item;
};

template <typename T>
output_decorator<T> returns(T& item) { return output_decorator<T>(item); }
/*!
    ensures
        - decorates item as an output type.  This stuff is used by the call_matlab()
          functions to tell if an argument is an input to the function or is supposed
          to be bound to one of the return arguments.
!*/

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

struct function_handle
{
    /*!
        WHAT THIS OBJECT REPRESENTS
            This type is used to represent function handles passed from MATLAB into a
            mex function.  You can call the function referenced by the handle by
            saying:
                call_matlab(my_handle);
    !*/

    // These two lines are just implementation details, ignore them.
    function_handle():h(0){}
    void* const h;
};

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

void call_matlab (
    const std::string& function_name
);
/*!
    ensures
        - Calls MATLAB's function of the given name
!*/

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

void call_matlab (
    const function_handle& funct 
);
/*!
    ensures
        - Calls MATLAB's function represented by the handle funct
!*/

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

template <
    typename T1
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1
);
/*!
    ensures
        - calls MATLAB's function of the given name.  
        - if (A1 is not decorated as an output by returns()) then
            - A1 is passed as an argument into the MATLAB function
        - else
            - A1 is treated as the first return value from the MATLAB function.
!*/

template <
    typename T1
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1
) { call_matlab("feval", funct, A1); }
/*!
    ensures
        - Calls MATLAB's function represented by the handle funct
        - if (A1 is not decorated as an output by returns()) then
            - A1 is passed as an argument into the MATLAB function
        - else
            - A1 is treated as the first return value from the MATLAB function.
!*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/*
    The rest of this file is just overloads of call_matlab() for up to 10 arguments (or
    just 9 arguments if function_handle is used).  They all do the same thing as the above 
    version of call_matlab().  Generally, any argument not decorated by returns() is an 
    input to the MATLAB function.  On the other hand, all arguments decorated by returns() 
    are treated as outputs.  
*/
// ----------------------------------------------------------------------------------------

template <
    typename T1, 
    typename T2
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2
);

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

template <
    typename T1, 
    typename T2,
    typename T3
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4,
    typename T5
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7,
    typename T8
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7,
    const T8& A8
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7,
    typename T8,
    typename T9
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7,
    const T8& A8,
    const T9& A9
);

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

template <
    typename T1, 
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7,
    typename T8,
    typename T9,
    typename T10
    >
void call_matlab (
    const std::string& function_name,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7,
    const T8& A8,
    const T9& A9,
    const T10& A10
);

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

template <
    typename T1,
    typename T2
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2
)
{
    call_matlab("feval", funct, A1, A2);
}

template <
    typename T1,
    typename T2,
    typename T3
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3
)
{
    call_matlab("feval", funct, A1, A2, A3);
}

template <
    typename T1,
    typename T2,
    typename T3,
    typename T4
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4
)
{
    call_matlab("feval", funct, A1, A2, A3, A4);
}

template <
    typename T1,
    typename T2,
    typename T3,
    typename T4,
    typename T5
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5
)
{
    call_matlab("feval", funct, A1, A2, A3, A4, A5);
}

template <
    typename T1,
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6
)
{
    call_matlab("feval", funct, A1, A2, A3, A4, A5, A6);
}

template <
    typename T1,
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7
)
{
    call_matlab("feval", funct, A1, A2, A3, A4, A5, A6, A7);
}

template <
    typename T1,
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7,
    typename T8
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7,
    const T8& A8
)
{
    call_matlab("feval", funct, A1, A2, A3, A4, A5, A6, A7, A8);
}

template <
    typename T1,
    typename T2,
    typename T3,
    typename T4,
    typename T5,
    typename T6,
    typename T7,
    typename T8,
    typename T9
    >
void call_matlab (
    const function_handle& funct,
    const T1& A1,
    const T2& A2,
    const T3& A3,
    const T4& A4,
    const T5& A5,
    const T6& A6,
    const T7& A7,
    const T8& A8,
    const T9& A9
)
{
    call_matlab("feval", funct, A1, A2, A3, A4, A5, A6, A7, A8, A9);
}

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

#endif // MIT_LL_CALL_MATLAB_H__