/** MxArray and ConstMap declaration.
 * @file MxArray.hpp
 * @author Kota Yamaguchi
 * @date 2012
 */
#ifndef __MXARRAY_HPP__
#define __MXARRAY_HPP__

#include <functional>
#include <map>
#include <stdint.h>
#include <string>
#include "mex.h"
#include "opencv2/opencv.hpp"

/** mxArray object wrapper for data conversion and manipulation.
 */
class MxArray
{
  public:
    /** MxArray constructor from mxArray*.
     * @param arr mxArray pointer given by mexFunction.
     */
    MxArray(const mxArray *arr) : p_(arr) {}
    /** Copy constructor.
     * @param arr Another MxArray.
     */
    MxArray(const MxArray& arr) : p_(arr.p_) {}
    /** Assignment operator.
     */
    MxArray& operator=(const MxArray& rhs);
    /** MxArray constructor from int.
     * @param i int value.
     */
    explicit MxArray(const int i);
    /** MxArray constructor from double.
     * @param d double value.
     */
    explicit MxArray(const double d);
    /** MxArray constructor from bool.
     * @param b bool value.
     */
    explicit MxArray(const bool b);
    /** MxArray constructor from std::string.
     * @param s reference to a string value.
     */
    explicit MxArray(const std::string& s);
    /** Convert cv::Mat to MxArray.
     * @param mat cv::Mat object.
     * @param classid classid of mxArray. e.g., mxDOUBLE_CLASS. When
     *                mxUNKNOWN_CLASS is specified, classid will be
     *                automatically determined from the type of cv::Mat.
     *                default: mxUNKNOWN_CLASS.
     * @param transpose Optional transposition to the return value so that rows
     *                  and columns of the 2D Mat are mapped to the 2nd and 1st
     *                  dimensions in MxArray, respectively. This does not
     *                  apply the N-D array conversion. default true.
     * @return MxArray object.
     *
     * Convert cv::Mat object to an MxArray. When the cv::Mat object is 2D, the
     * width, height, and channels are mapped to the first, second, and third
     * dimensions of the MxArray unless transpose flag is set to false. When
     * the cv::Mat object is N-D, (dim 1, dim 2,...dim N, channels) are mapped
     * to (dim 2, dim 1, ..., dim N, dim N+1), respectively.
     *
     * Example:
     * @code
     * cv::Mat x(120, 90, CV_8UC3, Scalar(0));
     * mxArray* plhs[0] = MxArray(x);
     * @endcode
     */
    explicit MxArray(const cv::Mat& mat,
                     mxClassID classid=mxUNKNOWN_CLASS,
                     bool transpose=true);
    /** Convert float cv::SparseMat to MxArray.
     * @param mat cv::SparseMat object.
     * @return MxArray object.
     */
    explicit MxArray(const cv::SparseMat& mat);
    /** Convert cv::Moments to MxArray.
     * @param m cv::Moments object.
     * @return MxArray object.
     */
    explicit MxArray(const cv::Moments& m);
    /** Convert cv::KeyPoint to MxArray.
     * @param p cv::KeyPoint object.
     * @return MxArray object.
     */
    explicit MxArray(const cv::KeyPoint& p);
    /** MxArray constructor from vector<KeyPoint>. Make a cell array.
     * @param v vector of KeyPoint.
     */
    explicit MxArray(const std::vector<cv::KeyPoint>& v);
    /** Convert cv::DMatch to MxArray.
     * @param m cv::DMatch object.
     * @return MxArray object.
     */
    explicit MxArray(const cv::DMatch& m);
    /** MxArray constructor from vector<T>. Make a cell array.
     * @param v vector of type T.
     */
    explicit MxArray(const std::vector<cv::DMatch>& v);
    /** Convert cv::RotatedRect to MxArray.
     * @param m cv::RotatedRect object.
     * @return MxArray object.
     */
    explicit MxArray(const cv::RotatedRect& m);
    /** Convert cv::TermCriteria to MxArray.
     * @param t cv::TermCriteria object.
     * @return MxArray object.
     */
    explicit MxArray(const cv::TermCriteria& t);
    /** MxArray constructor from vector<T>. Make a cell array.
     * @param v vector of type T.
     */
    template <typename T> explicit MxArray(const std::vector<T>& v);
    /** MxArray constructor from cv::Point_<T>.
     * @param p cv::Point_<T> object.
     * @return two-element numeric MxArray.
     */
    template <typename T> explicit MxArray(const cv::Point_<T>& p);
    /** MxArray constructor from cv::Point3_<T>.
     * @param p cv::Point3_<T> object.
     * @return three-element numeric MxArray.
     */
    template <typename T> explicit MxArray(const cv::Point3_<T>& p);
    /** MxArray constructor from cv::Size_<T>.
     * @return two-element numeric MxArray.
     */
    template <typename T> explicit MxArray(const cv::Size_<T>& s);
    /** MxArray constructor from cv::Rect_<T>.
     * @return four-element numeric MxArray [x, y, width, height].
     */
    template <typename T> explicit MxArray(const cv::Rect_<T>& r);
    /** MxArray constructor from cv::Scalar_<T>.
     * @return four-element numeric MxArray.
     */
    template <typename T> explicit MxArray(const cv::Scalar_<T>& s);
    /** Generic constructor for a struct array.
     * @param fields field names.
     * @param nfields number of field names.
     * @param m size of the first dimension.
     * @param n size of the second dimension.
     *
     * Example:
     * @code
     * const char* fields[] = {"field1", "field2"};
     * MxArray m(fields, 2);
     * m.set("field1", 1);
     * m.set("field2", "field2 value");
     * @endcode
     */
    MxArray(const char**fields, int nfields, int m=1, int n=1);
    /** Destructor. This does not free the underlying mxArray*.
     */
    virtual ~MxArray() {}
    /** Create a new cell array.
     * @param m Number of rows.
     * @param n Number of cols.
     */
    static inline MxArray Cell(int m=1, int n=1)
    {
        return MxArray(mxCreateCellMatrix(m,n));
    }
    /** Create a new struct array.
     * @param fields Field names.
     * @param nfields Number of fields.
     * @param m Number of rows.
     * @param n Number of cols.
     */
    static inline MxArray Struct(const char**fields=NULL,
                                 int nfields=0,
                                 int m=1,
                                 int n=1)
    {
        return MxArray(mxCreateStructMatrix(m, n, nfields, fields));
    }
    /** Clone mxArray. This allocates new mxArray*.
     * @return MxArray object.
     */
    MxArray clone() { return MxArray(mxDuplicateArray(p_)); }
    /** Destroy allocated mxArray. Use this to destroy a temporary mxArray not
     *  to be used in matlab.
     * @return newly allocated MxArray object.
     */
    void destroy() { mxDestroyArray(const_cast<mxArray*>(p_)); }
    /** Implicit conversion to const mxArray*.
     * @return const mxArray* pointer.
     */
    operator const mxArray*() const { return p_; };
    /** Implicit conversion to mxArray*. Be careful that this internally cast
     *  away mxArray* constness.
     * @return mxArray* pointer.
     */
    operator mxArray*() const { return const_cast<mxArray*>(p_); };
    /** Convert MxArray to int.
     * @return int value.
     */
    int toInt() const;
    /** Convert MxArray to double.
     * @return double value.
     */
    double toDouble() const;
    /** Convert MxArray to bool.
     * @return bool value.
     */
    bool toBool() const;
    /** Convert MxArray to std::string.
     * @return std::string value.
     */
    std::string toString() const;
    /** Convert MxArray to cv::Mat.
     * @param depth depth of cv::Mat. e.g., CV_8U, CV_32F.  When CV_USERTYPE1
     *                is specified, depth will be automatically determined from
     *                the classid of the MxArray. default: CV_USERTYPE1.
     * @param transpose Optional transposition to the return value so that rows
     *                  and columns of the 2D Mat are mapped to the 2nd and 1st
     *                  dimensions in MxArray, respectively. This does not
     *                  apply the N-D array conversion. default true.
     * @return cv::Mat object.
     *
     * Convert a MxArray object to a cv::Mat object. When the dimensionality of
     * the MxArray is more than 2, the last dimension will be mapped to the
     * channels of the cv::Mat. Also, if the resulting cv::Mat is 2D, the 1st
     * and 2nd dimensions of the MxArray are mapped to rows and columns of the
     * cv::Mat unless transpose flag is false. That is, when MxArray is 3D,
     * (dim 1, dim 2, dim 3) are mapped to (cols, rows, channels) of the
     * cv::Mat by default, whereas if MxArray is more than 4D, (dim 1, dim 2,
     * ..., dim N-1, dim N) are mapped to (dim 2, dim 1, ..., dim N-1,
     * channels) of the cv::Mat, respectively.
     *
     * Example:
     * @code
     * cv::Mat x(MxArray(prhs[0]).toMat());
     * @endcode
     */
    cv::Mat toMat(int depth=CV_USRTYPE1, bool transpose=true) const;
    /** Convert MxArray to a single-channel cv::Mat.
     * @param depth depth of cv::Mat. e.g., CV_8U, CV_32F.  When CV_USERTYPE1
     *              is specified, depth will be automatically determined from
     *              the the classid of the MxArray. default: CV_USERTYPE1.
     * @param transpose Optional transposition to the return value so that rows
     *                  and columns of the 2D Mat are mapped to the 2nd and 1st
     *                  dimensions in MxArray, respectively. This does not
     *                  apply the N-D array conversion. default true.
     * @return const cv::Mat object.
     *
     * Convert a MxArray object to a single-channel cv::Mat object. If the
     * MxArray is 2D, the 1st and 2nd dimensions of the MxArray are mapped to
     * rows and columns of the cv::Mat unless transpose flag is false. If the
     * MxArray is more than 3D, the 1st and 2nd dimensions of the MxArray are
     * mapped to 2nd and 1st dimensions of the cv::Mat. That is, when MxArray
     * is 2D, (dim 1, dim 2) are mapped to (cols, rows) of the cv::Mat by
     * default, whereas if MxArray is more than 3D, (dim 1, dim 2, dim 3, ...,
     * dim N) are mapped to (dim 2, dim 1, dim 3, ..., dim N) of the cv::Mat,
     * respectively.
     *
     * Example:
     * @code
     * cv::Mat x(MxArray(prhs[0]).toMatND());
     * @endcode
     */
    cv::MatND toMatND(int depth=CV_USRTYPE1, bool transpose=true) const;
    /** Convert double sparse MxArray to float cv::SparseMat.
     * @return cv::SparseMat object.
     */
    cv::SparseMat toSparseMat() const;
    /** Convert MxArray to cv::Moments.
     * @param index index of the struct array.
     * @return cv::Moments.
     */
    cv::Moments toMoments(mwIndex index=0) const;
    /** Convert MxArray to cv::KeyPoint.
     * @param index index of the struct array.
     * @return cv::KeyPoint.
     */
    cv::KeyPoint toKeyPoint(mwIndex index=0) const;
    /** Convert MxArray to cv::DMatch.
     * @param index index of the struct array.
     * @return cv::DMatch.
     */
    cv::DMatch toDMatch(mwIndex index=0) const;
    /** Convert MxArray to cv::Range.
     * @return cv::Range.
     */
    cv::Range toRange() const;
    /** Convert MxArray to cv::TermCriteria.
     * @param index index of the struct array.
     * @return cv::TermCriteria.
     */
    cv::TermCriteria toTermCriteria(mwIndex index=0) const;
    /** Convert MxArray to Point_<T>.
     * @return cv::Point_<T> value.
     */
    template <typename T> cv::Point_<T> toPoint_() const;
    /** Convert MxArray to Point3_<T>.
     * @return cv::Poin3_<T> value.
     */
    template <typename T> cv::Point3_<T> toPoint3_() const;
    /** Convert MxArray to Size_<T>.
     * @return cv::Size_<T> value.
     */
    template <typename T> cv::Size_<T> toSize_() const;
    /** Convert MxArray to Rect_<T>.
     * @return cv::Rect_<T> value.
     */
    template <typename T> cv::Rect_<T> toRect_() const;
    /** Convert MxArray to Scalar_<T>.
     * @return cv::Scalar_<T> value.
     */
    template <typename T> cv::Scalar_<T> toScalar_() const;
    /** Convert MxArray to std::vector<T> for a primitive type.
     * @return std::vector<T> value.
     *
     * The method is intended for conversion to a raw numeric vector such
     * as std::vector<int> or std::vector<double>. Example:
     *
     * @code
     * MxArray numArray(prhs[0]);
     * vector<double> vd = numArray.toVector<double>();
     * @endcode
     */
    template <typename T> std::vector<T> toVector() const;
    /** Convert MxArray to std::vector<T> by a specified conversion method.
     * @param f member function of MxArray (e.g., &MxArray::toMat,
     *          &MxArray::toInt).
     * @return std::vector<T> value.
     *
     * The method constructs std::vector<T> by applying conversion method f to
     * each cell array element. This is similar to std::transform function. An
     * example usage is shown below:
     *
     * @code
     * MxArray cellArray(prhs[0]);
     * const_mem_fun_ref_t<Point3i,MxArray> converter(&MxArray::toPoint3_<int>);
     * vector<Point3i> v = cellArray.toVector(converter);
     * @endcode
     */
    template <typename T>
    std::vector<T> toVector(std::const_mem_fun_ref_t<T, MxArray> f) const;
    /** Alias to toPoint_<int>.
     */
    inline cv::Point toPoint() const { return toPoint_<int>(); }
    /** Alias to toPoint_<float>.
     */
    inline cv::Point2f toPoint2f() const { return toPoint_<float>(); }
    /** Alias to toPoint3_<float>.
     */
    inline cv::Point3f toPoint3f() const { return toPoint3_<float>(); }
    /** Alias to toSize_<int>.
     */
    inline cv::Size toSize() const { return toSize_<int>(); }
    /** Alias to toRect_<int>.
     */
    inline cv::Rect toRect() const { return toRect_<int>(); }
    /** Alias to toScalar_<double>
     */
    inline cv::Scalar toScalar() const { return toScalar_<double>(); }   

