/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework 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 General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_MVECTOR_H
#define __IWEAR_MVECTOR_H


#ifndef __IWEAR_EXCEPTIONS_H
#include <iwear/exceptions.h>
#endif

#ifndef __IWEAR_MAPLESTREAM_H
#include <iwear/maplestream.h>
#endif

#ifndef __IWEAR_SCOPETRACER_H
#include <iwear/scopetracer.h>
#endif

#include <new>
#include <limits>

using std::numeric_limits;
namespace iwear
{
    namespace math
    {

template<class T> class MathVector;
template<class T> class MathMatrix;
template<class T> class MathVectorReference;
template<class T> class MatrixRepresentation;

/**
 * This class is the real representation of a vector. It cannot exist of its
 * own, since it assumes to be allocated by the MathVector class as an
 * contiguos array, therefore it acceses its elements after its actual own
 * position.
 * @note This class attemps some rather complex reference counting and
 * copy-on-write mechanisms. Therefore some things might happen in different
 * speeds than you suppose...
 */
template<class T>
class VectorRepresentation/*{{{*/
{
private:
    friend class MathVector<T>;
    friend class MathVectorReference<T>;
    friend class MathMatrix<T>;
    friend class MatrixRepresentation<T>;

    /**
     * Noone should ever really create one of those objects !
     */
    VectorRepresentation( void ) { }
    VectorRepresentation( VectorRepresentation& ) { }
    VectorRepresentation( const VectorRepresentation& ) { }
protected:
    /**
     * The size of the vector is stored here.
     * Bigger ones are not supported, but 64k should be enough for most cases.
     */
    uint16_t vector_size;

    /**
     * How many objects have references to us ?
     * If the count is negative, the vector belongs to a matrix and thus should
     * be real-copied etc. when assigning etc.
     * This leads to a max count of references of 32k which should be enough.
     * After that every new copy will be a real copy
     * @note this is mutable to be able to ref_copy from const objects.
     */
    mutable int16_t references;

    /**
     * Computes the start of the vector field data. its actually starting right
     * after this object.
     */
    inline char * vector_field_start( void ) { return reinterpret_cast<char*>(this + 1); }
    inline const char * vector_field_start( void ) const{ return reinterpret_cast<const char*>(this + 1); }

    /**
     * This casts the start of the vector field to an c-style array of Ts
     */
    inline T* vector_field( void ) { return reinterpret_cast<T*>(vector_field_start()); }
    inline const T* vector_field( void ) const { return reinterpret_cast<const T*>(vector_field_start()); }

    /**
     * Does a reference copy of this object. It will either increase the
     * reference count and then return itself or it will do a real copy if
     * either the refcount is too high or if the representation is just a part
     * of a matrix.
     */
    VectorRepresentation<T>* ref_copy ( void ) const;

    /**
     * This does a real copy of this vector stuff.
     */
    VectorRepresentation<T>* real_copy( void ) const;

    /**
     * Releases this copy. If its a reference copy, its counter will be
     * decreased. If the counter reaches 0, the object is destroyed.
     */
    void release( void );

    static VectorRepresentation<T>* allocate( uint16_t n );

public:
    /**
     * @return the size of the vector in elements
     */
    uint16_t size( void ) const { return vector_size; }

    /**
     * This should be used to access the element at id (starting from 0). Bound
     * checks are done, and if failed, an exception is thrown.
     */
    T& elem( uint16_t id );
    const T& elem( uint16_t id ) const;

};/*}}}*/

template<class T>
void VectorRepresentation<T>::release( void )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    // Either this counter reaches 0 because it was a standalone vector, or it
    // was one within a matrix so then it is <0 before. If that is the case,
    // something went wrong since we should never call release on a matrix
    // glued vector.
    if ( references <0 )
    {
	throw bug("Cannot release Matrix glued VectorRepresentation",BUGFFL);
    }

