/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_GGL_CONTEXT_H
#define ANDROID_GGL_CONTEXT_H

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <endian.h>

#include <pixelflinger/pixelflinger.h>
#include <private/pixelflinger/ggl_fixed.h>

namespace android {

// ----------------------------------------------------------------------------

#if BYTE_ORDER == LITTLE_ENDIAN

inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
    return v;
}
inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
    return v;
}

#else

inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
#if defined(__mips__) && __mips_isa_rev>=2
    uint32_t r;
    __asm__("wsbh %0, %1;"
        "rotr %0, %0, 16"
        : "=r" (r)
        : "r" (v)
        );
    return r;
#else
    return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
#endif
}
inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
#if defined(__mips__) && __mips_isa_rev>=2
    uint32_t r;
    __asm__("wsbh %0, %1;"
        "rotr %0, %0, 16"
        : "=r" (r)
        : "r" (v)
        );
    return r;
#else
    return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
#endif
}

#endif

// ----------------------------------------------------------------------------

const int GGL_DITHER_BITS = 6;  // dither weights stored on 6 bits
const int GGL_DITHER_ORDER_SHIFT= 3;
const int GGL_DITHER_ORDER      = (1<<GGL_DITHER_ORDER_SHIFT);
const int GGL_DITHER_SIZE       = GGL_DITHER_ORDER * GGL_DITHER_ORDER;
const int GGL_DITHER_MASK       = GGL_DITHER_ORDER-1;

// ----------------------------------------------------------------------------

const int GGL_SUBPIXEL_BITS = 4;

// TRI_FRACTION_BITS defines the number of bits we want to use
// for the sub-pixel coordinates during the edge stepping, the
// value shouldn't be more than 7, or bad things are going to
// happen when drawing large triangles (8 doesn't work because
// 32 bit muls will loose the sign bit)

#define  TRI_FRACTION_BITS  (GGL_SUBPIXEL_BITS)
#define  TRI_ONE            (1 << TRI_FRACTION_BITS)
#define  TRI_HALF           (1 << (TRI_FRACTION_BITS-1))
#define  TRI_FROM_INT(x)    ((x) << TRI_FRACTION_BITS)
#define  TRI_FRAC(x)        ((x)                 &  (TRI_ONE-1))
#define  TRI_FLOOR(x)       ((x)                 & ~(TRI_ONE-1))
#define  TRI_CEIL(x)        (((x) + (TRI_ONE-1)) & ~(TRI_ONE-1))
#define  TRI_ROUND(x)       (((x) +  TRI_HALF  ) & ~(TRI_ONE-1))

#define  TRI_ROUDNING       (1 << (16 - TRI_FRACTION_BITS - 1))
#define  TRI_FROM_FIXED(x)  (((x)+TRI_ROUDNING) >> (16-TRI_FRACTION_BITS))

#define  TRI_SNAP_NEXT_HALF(x)   (TRI_CEIL((x)+TRI_HALF) - TRI_HALF)
#define  TRI_SNAP_PREV_HALF(x)   (TRI_CEIL((x)-TRI_HALF) - TRI_HALF)

// ----------------------------------------------------------------------------

const int GGL_COLOR_BITS = 24;

// To maintain 8-bits color chanels, with a maximum GGLSurface
// size of 4096 and GGL_SUBPIXEL_BITS=4, we need 8 + 12 + 4 = 24 bits
// for encoding the color iterators

inline GGLcolor gglFixedToIteratedColor(GGLfixed c) {
    return (c << 8) - c;
}

// ----------------------------------------------------------------------------

template<bool> struct CTA;
template<> struct CTA<true> { };

#define GGL_CONTEXT(con, c)         context_t *(con) = static_cast<context_t *>(c) /* NOLINT */
#define GGL_OFFSETOF(field)         uintptr_t(&(((context_t*)0)->field))
#define GGL_INIT_PROC(p, f)         p.f = ggl_ ## f;
#define GGL_BETWEEN(x, L, H)        (uint32_t((x)-(L)) <= ((H)-(L)))

#define ggl_likely(x)	__builtin_expect(!!(x), 1)
#define ggl_unlikely(x)	__builtin_expect(!!(x), 0)

const int GGL_TEXTURE_UNIT_COUNT    = 2;
const int GGL_TMU_STATE             = 0x00000001;
const int GGL_CB_STATE              = 0x00000002;
const int GGL_PIXEL_PIPELINE_STATE  = 0x00000004;

// ----------------------------------------------------------------------------