    /** Class ID of mxArray.
     */
    inline mxClassID classID() const { return mxGetClassID(p_); }
    /** Class name of mxArray.
     */
    inline const std::string className() const
    {
        return std::string(mxGetClassName(p_));
    }
    /** Number of elements in an array.
     */
    inline mwSize numel() const { return mxGetNumberOfElements(p_); }
    /** Number of dimensions.
     */
    inline mwSize ndims() const { return mxGetNumberOfDimensions(p_); }
    /** Array of each dimension.
     */
    inline const mwSize* dims() const { return mxGetDimensions(p_); };
    /** Number of rows in an array.
     */
    inline mwSize rows() const { return mxGetM(p_); }
    /** Number of columns in an array.
     */
    inline mwSize cols() const { return mxGetN(p_); }
    /** Number of fields in a struct array.
     */
    inline int nfields() const { return mxGetNumberOfFields(p_); }
    /** Get field name of a struct array.
     * @param index index of the struct array.
     * @return std::string.
     */
    std::string fieldname(int index=0) const;
    /** Get field names of a struct array.
     * @return std::string.
     */
    std::vector<std::string> fieldnames() const;
    /** Number of elements in IR, PR, and PI arrays.
     */
    inline mwSize nzmax() const { return mxGetNzmax(p_); }
    /** Offset from first element to desired element.
     * @param i index of the first dimension of the array.
     * @param j index of the second dimension of the array.
     * @return linear offset of the specified subscript index.
     */
    mwIndex subs(mwIndex i, mwIndex j=0) const;
    /** Offset from first element to desired element.
     * @param si subscript index of the array.
     * @return linear offset of the specified subscript index.
     */
    mwIndex subs(const std::vector<mwIndex>& si) const;
    /** Determine whether input is cell array.
     */
    inline bool isCell() const { return mxIsCell(p_); }
    /** Determine whether input is string array.
     */
    inline bool isChar() const { return mxIsChar(p_); }
    /** Determine whether array is member of specified class.
     */
    inline bool isClass(std::string s) const
    {
        return mxIsClass(p_, s.c_str());
    }
    /** Determine whether data is complex.
     */
    inline bool isComplex() const { return mxIsComplex(p_); }
    /** Determine whether mxArray represents data as double-precision,
     * floating-point numbers.
     */
    inline bool isDouble() const { return mxIsDouble(p_); }
    /** Determine whether array is empty.
     */
    inline bool isEmpty() const { return mxIsEmpty(p_); }
    /** Determine whether input is finite.
     */
    static inline bool isFinite(double d) { return mxIsFinite(d); }
    /** Determine whether array was copied from MATLAB global workspace.
     */
    inline bool isFromGlobalWS() const { return mxIsFromGlobalWS(p_); };
    /** Determine whether input is infinite.
     */
    static inline bool isInf(double d) { return mxIsInf(d); }
    /** Determine whether array represents data as signed 8-bit integers.
     */
    inline bool isInt8() const { return mxIsInt8(p_); }
    /** Determine whether array represents data as signed 16-bit integers.
     */
    inline bool isInt16() const { return mxIsInt16(p_); }
    /** Determine whether array represents data as signed 32-bit integers.
     */
    inline bool isInt32() const { return mxIsInt32(p_); }
    /** Determine whether array represents data as signed 64-bit integers.
     */
    inline bool isInt64() const { return mxIsInt64(p_); }
    /** Determine whether array is of type mxLogical.
     */
    inline bool isLogical() const { return mxIsLogical(p_); }
    /** Determine whether scalar array is of type mxLogical.
     */
    inline bool isLogicalScalar() const { return mxIsLogicalScalar(p_); }
    /** Determine whether scalar array of type mxLogical is true.
     */
    inline bool isLogicalScalarTrue() const
    {
        return mxIsLogicalScalarTrue(p_);
    }
    /** Determine whether array is numeric.
     */
    inline bool isNumeric() const { return mxIsNumeric(p_); }
    /** Determine whether array represents data as single-precision,
     * floating-point numbers.
     */
    inline bool isSingle() const { return mxIsSingle(p_); }
    /** Determine whether input is sparse array.
     */
    inline bool isSparse() const { return mxIsSparse(p_); }
    /** Determine whether input is structure array.
     */
    inline bool isStruct() const { return mxIsStruct(p_); }
    /** Determine whether array represents data as unsigned 8-bit integers.
     */
    inline bool isUint8() const { return mxIsUint8(p_); }
    /** Determine whether array represents data as unsigned 16-bit integers.
     */
    inline bool isUint16() const { return mxIsUint16(p_); }
    /** Determine whether array represents data as unsigned 32-bit integers.
     */
    inline bool isUint32() const { return mxIsUint32(p_); }
    /** Determine whether array represents data as unsigned 64-bit integers.
     */
    inline bool isUint64() const { return mxIsUint64(p_); }
    /** Determine whether a struct array has a specified field.
     */
    bool isField(const std::string& fieldName, mwIndex index=0) const
    {
        return isStruct() &&
               mxGetField(p_, index, fieldName.c_str()) != NULL;
    }
    /** Template for element accessor.
     * @param index index of the array element.
     * @return value of the element at index.
     *
     * Example:
     * @code
     * MxArray m(prhs[0]);
     * double d = m.at<double>(0);
     * @endcode
     */
    template <typename T> T at(mwIndex index) const;
    /** Template for element accessor.
     * @param i index of the first dimension.
     * @param j index of the second dimension.
     * @return value of the element at (i,j).
     */
    template <typename T> T at(mwIndex i, mwIndex j) const;
    /** Template for element accessor.
     * @param si subscript index of the element.
     * @return value of the element at subscript index.
     */
    template <typename T> T at(const std::vector<mwIndex>& si) const;
    /** Struct element accessor.
     * @param fieldName field name of the struct array.
     * @param index index of the struct array.
     * @return value of the element at the specified field.
     */
    MxArray at(const std::string& fieldName, mwIndex index=0) const;
    /** Template for element write accessor.
     * @param index offset of the array element.
     * @param value value of the field.
     */
    template <typename T> void set(mwIndex index, const T& value);
    /** Template for element write accessor.
     * @param i index of the first dimension of the array element.
     * @param j index of the first dimension of the array element.
     * @param value value of the field.
     */
    template <typename T> void set(mwIndex i, mwIndex j, const T& value);
    /** Template for element write accessor.
     * @param si subscript index of the element.
     * @param value value of the field.
     */
    template <typename T> void set(const std::vector<mwIndex>& si,
                                   const T& value);
    /** Template for struct element write accessor.
     * @param fieldName field name of the struct array.
     * @param value value of the field.
     * @param index linear index of the struct array element.
     */
    template <typename T> void set(const std::string& fieldName,
                                   const T& value,
                                   mwIndex index=0);
    /** Determine whether input is NaN (Not-a-Number).
     */
    static inline bool isNaN(double d) { return mxIsNaN(d); }
    /** Value of infinity.
     */
    static inline double Inf() { return mxGetInf(); }
    /** Value of NaN (Not-a-Number).
     */
    static inline double NaN() { return mxGetNaN(); }
    /** Value of EPS.
     */
    static inline double Eps() { return mxGetEps(); }
  private:
    /** mxArray c object.
     */
    const mxArray* p_;
};

