/*
 *  foundation/Math/ByteColor.hpp
 *  
 *
 *  Created by thant on 3/10/2011.
 *  Copyright 2011 TeleNav Inc. All rights reserved.
 *
 */

#ifndef Foundation_Math_ByteColor_INCLUDED
#define Foundation_Math_ByteColor_INCLUDED

#include <assert.h>

#include <foundation/SharedPtr.h>
#include <foundation/WeakPtr.h>

#include <foundation/TnTypes.h>

#include <foundation/Math/misc.hpp>

namespace Tn {

namespace math {

template <typename T>
struct Array2 {

    typedef Tn::Foundation::shared_ptr<Array2<T> > ptr;
    typedef Tn::Foundation::weak_ptr<Array2<T> > wptr;

    Array2(int32_t w, int32_t h) : width(w), height(h), data(new T[width*height]) {
        // uninitialized values
    }

    Array2(int32_t w, int32_t h, T const & v) : width(w), height(h), data(new T[width*height]) {
        set(v);
    }

    ~Array2() {delete [] data;}

    void set(T const & value) {
        T* end = data + width * height;
        for (T* i = data; i < end; ++i) *i = value;
    }

    int32_t getWidth() const {return width;}
    int32_t getHeight() const {return height;}

    /* Unchecked. Allows array[y][x] style indexing. */

    T const * operator[](int32_t y) const {return data + y * width;}
    T* operator[](int32_t y) {return data + y * width;}

    /* This works even if the destination lies outside the source. The
     * items in the destination array that don't have a corresponding
     * source value are set to 'value'. */

    typename Array2<T>::ptr sub(T const & value, int32_t dest_x, int32_t dest_y, int32_t dest_w, int32_t dest_h) {

        assert((dest_w>=0) && (dest_h>=0));

        typename Array2<T>::ptr result = typename Array2<T>::ptr(new Array2<T>(dest_w, dest_h, value));

        int32_t src_left = tnmax(0, dest_x);
        int32_t src_right = tnmin(width, dest_w + dest_x);
        int32_t actual_width = src_right - src_left;

        int32_t src_bottom = tnmax(0, dest_y);
        int32_t src_top = tnmin(height, dest_h + dest_y);
        int32_t actual_height = src_top - src_bottom;

        int32_t dest_left = Tn::math::tnmax(-dest_x, 0);
        //int32_t dest_right = Tn::math::tnmin(width - dest_x, dest_w);

        int32_t dest_bottom = Tn::math::tnmax(-dest_y, 0);
        //int32_t dest_top = Tn::math::tnmin(dest_h, height - dest_y, dest_h);

        for (int32_t j = 0; j < actual_height; ++j)
        {
            T const * from = data + (j+src_bottom) * width + src_left;
            T* to = result->data + (j+dest_bottom) * dest_w + dest_left;
            for (int32_t i = 0; i < actual_width; ++i)
            {
                *to++ = *from++;
            }
        }

        return result;
    }
    
    size_t Size() const { return sizeof(T)*width*height; }

private:

    int32_t width;
    int32_t height;
    T* data;

    Array2(const Array2<T>&); 		// not defined
    Array2& operator=(const Array2<T>&);	// not defined
};


} // end namespace math

} // end namespace Tn


#endif