    d_dbg << "Ref " << references << " ==> ";
    references--;
    d_dbg << references << std::endl;
    if ( references < 0 )
    {
	// Here we really have to release the data... so what do we do ?
	// iterating through every element, explicitly call destructor for the
	// elements and then delete ourself
	uint16_t n = size();

	for ( uint16_t i = 0; i < n; i++ )
	{
	    elem(i).T::~T();
	}

	delete[] reinterpret_cast<char*>(this);
    }
}/*}}}*/

template<class T>
VectorRepresentation<T>* VectorRepresentation<T>::allocate( uint16_t n )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    char * vrfield = new char[sizeof(VectorRepresentation<T>) + n * sizeof(T)];
    return reinterpret_cast<VectorRepresentation<T>*>(vrfield);
}/*}}}*/

template<class T>
VectorRepresentation<T>* VectorRepresentation<T>::ref_copy( void ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( references == numeric_limits<int16_t>::max() || references < 0 ) 
    {
	return real_copy();
    }
    // here we increment the mutable reference counter and return a non-const
    // pointer from a possible const object.
    // Since this all should be hidden in the MathVector class, its ok here,
    // and even necessary to return a non-const pointer, since we want to be
    // able to also ref-copy const objects.
    references++;
    return const_cast<VectorRepresentation<T>*>(this);
}/*}}}*/

template<class T>
VectorRepresentation<T>* VectorRepresentation<T>::real_copy( void ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t n = size();
    VectorRepresentation<T>* nv = allocate(n);
    nv->vector_size = n;
    nv->references = 0;
    for ( uint16_t i = 0; i < n; i++ )
    {
	new (&(nv->elem(i))) T(elem(i));
    }
    return nv;
}/*}}}*/

template<class T>
inline T& VectorRepresentation<T>::elem( uint16_t id )/*{{{*/
{
    if ( id < size() )
    {
	return vector_field()[id];
    }
    else
    {
	THROW (bounds_error,("Vector Element Acces out of Bounds", id, size()));
    }
}/*}}}*/

template<class T>
inline const T& VectorRepresentation<T>::elem( uint16_t id ) const/*{{{*/
{
    if ( id < size() )
    {
	return vector_field()[id];
    }
    else
    {
	THROW ( bounds_error,("Vector Element Acces out of Bounds", id, size()));
    }
}/*}}}*/

template<class T>
ostream& operator<< ( ostream& o, const MathVector<T>& mv);

template<class T>
MapleStream& operator<< ( MapleStream& o, const MathVector<T>& mv);
/**
 * This is the real vector template class to be used by the end user.
 */
template<class T>
class MathVector/*{{{*/
{
private:
    friend class MathMatrix<T>;
    friend class MathVectorReference<T>;
    /**
     * We dont want a vector without a defined size, so we dont allow it.
     */
    MathVector( void ) { }
protected:
    VectorRepresentation<T>* vr;

    /**
     * This is an internal constructor to be used by the MathMatrix only !
     */
    MathVector( VectorRepresentation<T>* vr);
    MathVector( VectorRepresentation<T>& vr);
public:

    typedef T element_type;
    /**
     * This is a copy constructor. It can also copy other MathVectors as long
     * as S and T are compatible.
     */
    template<class S> MathVector( const MathVector<S>& );
    MathVector( const MathVector<T>& );
    MathVector( uint16_t n , const T& );


    /**
     * Yes, right, thats the constructor you should use
     */
    MathVector( uint16_t n );

    /**
     * Destroys a MathVector. It will deallocate the object only if it was
     * previously really allocated at its own, otherwise it seems to be
     * allocated by a matrix.
     */
    virtual ~MathVector( );

    /**
     * @return the count of elements that this vector can hold. This amount cannot be changed.
     */
    uint16_t size( void ) const { return vr->size(); }

    /**
     * This returns a copy of the vector, sliced to the specified size. If the
     * size is the same, a refcopy is returned, if its smaller, a sliced copy
     * will be returned.
     * If its bigger, the new elements are default constructed.
     */
    MathVector slice( uint16_t dim ) const;
    
    /**
     * This will be called if the current vector is about to be changed.
     */
    void taint_me(void);

    /**
     * Access an element of the vector by reference, so you can edit it.
     */
    T& operator[] ( uint16_t n );

    /**
     * This can be used to access a constant vector.
     */
    const T& operator[] ( uint16_t n ) const;

    /**
     *
     */
    MathVector<T>& operator=( const MathVector<T>& );

