#ifndef __value_range_h__
#define __value_range_h__

#include <cstdlib>
#include <vector>
#include <memory>
#include <map>

#include "rbase_def.h"
//#include "base_def.h"
#include "ranges/rpheader.h"
#include "ranges/index_base.h"
#include "ranges/range_base.h"
#include "ranges/x_to_string.h"
#include "ranges/type_map.h"

#include "xfor/for_type.h"

namespace MultiArrayTools
{

    namespace
    {
	using namespace MultiArrayHelper;
    }
    
    template <typename U>
    class ValueIndex : public IndexInterface<ValueIndex<U>,U>
    {
    public:

	typedef IndexInterface<ValueIndex<U>,U> IB;
	typedef U MetaType;
	typedef ValueRange<U> RangeType;
	typedef ValueIndex IType;

	ValueIndex(const std::shared_ptr<RangeType>& rptr);

	static constexpr IndexType sType() { return IndexType::SINGLE; }
	static constexpr size_t totalDim() { return 1; }
	static constexpr size_t sDim() { return 1; }

	static constexpr SpaceType STYPE = SpaceType::NONE;
	
	IndexType type() const;
	
	ValueIndex& operator=(size_t pos);
	ValueIndex& operator++();
	ValueIndex& operator--();

	int pp(std::intptr_t idxPtrNum);
	int mm(std::intptr_t idxPtrNum);

	std::string stringMeta() const;
	const U& meta() const;
	const U* metaPtr() const;
	ValueIndex& at(const U& metaPos);
	size_t posAt(const U& metaPos) const;
	
	size_t dim(); // = 1
	bool last();
	bool first();

	std::shared_ptr<RangeType> range();
	
	template <size_t N>
	void getPtr();

	size_t getStepSize(size_t n);
	
	std::string id() const;
	void print(size_t offset);

	template <class Expr>
	auto ifor(size_t step, Expr ex) const
	    -> For<ValueIndex<U>,Expr>;

	template <class Expr>
	auto iforh(size_t step, Expr ex) const
	    -> For<ValueIndex<U>,Expr,ForType::HIDDEN>;
	
    private:
	std::shared_ptr<RangeType> mExplicitRangePtr;
	U mMeta;
    };

    template <typename U>
    class ValueRangeFactory : public RangeFactoryBase
    {
    public:

	typedef ValueRange<U> oType;
	
	ValueRangeFactory();
	std::shared_ptr<RangeBase> create();
    };

    template <typename U>
    class ValueRange : public RangeInterface<ValueIndex<U> >
    {
    public:
	typedef RangeBase RB;
	typedef ValueIndex<U> IndexType;
	typedef ValueRange RangeType;
	typedef U MetaType;
        typedef ValueRangeFactory<U> FType;
        
	virtual size_t size() const final;
	virtual size_t dim() const final;

	virtual SpaceType spaceType() const final;
        virtual DataHeader dataHeader() const final;
        
        virtual size_t typeNum() const final;
        virtual size_t cmeta(char* target, size_t pos) const final;
	virtual std::string stringMeta(size_t pos) const final;
	virtual vector<char> data() const final;

        U get(size_t pos) const;
        
	virtual IndexType begin() const final;
	virtual IndexType end() const final;
		
	friend ValueRangeFactory<U>;

	static constexpr bool defaultable = true;
	static constexpr size_t ISSTATIC = 1;
	static constexpr size_t SIZE = 1;
	static constexpr bool HASMETACONT = false;

	static std::shared_ptr<ValueRange> Default()
	{
	    ValueRangeFactory<U> vrf;
	    return std::dynamic_pointer_cast<ValueRange>( vrf.create() );
	}
	
    protected:
        mutable U const* mMeta;
	ValueRange() = default;
    };
    
} // namespace MultiArrayTools

/* ========================= *
 * ---   TEMPLATE CODE   --- *
 * ========================= */

namespace MultiArrayTools
{
    /*****************
     *  ValueIndex   *	     
     *****************/

    namespace
    {
	template<typename U>
	std::shared_ptr<RangeBase> mkDefaultValueRange()
	{
	    ValueRangeFactory<U> vrf;
	    return vrf.create();
	}
    }
    
    template <typename U>
    ValueIndex<U>::ValueIndex(const std::shared_ptr<RangeType>& rptr) :
	IndexInterface<ValueIndex<U>,U>(rptr, 0),
	mExplicitRangePtr(std::dynamic_pointer_cast<RangeType>(IB::mRangePtr))
    {}

    template <typename U>
    IndexType ValueIndex<U>::type() const
    {
	return IndexType::SINGLE;
    }

    template <typename U>
    ValueIndex<U>& ValueIndex<U>::operator=(size_t pos)
    {
	IB::mPos = pos;
	return *this;
    }

    template <typename U>
    ValueIndex<U>& ValueIndex<U>::operator++()
    {
	++IB::mPos;
	return *this;
    }

    template <typename U>
    ValueIndex<U>& ValueIndex<U>::operator--()
    {
	--IB::mPos;
	return *this;
    }

    template <typename U>
    int ValueIndex<U>::pp(std::intptr_t idxPtrNum)
    {
	++(*this);
	return 1;
    }

    template <typename U>
    int ValueIndex<U>::mm(std::intptr_t idxPtrNum)
    {
	--(*this);
	return 1;
    }

    template <typename U>
    std::string ValueIndex<U>::stringMeta() const
    {
	return xToString(mMeta);
    }
    
