//==============================================================================
/*! \file
 * OpenMesh Toolkit for mesh analysis    \n
 * Copyright (c) 2010 by Rostislav Hulik     \n
 *
 * Author:  Rostislav Hulik, rosta.hulik@gmail.com  \n
 * Date:    2010/10/20                          \n
 *
 * This file is part of software developed for support of Rostislav Hulik's dissertation thesis at dcgm-robotics@FIT group.
 *
 * This file is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Description:
 * - Extension of OpenMesh OMFormat for use in MDSTk Channel export
 * - Most of the code is modified OMFormat from OpenMesh
 */

#ifndef OM_FORMAT_EXT_H
#define OM_FORMAT_EXT_H

#include <OpenMesh\Core\IO\OMFormat.hh>
#include <OMToolkit\IO\OMStoreRestore.h>

namespace OpenMesh {
namespace IO {
namespace OMFormat{

/**
 * Extended OM format chunk header (added store methods for MDSTk channel)
 */
struct HeaderExt : Header
{
	/**
	 * Store into stream
	 * @_os Output MDSTk channel
	 * @_swap Swap bytes flag
	 * @return Number of stored bytes
	 */
	size_t store( mds::mod::CChannel& _os, bool _swap ) const
	{
		_os.write( (char*)this, 4); // magic_, mesh_, version_
		size_t bytes = 4;
		bytes += binaryExt<uint32_t>::store( _os, n_vertices_, _swap );
		bytes += binaryExt<uint32_t>::store( _os, n_faces_, _swap );
		bytes += binaryExt<uint32_t>::store( _os, n_edges_, _swap );
		return bytes;
	}

