/*
 * Copyright (C) 2012 by
 *   MetraLabs GmbH (MLAB), GERMANY
 * and
 *   Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
 * All rights reserved.
 *
 * Contact: info@mira-project.org
 *
 * Commercial Usage:
 *   Licensees holding valid commercial licenses may use this file in
 *   accordance with the commercial license agreement provided with the
 *   software or, alternatively, in accordance with the terms contained in
 *   a written agreement between you and MLAB or NICR.
 *
 * GNU General Public License Usage:
 *   Alternatively, this file may be used under the terms of the GNU
 *   General Public License version 3.0 as published by the Free Software
 *   Foundation and appearing in the file LICENSE.GPL3 included in the
 *   packaging of this file. Please review the following information to
 *   ensure the GNU General Public License version 3.0 requirements will be
 *   met: http://www.gnu.org/copyleft/gpl.html.
 *   Alternatively you may (at your option) use any later version of the GNU
 *   General Public License if such license has been publicly approved by
 *   MLAB and NICR (or its successors, if any).
 *
 * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
 * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
 * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
 */

/**
 * @file SparseEigen
 *    Reflection adaptors for Eigen sparse matrices to support de-/serialization.
 *
 * @author Ronny Stricker
 * @date   2013/08/02
 */

#ifndef _MIRA_SPARSE_EIGEN_
#define _MIRA_SPARSE_EIGEN_

#include <Eigen/Sparse>

#include <math/EigenFormat.h>
#include <math/Angle.h>
#include <math/YawPitchRoll.h>
#include <error/Exceptions.h>
#include <utils/MakeString.h>
#include <serialization/adapters/std/vector>
#include <serialization/BinarySerializer.h>
#include <serialization/PropertySerializer.h>
#include <serialization/JSONSerializer.h>
#include <serialization/IsTransparentSerializable.h>

namespace mira {

////////////////////////////////////////////////////////////////////////////////

/**
 * Non intrusive reflect for BinarySerializer and Eigen Sparse Matrices
 * @param iR The binary serializer
 * @param m The matrix
 */
template <typename DerivedSerializer, typename Derived>
void reflect(BinarySerializer<DerivedSerializer>& iR, Eigen::SparseMatrix<Derived>& m)
{
	// store rows and cols
	int rows = m.rows();
	int cols = m.cols();
	int nonZeros = m.nonZeros();
	
	iR.member("rows", rows, "");
	iR.member("cols", cols, "");
	iR.member("elements", nonZeros, "");
	
	for (int k=0; k<m.outerSize(); ++k)
		for (typename Eigen::SparseMatrix<Derived>::InnerIterator it(m,k); it; ++it) {
			int row = it.row();
			int col = it.col();
			MIRA_MEMBER_WITH_ID(iR, "row", "", row, "");
			MIRA_MEMBER_WITH_ID(iR, "col", "", col, "");
			MIRA_MEMBER_WITH_ID(iR, "val", "", it.value(), "");
		}
}

/**
 * Non intrusive reflect for BinaryDeserializer and Eigen Sparse Matrices
 * @param iR The binary deserializer
 * @param m The matrix
 */
template <typename DerivedSerializer, typename Derived>
void reflect(BinaryDeserializer<DerivedSerializer>& iR, Eigen::SparseMatrix<Derived>& m)
{
	typedef Eigen::Triplet<Derived> T;
	typedef typename Eigen::SparseMatrix<Derived>::StorageIndex I;
	typedef typename Eigen::SparseMatrix<Derived>::Scalar S;
	
	int rows=0;
	int cols=0;
	I elements;

	// read rows and cols
	iR.member("rows", rows, "");
	iR.member("cols", cols, "");
	iR.member("elements", elements, "");
	
	// resize and set to zero
	m.resize(rows, cols);
	
	// read list of triplets (row, col, value) and assign it to matrix
	int r = 0;
	int c = 0;
	S s = 0;
	std::vector<T> tList;
	tList.resize( elements );
	
	for (I i = 0; i < elements; ++i ) {
		MIRA_MEMBER_WITH_ID(iR, "row", "", r, "");
		MIRA_MEMBER_WITH_ID(iR, "col", "", c, "");
		MIRA_MEMBER_WITH_ID(iR, "val", "", s, "");
		tList[i] = T(r,c,s); 
	}
	m.setFromTriplets(tList.begin(),tList.end());
}

/**
 * Non intrusive reflect for Eigen Sparse Matrices
 * @param iR The serializer
 * @param m The matrix
 */
template <typename Reflector, typename Derived>
void reflectRead(Reflector& r, Eigen::SparseMatrix<Derived>& m)
{
	// use formatter to stream the matrix
	std::stringstream s;
	
	s << "[";
	for (int k=0; k<m.outerSize(); ++k)
		for (typename Eigen::SparseMatrix<Derived>::InnerIterator it(m,k); it; ++it)
			s << "(" << it.row() << "," << it.col() << ")" << it.value() << " ";
	s << "]";
	std::string data = s.str();

	int rows = m.rows();
	int cols = m.cols();
	
	r.member("rows", rows, "");
	r.member("cols", cols, "");
	r.member("values", data, "");
	
}

/**
 * Non intrusive reflect for Eigen Sparse Matrices
 * @param iR The deserializer
 * @param m The matrix
 */
template <typename Reflector, typename Derived>
void reflectWrite(Reflector& r, Eigen::SparseMatrix<Derived>& m)
{
	typedef Eigen::Triplet<Derived> T;
	
	std::string data;
	int rows,cols;
	
	r.member("rows", rows, "");
	r.member("cols", cols, "");
	r.member("values", data, "");
	
	std::stringstream s(data);
	NumericalIstream nis(s);
	
	m.resize(rows,cols);
	
	int row,col;
	Derived val;
	
	std::vector<T> tList;
	TEigenFormat<Derived>::match(s,"[");
	while ( TEigenFormat<Derived>::match(s,"(","]") == 1 && s.good() ) {
		nis >> row;
		TEigenFormat<Derived>::match(s,",");
		nis >> col;
		TEigenFormat<Derived>::match(s,")");
		nis >> val;
		if(!s.fail()) {
			tList.push_back(T(row,col,val));
		}
	}
	m.setFromTriplets(tList.begin(),tList.end());
}

template <typename DerivedReflector, typename Derived>
void reflect(ReflectorInterface<DerivedReflector>& iR, Eigen::SparseMatrix<Derived>& m)
{
	splitReflect(iR, m);
}

////////////////////////////////////////////////////////////////////////////////

} // namespace

#endif