    template <typename U>
    const U& ValueIndex<U>::meta() const
    {
	return mMeta;
    }

    template <typename U>
    const U* ValueIndex<U>::metaPtr() const
    {
	return &mMeta;
    }
    
    template <typename U>
    ValueIndex<U>& ValueIndex<U>::at(const U& metaPos)
    {
	mMeta = metaPos;
	return *this;
    }

    template <typename U>
    size_t ValueIndex<U>::posAt(const U& metaPos) const
    {
	return 0;
    }
    
    template <typename U>
    size_t ValueIndex<U>::dim() // = 1
    {
	return 1;
    }

    template <typename U>
    bool ValueIndex<U>::last()
    {
	return IB::mPos == IB::mMax - 1;
    }

    template <typename U>
    bool ValueIndex<U>::first()
    {
	return IB::mPos == 0;
    }

    template <typename U>
    std::shared_ptr<typename ValueIndex<U>::RangeType> ValueIndex<U>::range()
    {
	return mExplicitRangePtr;
    }

    template <typename U>
    template <size_t N>
    void ValueIndex<U>::getPtr() {}
    
    template <typename U>
    size_t ValueIndex<U>::getStepSize(size_t n)
    {
	return 1;
    }

    template <typename U>
    std::string ValueIndex<U>::id() const
    {
	return std::string("val") + std::to_string(IB::mId);
    }

    template <typename U>
    void ValueIndex<U>::print(size_t offset)
    {
	if(offset == 0){
	    std::cout << " === " << std::endl;
	}
	for(size_t j = 0; j != offset; ++j) { std::cout << "\t"; }
	std::cout << id() << "[" << reinterpret_cast<std::intptr_t>(this)
		  << "](" << IB::mRangePtr << "): " << meta() << std::endl;
    }

    template <typename U>
    template <class Expr>
    auto ValueIndex<U>::ifor(size_t step, Expr ex) const
	-> For<ValueIndex<U>,Expr>
    {
	//static const size_t LAYER = typename Expr::LAYER; 
	return For<ValueIndex<U>,Expr>(this, step, ex);
    }

    template <typename U>
    template <class Expr>
    auto ValueIndex<U>::iforh(size_t step, Expr ex) const
	-> For<ValueIndex<U>,Expr,ForType::HIDDEN>
    {
	//static const size_t LAYER = typename Expr::LAYER; 
	return For<ValueIndex<U>,Expr,ForType::HIDDEN>(this, step, ex);
    }


    /**************************
     *   ValueRangeFactory    *
     **************************/


    template <typename U>
    ValueRangeFactory<U>::ValueRangeFactory()
    {
	mProd = std::shared_ptr<oType>( new ValueRange<U>() );
    }

    template <typename U>
    std::shared_ptr<RangeBase> ValueRangeFactory<U>::create()
    {
	setSelf();
	return mProd;
    }


    /*******************
     *   ValueRange    *
     *******************/

    template <typename U>
    size_t ValueRange<U>::size() const
    {
	return 1;
    }

    template <typename U>
    size_t ValueRange<U>::dim() const
    {
	return 1;
    }

    template <typename U>
    SpaceType ValueRange<U>::spaceType() const
    {
	return SpaceType::NONE;
    }

    template <typename U>
    size_t ValueRange<U>::typeNum() const
    {
        return NumTypeMap<U>::num;
    }
        
    template <typename U>
    size_t ValueRange<U>::cmeta(char* target, size_t pos) const
    {
        if(target){
            *reinterpret_cast<U*>(target) = *mMeta;
        }
        return sizeof(U);
    }

    template <typename U>
    std::string ValueRange<U>::stringMeta(size_t pos) const
    {
	return "";
    }

    template <typename U>
    U ValueRange<U>::get(size_t pos) const
    {
        return *mMeta;
    }
    
    template <typename U>
    vector<char> ValueRange<U>::data() const
    {
	assert(0);
	DataHeader h = dataHeader();
	vector<char> out;
	out.reserve(h.metaSize + sizeof(DataHeader));
	char* hcp = reinterpret_cast<char*>(&h);
	out.insert(out.end(), hcp, hcp + sizeof(DataHeader));
	
	//const char* scp = reinterpret_cast<const char*>(mSpace.data());
	//out.insert(out.end(), scp, scp + h.metaSize);
	return out;
    }

    template <typename U>
    DataHeader ValueRange<U>::dataHeader() const
    {
	DataHeader h;
	h.spaceType = static_cast<int>( SpaceType::NONE );
	h.metaSize = 0;
	h.metaType = NumTypeMap<U>::num;
	h.multiple = 0;
        return h;
    }

    template <typename U>
    typename ValueRange<U>::IndexType ValueRange<U>::begin() const
    {
	ValueIndex<U> i( std::dynamic_pointer_cast<ValueRange<U> >
			 ( std::shared_ptr<RangeBase>( RB::mThis ) ) );
	i = 0;
        mMeta = &i.meta();
	return i;
    }
    
    template <typename U>
    typename ValueRange<U>::IndexType ValueRange<U>::end() const
    {
	ValueIndex<U> i( std::dynamic_pointer_cast<ValueRange<U> >
			 ( std::shared_ptr<RangeBase>( RB::mThis ) ) );
	i = size();
        mMeta = &i.meta();
	return i;
    }


} // namespace MultiArrayTools

#endif