#define GGL_RESERVE_NEEDS(name, l, s)                               \
    const uint32_t  GGL_NEEDS_##name##_MASK = (((1LU<<(s))-1)<<(l));  \
    const uint32_t  GGL_NEEDS_##name##_SHIFT = (l);

#define GGL_BUILD_NEEDS(val, name)                                  \
    (((val)<<(GGL_NEEDS_##name##_SHIFT)) & GGL_NEEDS_##name##_MASK)

#define GGL_READ_NEEDS(name, n)                                     \
    (uint32_t((n) & GGL_NEEDS_##name##_MASK) >> GGL_NEEDS_##name##_SHIFT)

#define GGL_NEED_MASK(name)     (uint32_t(GGL_NEEDS_##name##_MASK))
#define GGL_NEED(name, val)     GGL_BUILD_NEEDS(val, name)

GGL_RESERVE_NEEDS( CB_FORMAT,       0, 6 )
GGL_RESERVE_NEEDS( SHADE,           6, 1 )
GGL_RESERVE_NEEDS( W,               7, 1 )
GGL_RESERVE_NEEDS( BLEND_SRC,       8, 4 )
GGL_RESERVE_NEEDS( BLEND_DST,      12, 4 )
GGL_RESERVE_NEEDS( BLEND_SRCA,     16, 4 )
GGL_RESERVE_NEEDS( BLEND_DSTA,     20, 4 )
GGL_RESERVE_NEEDS( LOGIC_OP,       24, 4 )
GGL_RESERVE_NEEDS( MASK_ARGB,      28, 4 )

GGL_RESERVE_NEEDS( P_ALPHA_TEST,    0, 3 )
GGL_RESERVE_NEEDS( P_AA,            3, 1 )
GGL_RESERVE_NEEDS( P_DEPTH_TEST,    4, 3 )
GGL_RESERVE_NEEDS( P_MASK_Z,        7, 1 )
GGL_RESERVE_NEEDS( P_DITHER,        8, 1 )
GGL_RESERVE_NEEDS( P_FOG,           9, 1 )
GGL_RESERVE_NEEDS( P_RESERVED1,    10,22 )

GGL_RESERVE_NEEDS( T_FORMAT,        0, 6 )
GGL_RESERVE_NEEDS( T_RESERVED0,     6, 1 )
GGL_RESERVE_NEEDS( T_POT,           7, 1 )
GGL_RESERVE_NEEDS( T_S_WRAP,        8, 2 )
GGL_RESERVE_NEEDS( T_T_WRAP,       10, 2 )
GGL_RESERVE_NEEDS( T_ENV,          12, 3 )
GGL_RESERVE_NEEDS( T_LINEAR,       15, 1 )

const int GGL_NEEDS_WRAP_CLAMP_TO_EDGE  = 0;
const int GGL_NEEDS_WRAP_REPEAT         = 1;
const int GGL_NEEDS_WRAP_11             = 2;

inline uint32_t ggl_wrap_to_needs(uint32_t e) {
    switch (e) {
    case GGL_CLAMP:         return GGL_NEEDS_WRAP_CLAMP_TO_EDGE;
    case GGL_REPEAT:        return GGL_NEEDS_WRAP_REPEAT;
    }
    return 0;
}

inline uint32_t ggl_blendfactor_to_needs(uint32_t b) {
    if (b <= 1) return b;
    return (b & 0xF)+2;
}

inline uint32_t ggl_needs_to_blendfactor(uint32_t n) {
    if (n <= 1) return n;
    return (n - 2) + 0x300;
}

inline uint32_t ggl_env_to_needs(uint32_t e) {
    switch (e) {
    case GGL_REPLACE:   return 0;
    case GGL_MODULATE:  return 1;
    case GGL_DECAL:     return 2;
    case GGL_BLEND:     return 3;
    case GGL_ADD:       return 4;
    }
    return 0;
}

inline uint32_t ggl_needs_to_env(uint32_t n) {
    const uint32_t envs[] = { GGL_REPLACE, GGL_MODULATE, 
            GGL_DECAL, GGL_BLEND, GGL_ADD };
    return envs[n];

}

// ----------------------------------------------------------------------------

enum {
    GGL_ENABLE_BLENDING     = 0x00000001,
    GGL_ENABLE_SMOOTH       = 0x00000002,
    GGL_ENABLE_AA           = 0x00000004,
    GGL_ENABLE_LOGIC_OP     = 0x00000008,
    GGL_ENABLE_ALPHA_TEST   = 0x00000010,
    GGL_ENABLE_SCISSOR_TEST = 0x00000020,
    GGL_ENABLE_TMUS         = 0x00000040,
    GGL_ENABLE_DEPTH_TEST   = 0x00000080,
    GGL_ENABLE_STENCIL_TEST = 0x00000100,
    GGL_ENABLE_W            = 0x00000200,
    GGL_ENABLE_DITHER       = 0x00000400,
    GGL_ENABLE_FOG          = 0x00000800,
    GGL_ENABLE_POINT_AA_NICE= 0x00001000
};

// ----------------------------------------------------------------------------

struct needs_filter_t;
struct needs_t {
    inline int match(const needs_filter_t& filter);
    inline bool operator == (const needs_t& rhs) const {
        return  (n==rhs.n) &&
                (p==rhs.p) &&
                (t[0]==rhs.t[0]) &&
                (t[1]==rhs.t[1]);
    }
    inline bool operator != (const needs_t& rhs) const {
        return !operator == (rhs);
    }
    uint32_t    n;
    uint32_t    p;
    uint32_t    t[GGL_TEXTURE_UNIT_COUNT];
};

inline int compare_type(const needs_t& lhs, const needs_t& rhs) {
    return memcmp(&lhs, &rhs, sizeof(needs_t));
}

struct needs_filter_t {
    needs_t     value;
    needs_t     mask;
};

int needs_t::match(const needs_filter_t& filter) {
    uint32_t result = 
        ((filter.value.n ^ n)       & filter.mask.n)    |
        ((filter.value.p ^ p)       & filter.mask.p)    |
        ((filter.value.t[0] ^ t[0]) & filter.mask.t[0]) |
        ((filter.value.t[1] ^ t[1]) & filter.mask.t[1]);
    return (result == 0);
}

// ----------------------------------------------------------------------------

struct context_t;
class Assembly;

struct blend_state_t {
	uint32_t			src;
	uint32_t			dst;
	uint32_t			src_alpha;
	uint32_t			dst_alpha;
	uint8_t				reserved;
	uint8_t				alpha_separate;
	uint8_t				operation;
	uint8_t				equation;
};

struct mask_state_t {
    uint8_t             color;
    uint8_t             depth;
    uint32_t            stencil;
};

struct clear_state_t {
    GGLclampx           r;
    GGLclampx           g;
    GGLclampx           b;
    GGLclampx           a;
    GGLclampx           depth;
    GGLint              stencil;
    uint32_t            colorPacked;
    uint32_t            depthPacked;
    uint32_t            stencilPacked;
    uint32_t            dirty;
};

struct fog_state_t {
    uint8_t     color[4];
};

struct logic_op_state_t {
    uint16_t            opcode;
};

struct alpha_test_state_t {
    uint16_t            func;
    GGLcolor            ref;
};

struct depth_test_state_t {
    uint16_t            func;
    GGLclampx           clearValue;
};

struct scissor_t {
    uint32_t            user_left;
    uint32_t            user_right;
    uint32_t            user_top;
    uint32_t            user_bottom;
    uint32_t            left;
    uint32_t            right;
    uint32_t            top;
    uint32_t            bottom;
};

struct pixel_t {
    uint32_t    c[4];
    uint8_t     s[4];
};

struct surface_t {
    union {
        GGLSurface          s;
        // Keep the following struct field types in line with the corresponding
        // GGLSurface fields to avoid mismatches leading to errors.
        struct {
            GGLsizei        reserved;
            GGLuint         width;
            GGLuint         height;
            GGLint          stride;
            GGLubyte*       data;
            GGLubyte        format;
            GGLubyte        dirty;
            GGLubyte        pad[2];
        };
    };
    void                (*read) (const surface_t* s, context_t* c,
                                uint32_t x, uint32_t y, pixel_t* pixel);
    void                (*write)(const surface_t* s, context_t* c,
                                uint32_t x, uint32_t y, const pixel_t* pixel);
};

// ----------------------------------------------------------------------------

struct texture_shade_t {
    union {
        struct {
            int32_t             is0;
            int32_t             idsdx;
            int32_t             idsdy;
            int                 sscale;
            int32_t             it0;
            int32_t             idtdx;
            int32_t             idtdy;
            int                 tscale;
        };
        struct {
            int32_t             v;
            int32_t             dx;
            int32_t             dy;
            int                 scale;
        } st[2];
    };
};

struct texture_iterators_t {
    // these are not encoded in the same way than in the
    // texture_shade_t structure
    union {
        struct {
            GGLfixed			ydsdy;
            GGLfixed            dsdx;
            GGLfixed            dsdy;
            int                 sscale;
            GGLfixed			ydtdy;
            GGLfixed            dtdx;
            GGLfixed            dtdy;
            int                 tscale;
        };
        struct {
            GGLfixed			ydvdy;
            GGLfixed            dvdx;
            GGLfixed            dvdy;
            int                 scale;
        } st[2];
    };
};

struct texture_t {
	surface_t			surface;
	texture_iterators_t	iterators;
    texture_shade_t     shade;
	uint32_t			s_coord;
	uint32_t            t_coord;
	uint16_t			s_wrap;
	uint16_t            t_wrap;
	uint16_t            min_filter;
	uint16_t            mag_filter;
    uint16_t            env;
    uint8_t             env_color[4];
	uint8_t				enable;
	uint8_t				dirty;
};

struct raster_t {
    GGLfixed            x;
    GGLfixed            y;
};

struct framebuffer_t {
    surface_t           color;
    surface_t           read;
	surface_t			depth;
	surface_t			stencil;
    int16_t             *coverage;
    size_t              coverageBufferSize;
};

// ----------------------------------------------------------------------------

struct iterators_t {
	int32_t             xl;
	int32_t             xr;
    int32_t             y;
	GGLcolor			ydady;
	GGLcolor			ydrdy;
	GGLcolor			ydgdy;
	GGLcolor			ydbdy;
	GGLfixed			ydzdy;
	GGLfixed			ydwdy;
	GGLfixed			ydfdy;
};

struct shade_t {
	GGLcolor			a0;
    GGLcolor            dadx;
    GGLcolor            dady;
	GGLcolor			r0;
    GGLcolor            drdx;
    GGLcolor            drdy;
	GGLcolor			g0;
    GGLcolor            dgdx;
    GGLcolor            dgdy;
	GGLcolor			b0;
    GGLcolor            dbdx;
    GGLcolor            dbdy;
	uint32_t            z0;
    GGLfixed32          dzdx;
    GGLfixed32          dzdy;
	GGLfixed            w0;
    GGLfixed            dwdx;
    GGLfixed            dwdy;
	uint32_t			f0;
    GGLfixed            dfdx;
    GGLfixed            dfdy;
};

// these are used in the generated code
// we use this mirror structure to improve
// data locality in the pixel pipeline
struct generated_tex_vars_t {
    uint32_t    width;
    uint32_t    height;
    uint32_t    stride;
    uintptr_t   data;
    int32_t     dsdx;
    int32_t     dtdx;
    int32_t     spill[2];
};

struct generated_vars_t {
    struct {
        int32_t c;
        int32_t dx;
    } argb[4];
    int32_t     aref;
    int32_t     dzdx;
    int32_t     zbase;
    int32_t     f;
    int32_t     dfdx;
    int32_t     spill[3];
    generated_tex_vars_t    texture[GGL_TEXTURE_UNIT_COUNT];
    int32_t     rt;
    int32_t     lb;
};

// ----------------------------------------------------------------------------

struct state_t {
	framebuffer_t		buffers;
	texture_t			texture[GGL_TEXTURE_UNIT_COUNT];
    scissor_t           scissor;
    raster_t            raster;
	blend_state_t		blend;
    alpha_test_state_t  alpha_test;
    depth_test_state_t  depth_test;
    mask_state_t        mask;
    clear_state_t       clear;
    fog_state_t         fog;
    logic_op_state_t    logic_op;
    uint32_t            enables;
    uint32_t            enabled_tmu;
    needs_t             needs;
};

// ----------------------------------------------------------------------------

struct context_t {
	GGLContext          procs;
	state_t             state;
    shade_t             shade;
	iterators_t         iterators;
    generated_vars_t    generated_vars                __attribute__((aligned(32)));
    uint8_t             ditherMatrix[GGL_DITHER_SIZE] __attribute__((aligned(32)));
    uint32_t            packed;
    uint32_t            packed8888;
    const GGLFormat*    formats;
    uint32_t            dirty;
    texture_t*          activeTMU;
    uint32_t            activeTMUIndex;

    void                (*init_y)(context_t* c, int32_t y);
	void                (*step_y)(context_t* c);
	void                (*scanline)(context_t* c);
    void                (*span)(context_t* c);
    void                (*rect)(context_t* c, size_t yc);
    
    void*               base;
    Assembly*           scanline_as;
    GGLenum             error;
};

// ----------------------------------------------------------------------------

void ggl_init_context(context_t* context);
void ggl_uninit_context(context_t* context);
void ggl_error(context_t* c, GGLenum error);
int64_t ggl_system_time();

// ----------------------------------------------------------------------------

};

#endif // ANDROID_GGL_CONTEXT_H

