/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2009 Pelican Ventures, Inc.
 * http://osgearth.org
 *
 * osgEarth 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program.  If not, see <http://www.gnu.org/licenses/>
 */

// Poor man's multi array adapter.

#ifndef SEAMLESS_MULTIARRAY
#define SEAMLESS_MULTIARRAY 1

#include <algorithm>

namespace seamless
{

template<typename ElementType, typename Store, unsigned N> class Reference;

template<typename ElementType, typename Store, unsigned N>
class SubArraySimple : public Reference<ElementType, Store, N>
{
public:
    typedef Reference<ElementType, Store, N> super_type;
    typedef typename super_type::reference reference;
    SubArraySimple(int base, Store& store, const int* strides)
        : super_type(base), _store(store), _strides(strides)
    {
    }

    SubArraySimple(const SubArraySimple& rhs);
    
    reference operator[](int index)
    {
        return super_type::access(index, _store, _strides);
    }
protected:
    Store& _store;
    const int* _strides;
};

template<typename ElementType, typename Store, unsigned N>
class SubArray : public SubArraySimple<ElementType, Store, N>
{
    typedef SubArraySimple<ElementType, Store, N> super_type;
    typedef typename super_type::reference reference;
public:
    SubArray(int base, Store& store,
             const int* strides, const int* indexBase)
        : super_type(base, store, strides), _indexBase(indexBase)
    {
    }

    SubArray(const SubArray& rhs)
        : super_type(rhs), _indexBase(rhs._indexBase)
    {
    }
    
    reference operator[](int index)
    {
        return super_type::access(index, this->_store, this->_strides,
                                  _indexBase);
    }
protected:
    const int* _indexBase;
};


template<typename ElementType, typename Store, unsigned N>
class Reference
{
public:
    typedef SubArraySimple<ElementType, Store, N - 1> simple_reference;
    typedef SubArray<ElementType, Store, N - 1> reference;
    Reference(int base) : _base(base) {}

    Reference(const Reference& rhs) : _base(rhs._base) {}

    simple_reference access(int index, Store& store, const int* strides) const
    {
        int newbase = _base + index * strides[0];
        return simple_reference(newbase, store, strides + 1);
    }

    reference access(int index, Store& store, const int* strides,
                     const int* indexBase) const
    {
        int newbase = _base + (index - indexBase[0]) * strides[0];
        return reference(newbase, store, indexBase + 1, strides + 1);
    }
    const int _base;
};

template<typename ElementType, typename Store>
class Reference<ElementType, Store, 1>
{
public:
    Reference(int base) : _base(base) {}
    Reference(const Reference& rhs) : _base(rhs._base) {}
    
    typedef ElementType& simple_reference;
    typedef ElementType& reference;

    simple_reference access(int index, Store& store, const int* strides)
    {
        return store[_base + index * strides[0]];
    }
    
    reference access(int index, Store& store, const int* strides,
                     const int* indexBase)
    {
        return store[_base + (index - indexBase[0]) * strides[0]];
    }

    const int _base;
};

template<typename ElementType, typename Store, unsigned N>
inline SubArraySimple<ElementType, Store, N>::SubArraySimple(
    const SubArraySimple& rhs)
    : super_type(rhs), _store(rhs._store), _strides(rhs._strides)
{
}
    
template<typename ElementType, typename Store, unsigned N>
class multi_array_ref : public Reference<ElementType, Store, N>
{
public:
    typedef Reference<ElementType, Store, N> super_type;
    typedef typename super_type::simple_reference simple_reference;
    multi_array_ref(Store& store, unsigned dimension, int base = 0)
        : _store(store), super_type(base)
    {
        std::fill_n(&_shape[0], N, dimension);
        std::fill_n(&_indexBase[0], N, 0);
        _strides[N - 1] = 1;
        for (int i = N - 2; i >= 0; --i)
        {
            _strides[i] = dimension * _strides[i + 1];
        }
    }

    multi_array_ref(Store& store, int *strides, int *shape, int base)
        : _store(store), super_type(base)
    {
        std::copy(&shape[0], &shape[N], &_shape[0]);
        std::fill_n(&_indexBase[0], N, 0);
        std::copy(&strides[0], &strides[N], &_strides[0]);
        
    }

    multi_array_ref(const multi_array_ref& rhs)
        : super_type(rhs), _store(rhs._store)
    {
        std::copy(&rhs._shape[0], &rhs._shape[N], &_shape[0]);
        std::copy(&rhs._indexBase[0], &rhs._indexBase[N], &_indexBase[0]);
        std::copy(&rhs._strides[0], &rhs._strides[N], &_strides[0]);
    }
    
    simple_reference operator[](int index)
    {
        return super_type::access(index, _store, _strides);
    }

    int shape(int n) const { return _shape[n]; }
    int indexBase(int n) const { return _indexBase[n]; }
    int stride(int n) const { return _strides[n]; }
protected:
    Store& _store;
    int _shape[N];
    int _indexBase[N];
    int _strides[N];
};

template<typename ElementType, typename Store>
class vector_ref : public multi_array_ref<ElementType, Store, 1>
{
public:
    typedef multi_array_ref<ElementType, Store, 1> super_type;

    vector_ref(Store& store, int stride, int shape, int base)
        : super_type(store, &stride, &shape, base)
    {
    }

    int shape() const { return super_type::shape(0); }
};

}
#endif