/** std::map wrapper with one-line initialization and lookup method.
 * Initialization
 * @code
 * const ConstMap<std::string,int> BorderType = ConstMap<std::string,int>
 *     ("Replicate",  cv::BORDER_REPLICATE)
 *     ("Constant",   cv::BORDER_CONSTANT)
 *     ("Reflect",    cv::BORDER_REFLECT);
 * @endcode
 * Lookup
 * @code
 * BorderType["Constant"] // => cv::BORDER_CONSTANT
 * @endcode
 */
template <typename T, typename U>
class ConstMap
{
  public:
    /// Constructor with a single key-value pair
    ConstMap(const T& key, const U& val)
    {
        m_[key] = val;
    }
    /// Consecutive insertion operator
    ConstMap<T, U>& operator()(const T& key, const U& val)
    {
        m_[key] = val;
        return *this;
    }
    /// Implicit converter to std::map
    operator std::map<T, U>() { return m_; }
    /// Lookup operator; fail if not found
    U operator [](const T& key) const
    {
        typename std::map<T,U>::const_iterator it = m_.find(key);
        if (it==m_.end())
            mexErrMsgIdAndTxt("mexopencv:error", "Value not found");
        return (*it).second;
    }
  private:
    std::map<T, U> m_;
};

template <typename T>
MxArray::MxArray(const std::vector<T>& v) : p_(mxCreateCellMatrix(1, v.size()))
{
    if (!p_)
        mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
    for (int i = 0; i < v.size(); ++i)
        mxSetCell(const_cast<mxArray*>(p_), i, MxArray(v[i]));
}