	/**
	 * Restore from stream
	 * @_is Intput MDSTk channel
	 * @_swap Swap bytes flag
	 * @return Number of read bytes
	 */
	size_t restore( mds::mod::CChannel& _is, bool _swap )
	{
		if (_is.read( (char*)this, 4 ) == 0)
			return 0;

		size_t bytes = 4;
		bytes += binaryExt<uint32_t>::restore( _is, n_vertices_, _swap );
		bytes += binaryExt<uint32_t>::restore( _is, n_faces_, _swap );
		bytes += binaryExt<uint32_t>::restore( _is, n_edges_, _swap );
		return bytes;
	}
};
} // namespace OMFormat


/**
 * Store value into stream
 * @_os Output MDSTk channel
 * @_v Value to store
 * @_swap Swap bytes flag
 * @return Number of stored bytes
 */
template <typename T> inline
size_t store( mds::mod::CChannel& _os, const T& _v, bool _swap=false)
{ 
	return binaryExt< T >::store( _os, _v, _swap ); 
}

/**
 * Retore value from stream
 * @_is Intput MDSTk channel
 * @_v Value to restore
 * @_swap Swap bytes flag
 * @return Number of restored bytes
 */
template <typename T> inline
size_t restore( mds::mod::CChannel& _is, T& _v, bool _swap=false)
{ 
	return binaryExt< T >::restore( _is, _v, _swap ); 
}

/**
 * Store OM format header into stream
 * @_os Output MDSTk channel
 * @_hdr Header to store
 * @_swap Swap bytes flag
 * @return Number of stored bytes
 */
template <> inline
size_t store( mds::mod::CChannel& _os, const OMFormat::HeaderExt& _hdr, bool _swap)
{ 
	return _hdr.store( _os, _swap ); 
}

/**
 * Retore OM format header from stream
 * @_is Intput MDSTk channel
 * @_hdr Header to restore
 * @_swap Swap bytes flag
 * @return Number of restored bytes
 */
template <> inline
size_t restore( mds::mod::CChannel& _is, OMFormat::HeaderExt& _hdr, bool _swap )
{ 
	return _hdr.restore( _is, _swap ); 
}


/**
 * Store OM chunk header into stream
 * @_os Output MDSTk channel
 * @_hdr Header to store
 * @_swap Swap bytes flag
 * @return Number of stored bytes
 */
template <> inline
size_t store( mds::mod::CChannel& _os, const OMFormat::Chunk::Header& _hdr, bool _swap)
{
    OMFormat::uint16 val; val << _hdr;
    return binaryExt<uint16_t>::store( _os, val, _swap );
}

/**
 * Retore OM chunk header from stream
 * @_is Intput MDSTk channel
 * @_hdr Header to restore
 * @_swap Swap bytes flag
 * @return Number of restored bytes
 */
template <> inline
size_t restore( mds::mod::CChannel& _is, OMFormat::Chunk::Header& _hdr, bool _swap )
{
    OMFormat::uint16 val;
    size_t bytes = binaryExt<uint16_t>::restore( _is, val, _swap );
    _hdr << val;
    return bytes;
}




////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Storing/restoring scalars

/**
 * Helper to store a an integer
 * @param _os Output MDSTk channel
 * @param _val Value to store
 * @param _b Integer size
 * @param _swap Swap byte flag
 * @param t_signed Signed flag
 * @return Number of saved bytes
 */
template< typename T > size_t store( mds::mod::CChannel& _os, const T& _val, OMFormat::Chunk::Integer_Size _b, bool _swap, t_signed);

/**
 * Helper to store a an unsigned integer
 * @param _os Output MDSTk channel
 * @param _val Value to store
 * @param _b Integer size
 * @param _swap Swap byte flag
 * @param t_unsigned Unsigned flag
 * @return Number of saved bytes
 */
template< typename T > size_t store( mds::mod::CChannel& _os, const T& _val, OMFormat::Chunk::Integer_Size _b, bool _swap, t_unsigned);

/**
 * Store an integer with a wanted number of bits
 * @param _os Output MDSTk channel
 * @param _val Value to store
 * @param _b Integer size
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template< typename T > inline size_t store( mds::mod::CChannel& _os, const T& _val, OMFormat::Chunk::Integer_Size _b, bool _swap)
{
	assert( OMFormat::is_integer( _val ) );
	if ( OMFormat::is_signed( _val ) )
		return store( _os, _val, _b, _swap, t_signed()   );
		return store( _os, _val, _b, _swap, t_unsigned() );
}

/**
 * Helper to restore a an integer
 * @param _is Input MDSTk channel
 * @param _val Value to restore
 * @param _b Integer size
 * @param _swap Swap byte flag
 * @param t_signed Signed flag
 * @return Number of restored bytes
 */
template< typename T > inline size_t restore( mds::mod::CChannel& _is, T& _val, OMFormat::Chunk::Integer_Size _b,bool _swap, t_signed);

/**
 * Helper to restore a an unsigned integer
 * @param _is Input MDSTk channel
 * @param _val Value to restore
 * @param _b Integer size
 * @param _swap Swap byte flag
 * @param t_unsigned unsigned flag
 * @return Number of restored bytes
 */
template< typename T > inline size_t restore(  mds::mod::CChannel& _is, T& _val, OMFormat::Chunk::Integer_Size _b, bool _swap, t_unsigned);

/**
 * Restore an integer with a wanted number of bits
 * @param _is Input MDSTk channel
 * @param _val Value to restore
 * @param _b Integer size
 * @param _swap Swap byte flag
 * @param t_unsigned unsigned flag
 */
template< typename T > inline size_t restore(  mds::mod::CChannel& _is, T& _val, OMFormat::Chunk::Integer_Size _b, bool _swap)
{
	assert( OMFormat::is_integer( _val ) );

	if ( OMFormat::is_signed( _val ) )
		return restore( _is, _val, _b, _swap, t_signed() );
		return restore( _is, _val, _b, _swap, t_unsigned() );
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Storing vectors

/**
 * Helper to store a 2D vector
 * @param _os Output MDSTk channel
 * @param _vec Vector to store
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template <typename VecT> inline size_t store( mds::mod::CChannel& _os, const VecT& _vec, GenProg::Int2Type<2>, bool _swap )
{
    size_t bytes =  store( _os, _vec[0], _swap );
    bytes += store( _os, _vec[1], _swap );
    return bytes;
}

/**
 * Helper to store a 3D vector
 * @param _os Output MDSTk channel
 * @param _vec Vector to store
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template <typename VecT> inline size_t store( mds::mod::CChannel& _os, const VecT& _vec, GenProg::Int2Type<3>, bool _swap )
{
    size_t bytes =  store( _os, _vec[0], _swap );
    bytes += store( _os, _vec[1], _swap );
    bytes += store( _os, _vec[2], _swap );
    return bytes;
}

/**
 * Helper to store a 4D vector
 * @param _os Output MDSTk channel
 * @param _vec Vector to store
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template <typename VecT> inline size_t store( mds::mod::CChannel& _os, const VecT& _vec, GenProg::Int2Type<4>, bool _swap )
{
    size_t bytes =  store( _os, _vec[0], _swap );
    bytes += store( _os, _vec[1], _swap );
    bytes += store( _os, _vec[2], _swap );
    bytes += store( _os, _vec[3], _swap );
    return bytes;
}

/**
 * Helper to store a 1D vector
 * @param _os Output MDSTk channel
 * @param _vec Vector to store
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template <typename VecT> inline size_t store( mds::mod::CChannel& _os, const VecT& _vec, GenProg::Int2Type<1>, bool _swap )
{
    return store( _os, _vec[0], _swap );
}

/**
 * Storing a vector type
 * @param _os Output MDSTk channel
 * @param _vec Vector to store
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template <typename VecT> inline size_t vector_store(mds::mod::CChannel& _os, const VecT& _vec, bool _swap )
{
   return store( _os, _vec, GenProg::Int2Type< vector_traits<VecT>::size_ >(), _swap );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Restoring vectors

/**
 * Helper to restore a 2D vector
 * @param _is Input MDSTk channel
 * @param _vec Vector to restore
 * @param _swap Swap byte flag
 * @return Number of restored bytes
 */
template <typename VecT> inline size_t restore( mds::mod::CChannel& _is, VecT& _vec, GenProg::Int2Type<2>, bool _swap )
{
	size_t bytes =  restore( _is, _vec[0], _swap );
    bytes += restore( _is, _vec[1], _swap );
    return bytes;
}

/**
 * Helper to restore a 3D vector
 * @param _is Input MDSTk channel
 * @param _vec Vector to restore
 * @param _swap Swap byte flag
 * @return Number of restored bytes
 */
template <typename VecT> inline size_t restore( mds::mod::CChannel& _is, VecT& _vec, GenProg::Int2Type<3>, bool _swap )
{
    typedef typename vector_traits<VecT>::value_type scalar_type;
    size_t bytes;

    bytes  = binaryExt<scalar_type>::restore( _is, _vec[0], _swap );
    bytes += binaryExt<scalar_type>::restore( _is, _vec[1], _swap );
    bytes += binaryExt<scalar_type>::restore( _is, _vec[2], _swap );
    return bytes;
}

/**
 * Helper to restore a 4D vector
 * @param _is Input MDSTk channel
 * @param _vec Vector to restore
 * @param _swap Swap byte flag
 * @return Number of restored bytes
 */
template <typename VecT> inline size_t restore( mds::mod::CChannel& _is, VecT& _vec, GenProg::Int2Type<4>, bool _swap )
{
    typedef typename vector_traits<VecT>::value_type scalar_type;
    size_t bytes;

    bytes  = binaryExt<scalar_type>::restore( _is, _vec[0], _swap );
    bytes += binaryExt<scalar_type>::restore( _is, _vec[1], _swap );
    bytes += binaryExt<scalar_type>::restore( _is, _vec[2], _swap );
    bytes += binaryExt<scalar_type>::restore( _is, _vec[3], _swap );
    return bytes;
}

/**
 * Helper to restore a 1D vector
 * @param _is Input MDSTk channel
 * @param _vec Vector to restore
 * @param _swap Swap byte flag
 * @return Number of restored bytes
 */
template <typename VecT> inline size_t restore( mds::mod::CChannel& _is, VecT& _vec, GenProg::Int2Type<1>, bool _swap )
{
	return restore( _is, _vec[0], _swap );
}

/**
 * Restoring a vector type
 * @param _is Input MDSTk channel
 * @param _vec Vector to restore
 * @param _swap Swap byte flag
 * @return Number of restored bytes
 */
template <typename VecT> inline size_t vector_restore( mds::mod::CChannel& _is, VecT& _vec, bool _swap )
{
	return restore( _is, _vec, GenProg::Int2Type< vector_traits<VecT>::size_ >(), _swap );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Storing property names

/**
 * Store a property name
 * @param _os Output MDSTk channel
 * @param _pn Property name to store
 * @param _swap Swap byte flag
 * @return Number of saved bytes
 */
template <> inline size_t store( mds::mod::CChannel& _os, const OMFormat::Chunk::PropertyName& _pn,	bool _swap )
{
	store( _os, _pn.size(), OMFormat::Chunk::Integer_8, _swap ); // 1 byte
    if ( _pn.size() )
      _os.write( _pn.c_str(), _pn.size() ); // size bytes
    return _pn.size() + 1;
}

/**
 * Retore a property name
 * @param _is Input MDSTk channel
 * @param _pn Property name to restore
 * @param _swap Swap byte flag
 * @return Number of restored bytes
 */
template <> inline size_t restore( mds::mod::CChannel& _is, OMFormat::Chunk::PropertyName& _pn, bool _swap )
{
    size_t size;

    restore( _is, size, OMFormat::Chunk::Integer_8, _swap); // 1 byte

    assert( OMFormat::Chunk::PropertyName::is_valid( size ) );

    if ( size > 0 )
    {
      char buf[256];
      _is.read( buf, size ); // size bytes
      buf[size] = '\0';
      _pn.resize(size);
      _pn = buf;
    }
    return size+1;
}

} // namespace IO
} // namespace Openmesh

#include <OMToolkit\IO\OMFormatExt.hxx>
#endif