    template<class S> MathVector<T>& operator=( const MathVector<S>& );


    MathVector<T> operator+( const MathVector<T>& mm ) const;
    template <class S> MathVector<T> operator+( const MathVector<S>& mm ) const;
    MathVector<T> operator-( const MathVector<T>& mm ) const;
    template <class S> MathVector<T> operator-( const MathVector<S>& mm ) const;

    template<class S> MathVector<T> operator/( const S& mm ) const;
    template<class S> MathVector<T> operator*( const S& mm ) const;
    template<class S> MathVector<T> left_mul( const S& mm ) const;

    friend ostream& operator<< <T>( ostream& o, const MathVector<T>& mv);
    friend MapleStream& operator<< <T>( MapleStream& o, const MathVector<T>& mv);
};/*}}}*/

/**
 * For efficiency some functions (especially the matrix ones) return a
 * MathVectorReference instead of a MathVector. These References really point
 * to a Vector within a matrix, so changing something in them changes the
 * matrix(instead of the vector). Assigning them to a MathVector will of course
 * do a complete copy of the data.
 */
template<class T>
class MathVectorReference : public MathVector<T>
{
private:
protected:
public:
    virtual ~MathVectorReference() { }
    MathVectorReference( VectorRepresentation<T>* v ) : MathVector<T>(v) { }
    MathVectorReference<T>& operator=( const MathVector<T>& sv) 
    { return static_cast<MathVectorReference&>(MathVector<T>::operator=(sv) ); }
};

/**
 * @addtogroup Output_Operators
 * @{
 */
template<class T>
inline ostream& operator<< ( ostream& o, const MathVector<T>& mv)/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t vsize = mv.size();
    for ( uint16_t vp = 0; vp < vsize; vp++)
    {
	o << mv[vp];
	o << std::endl;
    }
    return o;
}/*}}}*/

template<class T>
inline MapleStream& operator<< ( MapleStream& o, const MathVector<T>& mv)/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t vsize = mv.size();
    o << "Vector([";
    for ( uint16_t vp = 0; vp < vsize; vp++)
    {
	if ( vp != 0 )
	{
	    o << ",";
	}
	o << mv[vp];
    }
    o << "])";
    return o;
}/*}}}*/
/** @} */

template<class T>
template<class S>
MathVector<T> MathVector<T>::left_mul( const S& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
	// Note that mm is on the left here...
	new (&(nv->elem(i))) T(mm * vr->elem(i));
    }
    return nv;
}/*}}}*/

template<class T>
MathVector<T> MathVector<T>::slice( uint16_t dim ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( dim == vr->vector_size )
    {
	return *this;
    }
    else
    {
	uint16_t n = dim;
	VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
	nv->vector_size = n;
	nv->references = 0;
	uint16_t m = min(n,size());
	uint16_t i;
	for ( i = 0; i < m; i++ )
	{
	    put_new_element(&(nv->elem(i)), vr->elem(i));
	}
	for (; i < n; i++ )
	{
	    new (&(nv->elem(i))) T();
	}
	return nv;
    }
}/*}}}*/

template<class T>
inline MathVector<T> MathVector<T>::operator-( const MathVector<T>& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( mm.size() != size() ) THROW (iwdomain_error,("Cannot add Vector of different size"));

    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
	new (&(nv->elem(i))) T( vr->elem(i) - mm.vr->elem(i) );
    }

    return nv;
}/*}}}*/

template<class T>
template<class S>
inline MathVector<T> MathVector<T>::operator-( const MathVector<S>& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( mm.size() != size() ) THROW(iwdomain_error,("Cannot add Vector of different size"));

    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
	new (&(nv->elem(i))) T( vr->elem(i) - mm[i] );
    }

    return nv;
}/*}}}*/

template<class T>
inline MathVector<T> MathVector<T>::operator+( const MathVector<T>& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( mm.size() != size() ) THROW(iwdomain_error,("Cannot add Vector of different size"));

    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
	new (&(nv->elem(i))) T( vr->elem(i) + mm.vr->elem(i) );
    }

    return nv;
}/*}}}*/