template <typename T>
MxArray::MxArray(const cv::Point_<T>& p) :
    p_(mxCreateNumericMatrix(1, 2, mxDOUBLE_CLASS,mxREAL))
{
    if (!p_)
        mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
    double *x = mxGetPr(p_);
    x[0] = p.x;
    x[1] = p.y;
}

template <typename T>
MxArray::MxArray(const cv::Point3_<T>& p) :
    p_(mxCreateNumericMatrix(1, 3, mxDOUBLE_CLASS,mxREAL))
{
    if (!p_)
        mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
    double *x = mxGetPr(p_);
    x[0] = p.x;
    x[1] = p.y;
    x[2] = p.z;
}

template <typename T>
MxArray::MxArray(const cv::Size_<T>& s) :
    p_(mxCreateNumericMatrix(1, 2, mxDOUBLE_CLASS, mxREAL))
{
    if (!p_)
        mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
    double *x = mxGetPr(p_);
    x[0] = s.width;
    x[1] = s.height;
}

template <typename T>
MxArray::MxArray(const cv::Rect_<T>& r) :
    p_(mxCreateNumericMatrix(1, 4, mxDOUBLE_CLASS,mxREAL))
{
    if (!p_)
        mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
    double *x = mxGetPr(p_);
    x[0] = r.x;
    x[1] = r.y;
    x[2] = r.width;
    x[3] = r.height;
}

template <typename T>
MxArray::MxArray(const cv::Scalar_<T>& s) :
    p_(mxCreateNumericMatrix(1, 4, mxDOUBLE_CLASS,mxREAL))
{
    if (!p_)
        mexErrMsgIdAndTxt("mexopencv:error", "Allocation error");
    double *x = mxGetPr(p_);
    x[0] = s[0];
    x[1] = s[1];
    x[2] = s[2];
    x[3] = s[3];
}

template <typename T>
cv::Point_<T> MxArray::toPoint_() const
{
    if (!isNumeric() || numel()!=2)
        mexErrMsgIdAndTxt("mexopencv:error", "MxArray is not a Point");
    return cv::Point_<T>(at<T>(0), at<T>(1));
}

template <typename T>
cv::Point3_<T> MxArray::toPoint3_() const
{
    if (!isNumeric() || numel()!=3)
        mexErrMsgIdAndTxt("mexopencv:error", "MxArray is not a Point");
    return cv::Point3_<T>(at<T>(0), at<T>(1), at<T>(2));
}

template <typename T>
cv::Size_<T> MxArray::toSize_() const
{
    if (!isNumeric() || numel()!=2)
        mexErrMsgIdAndTxt("mexopencv:error",
                          "MxArray is incompatible to cv::Size");
    return cv::Size_<T>(at<T>(0), at<T>(1));
}