template<class T>
template<class S>
inline MathVector<T> MathVector<T>::operator+( const MathVector<S>& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( mm.size() != size() ) THROW( iwdomain_error,("Cannot add Vector of different size"));

    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
	new (&(nv->elem(i))) T( vr->elem(i) + mm[i] );
    }

    return nv;
}/*}}}*/

template<class T>
template<class S>
inline MathVector<T> MathVector<T>::operator/( const S& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
//	new (&(nv->elem(i))) T(vr->elem(i) / mm);
	put_new_element(&(nv->elem(i)), (vr->elem(i) / mm));
    }

    return nv;
}/*}}}*/

/**
 * This one and all of its specialization are for those cases where an element
 * cannot be copy-constructor converted but must be casted. This is usually
 * only the case when converting from a float type to an integral type.
 * But since there are many of them, we have to create those function for all
 * of them. To make this easy we have a macro for it, which may look template like ;)
 * @note To avoid unnecessary complexity we only really use this when a
 * convertion might be needed, e.g. when another type S occurs in the template
 * list.
 * @todo TODO check if we need to take of cases where long and int are the same
 * types, or similar... And whether we need some other conversions that need casts.
 */
template<class T, class S>
void put_new_element( T* t, const S& s );

#define MK_put_new_element(T,S) \
template<> \
inline void put_new_element( T* t, const S& s ) \
{ \
    new (t) T(static_cast<T>(s)); \
} 

MK_put_new_element(int,double)
MK_put_new_element(long,double)
MK_put_new_element(unsigned int,double)
MK_put_new_element(unsigned long,double)

/**
 * This is the generic inline variant that does no cast... and all of this to
 * just avoid a compiler warning.
 */
template<class T, class S>
inline void put_new_element( T* t, const S& s )
{
    new (t) T(s);
}

/**
 * Here we have the same stuff for just assignments...
 */
template<class T, class S>
inline void assign_mbc( T& t, const S& s );

#define MK_assign_mbc(T,S) \
template<> \
inline void assign_mbc( T& t, const S& s ) \
{ \
    t = static_cast<T>(s); \
}

MK_assign_mbc(int,double)
MK_assign_mbc(long,double)
MK_assign_mbc(unsigned int,double)
MK_assign_mbc(unsigned long,double)

/**
 * This is the generic variant without anything special, just assigning...
 */
template<class T, class S>
inline void assign_mbc( T& t, const S& s )
{
    t = s;
}

template<class T>
template<class S>
inline MathVector<T> MathVector<T>::operator*( const S& mm ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t n = size();
    VectorRepresentation<T>* nv = VectorRepresentation<T>::allocate(n);
    nv->vector_size = n;
    nv->references = 0;

    for ( uint16_t i = 0; i < n; i++ )
    {
	// Note that here the vector is on the left
	put_new_element(&(nv->elem(i)), ((vr->elem(i) * mm)));
    }

    return nv;
}/*}}}*/

template<class T>
inline MathVector<T>& MathVector<T>::operator=( const MathVector<T>& mv )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( size() != mv.size() )
    {
	THROW(iwdomain_error,("Cannot Assign Vector of different size"));
    }
    // Now, thats interesting here ;) we will release the old data and then do
    // a refcopy of the to-be-assigned data
    // But only if this is not a part of a matrix, in this case we do a
    // member-by-member copy/assignment.

    if ( vr->references >=0 )
    {
	vr->release();
	vr = mv.vr->ref_copy();
    }
    else
    {
	uint16_t n = mv.size();

	for ( uint16_t i = 0; i < n; i++ )
	{
	    vr->elem(i) = mv.vr->elem(i);
	}
    }
    return *this;
}/*}}}*/

template<class T>
template<class S> 
inline MathVector<T>& MathVector<T>::operator=( const MathVector<S>& mv )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( size() != mv.size() )
    {
	THROW( iwdomain_error,("Cannot Assign Vector of different size"));
    }
    // Since we assign from another type, we will do first a real-copy of the
    // original one...

    if ( vr->references > 0 )
    {
	vr->references--;
	// We need a brand new and empty vector, but copying is too much since
	// we dont need the data...
	//
	// trying some nice hack with placement new...
	new (this) MathVector<T>(mv);
    }
    else
    {
	// The memory is already there, overwrite
	uint16_t n = mv.size();

	for ( uint16_t i = 0; i < n; i++ )
	{
	    assign_mbc(vr->elem(i), mv[i]);
	}
    }
    return *this;
}/*}}}*/