template <typename T>
cv::Rect_<T> MxArray::toRect_() const
{
    if (!isNumeric() || numel()!=4)
        mexErrMsgIdAndTxt("mexopencv:error",
                          "MxArray is incompatible to cv::Rect");
    return cv::Rect_<T>(at<T>(0), at<T>(1), at<T>(2), at<T>(3));
}

template <typename T>
cv::Scalar_<T> MxArray::toScalar_() const
{
    int n = numel();
    if (!isNumeric() || n < 1 || 4 < n)
        mexErrMsgIdAndTxt("mexopencv:error",
                          "MxArray is incompatible to cv::Scalar");
    switch (n)
    {
        case 1: return cv::Scalar_<T>(at<T>(0));
        case 2: return cv::Scalar_<T>(at<T>(0), at<T>(1));
        case 3: return cv::Scalar_<T>(at<T>(0), at<T>(1), at<T>(2));
        case 4: return cv::Scalar_<T>(at<T>(0), at<T>(1), at<T>(2), at<T>(3));
    }
}

template <typename T>
std::vector<T> MxArray::toVector() const
{
    int n = numel();
    std::vector<T> vt(n);
    if (isNumeric())
        for (int i = 0; i < n; ++i)
            vt[i] = at<T>(i);
    else if (isCell())
        for (int i = 0; i < n; ++i)
            vt[i] = MxArray(mxGetCell(p_, i)).at<T>(0);
    else
        mexErrMsgIdAndTxt("mexopencv:error", "Cannot convert to std::vector");
    return vt;
}

template <typename T>
std::vector<T> MxArray::toVector(std::const_mem_fun_ref_t<T,MxArray> f) const
{
    std::vector<MxArray> v = toVector<MxArray>();
    std::vector<T> vt;
    vt.reserve(v.size());
    for (std::vector<MxArray>::iterator it=v.begin(); it<v.end(); ++it)
        vt.push_back(f(*it));
    return vt;
}

template <typename T>
T MxArray::at(mwIndex index) const
{
    if (!p_ || numel() <= index)
        mexErrMsgIdAndTxt("mexopencv:error", "Accessing invalid range");
    switch (classID())
    {
        case mxCHAR_CLASS:
            return static_cast<T>(*(mxGetChars(p_)+index));
        case mxDOUBLE_CLASS:
            return static_cast<T>(*(mxGetPr(p_)+index));
        case mxINT8_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<int8_t*>(mxGetData(p_))+index));
        case mxUINT8_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<uint8_t*>(mxGetData(p_))+index));
        case mxINT16_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<int16_t*>(mxGetData(p_))+index));
        case mxUINT16_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<uint16_t*>(mxGetData(p_))+index));
        case mxINT32_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<int32_t*>(mxGetData(p_))+index));
        case mxUINT32_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<uint32_t*>(mxGetData(p_))+index));
        case mxINT64_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<int64_t*>(mxGetData(p_))+index));
        case mxUINT64_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<uint64_t*>(mxGetData(p_))+index));
        case mxSINGLE_CLASS:
            return static_cast<T>(
                *(reinterpret_cast<float*>(mxGetData(p_))+index));
        case mxLOGICAL_CLASS:
            return static_cast<T>(*(mxGetLogicals(p_)+index));
        case mxCELL_CLASS:
        case mxSTRUCT_CLASS:
        case mxFUNCTION_CLASS:
        default:
            mexErrMsgIdAndTxt("mexopencv:error", "MxArray is not primitive");
            return static_cast<T>(0);
    }
}

template <typename T>
T MxArray::at(mwIndex i, mwIndex j) const
{
    return at<T>(subs(i,j));
}

template <typename T>
T MxArray::at(const std::vector<mwIndex>& si) const
{
    return at<T>(subs(si));
}

template <typename T>
void MxArray::set(mwIndex index, const T& value)
{
    if (index < 0 || numel() <= index)
        mexErrMsgIdAndTxt("mexopencv:error", "Accessing invalid range");
    switch (classID())
    {
        case mxCHAR_CLASS:
            *(mxGetChars(p_)+index) = static_cast<mxChar>(value); break;
        case mxDOUBLE_CLASS:
            *(mxGetPr(p_)+index) = static_cast<double>(value); break;
        case mxINT8_CLASS:
            *(reinterpret_cast<int8_t*>(mxGetData(p_))+index) =
                static_cast<int8_t>(value); break;
        case mxUINT8_CLASS:
            *(reinterpret_cast<uint8_t*>(mxGetData(p_))+index) =
                static_cast<uint8_t>(value); break;
        case mxINT16_CLASS:
            *(reinterpret_cast<int16_t*>(mxGetData(p_))+index) =
                static_cast<int16_t>(value); break;
        case mxUINT16_CLASS:
            *(reinterpret_cast<uint16_t*>(mxGetData(p_))+index) =
                static_cast<uint16_t>(value); break;
        case mxINT32_CLASS:
            *(reinterpret_cast<int32_t*>(mxGetData(p_))+index) =
                static_cast<int32_t>(value); break;
        case mxUINT32_CLASS:
            *(reinterpret_cast<uint32_t*>(mxGetData(p_))+index) =
                static_cast<uint32_t>(value); break;
        case mxINT64_CLASS:
            *(reinterpret_cast<int64_t*>(mxGetData(p_))+index) =
                static_cast<int64_t>(value); break;
        case mxUINT64_CLASS:
            *(reinterpret_cast<uint64_t*>(mxGetData(p_))+index) =
                static_cast<uint64_t>(value); break;
        case mxSINGLE_CLASS:
            *(reinterpret_cast<float*>(mxGetData(p_))+index) =
                static_cast<float>(value); break;
        case mxLOGICAL_CLASS:
            *(mxGetLogicals(p_)+index) = static_cast<mxLogical>(value); break;
        case mxCELL_CLASS:
            mxSetCell(const_cast<mxArray*>(p_), index, MxArray(value)); break;
        case mxSTRUCT_CLASS:
        case mxFUNCTION_CLASS:
        default:
            mexErrMsgIdAndTxt("mexopencv:error", "MxArray type is not valid");
    }
}