template<class T>
template<class S> 
inline MathVector<T>::MathVector( const MathVector<S>& mv )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    uint16_t n = mv.size();
    vr = VectorRepresentation<T>::allocate(n);
    vr->references = 0;
    vr->vector_size = n;
    uint16_t i;
    try
    {
	for ( i = 0; i < n; i++ )
	{
	    ///@todo What should we do when this throws an exception ?
	    put_new_element(&(vr->elem(i)) ,mv[i]);
	}
    }
    catch(...)
    {	
	for ( uint16_t j = 0; j < i; j++ )
	{
	    try
	    {
		vr->elem(j).T::~T();
	    }
	    catch(...)
	    {
	    }
	}

	vr->release();
	vr = NULL;
	throw;
    }
}/*}}}*/

template<class T>
inline MathVector<T>::MathVector( const MathVector<T>& mv )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    vr = mv.vr->ref_copy();
}/*}}}*/

template<class T>
inline MathVector<T>::MathVector( uint16_t n , const T& t)/*{{{*/
{    
    //ScopeTracer st(__PRETTY_FUNCTION__);
    vr = VectorRepresentation<T>::allocate(n);
    vr->references = 0;
    vr->vector_size = n;
    uint16_t i;
    try
    {
	for ( i = 0; i < n; i++ )
	{
	    ///@todo What should we do when this throws an exception ?
	    put_new_element(&(vr->elem(i)) ,t);
	}
    }
    catch(...)
    {	
	for ( uint16_t j = 0; j < i; j++ )
	{
	    try
	    {
		vr->elem(j).T::~T();
	    }
	    catch(...)
	    {
	    }
	}

	vr->release();
	vr = NULL;
	throw;
    }

}/*}}}*/

template<class T>
inline MathVector<T>::MathVector( VectorRepresentation<T>& _vr )/*{{{*/
    : vr(&_vr)
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( vr == NULL ) THROW( nullptr_error,("Creation of MathVector"));
}/*}}}*/

template<class T>
inline MathVector<T>::MathVector( VectorRepresentation<T>* _vr )/*{{{*/
    : vr(_vr)
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( vr == NULL ) THROW( nullptr_error,("Creation of MathVector"));
}/*}}}*/

template<class T>
inline MathVector<T>::MathVector( uint16_t n )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    vr = VectorRepresentation<T>::allocate(n);
    vr->references = 0;
    vr->vector_size = n;
    uint16_t i;
    try
    {
	for ( i = 0; i < n; i++ )
	{
	    new (&(vr->elem(i))) T();
	}
    }
    catch(...)
    {
	for ( uint16_t j = 0; j < i; j++ )
	{
	    try
	    {
		vr->elem(j).T::~T();
	    }
	    catch(...)
	    {
	    }
	}

	vr->release();
	vr = NULL;
	throw;
    }
}/*}}}*/

template<class T>
inline MathVector<T>::~MathVector( )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    if ( vr->references >= 0 ) 
    {
	vr->release();
    }
}/*}}}*/

template<class T>
inline void MathVector<T>::taint_me(void)/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
//    cout << "TAINTING MATH VECTOR" << std::endl;
//    cout << "REFCOUNT " << vr->references << std::endl;
    if ( vr->references > 0 )
    {
//	cout << "DOING REAL COPY" << std::endl;
	VectorRepresentation<T>* ovr = vr;
	vr = vr->real_copy();
	ovr->release();
    }
}/*}}}*/

template<class T>
inline T& MathVector<T>::operator[] ( uint16_t n )/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    taint_me();
    return vr->elem(n);
}/*}}}*/

template<class T>
inline const T& MathVector<T>::operator[] ( uint16_t n ) const/*{{{*/
{
    //ScopeTracer st(__PRETTY_FUNCTION__);
    return vr->elem(n);
}/*}}}*/

}
}
#endif
/**
 */