template <typename T>
void MxArray::set(mwIndex i, mwIndex j, const T& value)
{
    set<T>(subs(i,j),value);
}

template <typename T>
void MxArray::set(const std::vector<mwIndex>& si, const T& value)
{
    set<T>(subs(si),value);
}

template <typename T>
void MxArray::set(const std::string& fieldName, const T& value, mwIndex index)
{
    if (!isStruct())
        mexErrMsgIdAndTxt("mexopencv:error", "MxArray is not struct");
    if (!isField(fieldName))
    {
        if (mxAddField(const_cast<mxArray*>(p_), fieldName.c_str())<0)
            mexErrMsgIdAndTxt("mexopencv:error",
                              "Failed to create a field '%s'",
                              fieldName.c_str());
    }
    mxSetField(const_cast<mxArray*>(p_), index, fieldName.c_str(),
               static_cast<mxArray*>(MxArray(value)));
}

/** Cell element accessor.
 * @param index index of the cell array.
 * @return MxArray of the element at index.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * MxArray m = cellArray.at<MxArray>(0);
 * @endcode
 */
template <> MxArray MxArray::at(mwIndex index) const;

/** Template for element write accessor.
 * @param index offset of the array element.
 * @param value value of the field.
 */
template <> void MxArray::set(mwIndex index, const MxArray& value);

/** Convert MxArray to std::vector<MxArray>.
 * @return std::vector<MxArray> value.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * vector<MxArray> v = cellArray.toVector<MxArray>();
 * @endcode
 */
template <> std::vector<MxArray> MxArray::toVector() const;

/** Convert MxArray to std::vector<std::string>.
 * @return std::vector<std::string> value.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * vector<string> v = cellArray.toVector<string>();
 * @endcode
 */
template <> std::vector<std::string> MxArray::toVector() const;

/** Convert MxArray to std::vector<cv::Mat>.
 * @return std::vector<cv::Mat> value.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * vector<Mat> v = cellArray.toVector<Mat>();
 * @endcode
 */
template <> std::vector<cv::Mat> MxArray::toVector() const;

/** Convert MxArray to std::vector<Point>.
 * @return std::vector<Point> value.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * vector<Point> v = cellArray.toVector<Point>();
 * @endcode
 */
template <> std::vector<cv::Point> MxArray::toVector() const;

/** Convert MxArray to std::vector<Point2f>.
 * @return std::vector<Point2f> value.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * vector<Point2f> v = cellArray.toVector<Point2f>();
 * @endcode
 */
template <> std::vector<cv::Point2f> MxArray::toVector() const;

/** Convert MxArray to std::vector<Point3f>.
 * @return std::vector<Point3f> value.
 *
 * Example:
 * @code
 * MxArray cellArray(prhs[0]);
 * vector<Point3f> v = cellArray.toVector<Point3f>();
 * @endcode
 */
template <> std::vector<cv::Point3f> MxArray::toVector() const;

/** Convert MxArray to std::vector<cv::KeyPoint>.
 * @return std::vector<cv::KeyPoint> value.
 *
 * Example:
 * @code
 * MxArray structArray(prhs[0]);
 * vector<KeyPoint> v = structArray.toVector<KeyPoint>();
 * @endcode
 */
template <> std::vector<cv::KeyPoint> MxArray::toVector() const;

/** Convert MxArray to std::vector<cv::DMatch>.
 * @return std::vector<cv::DMatch> value.
 *
 * Example:
 * @code
 * MxArray structArray(prhs[0]);
 * vector<DMatch> v = structArray.toVector<DMatch>();
 * @endcode
 */
template <> std::vector<cv::DMatch> MxArray::toVector() const;

#endif // __MXARRAY_HPP__
