//
//  Basic video functions
//
//! \file tonc_oam.h
//! \author J Vijn
//! \date 20060604 - 20060604
//
// === NOTES ===
// * Basic video-IO, color, background and object functionality

#ifndef TONC_OAM
#define TONC_OAM

#include "tonc_memmap.h"
#include "tonc_memdef.h"
#include "tonc_core.h"
#ifdef CODE_COMPILED_AS_PART_OF_TEST
#include "tonc_math_stub.h"
#else
#include "tonc_math.h"
#endif


// --------------------------------------------------------------------
// OBJECTS
// --------------------------------------------------------------------


//! \addtogroup grpVideoObj
/*!	\{	*/

#define OAM_CLEAR()	memset32(oam_mem, 0, OAM_SIZE/4)

// --- Prototypes -----------------------------------------------------

// --- Full OAM ---
void oam_init(OBJ_ATTR *obj, uint count);
INLINE void oam_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count);

// --- Obj attr only ---
INLINE OBJ_ATTR *obj_set_attr(OBJ_ATTR *obj, u16 a0, u16 a1, u16 a2);
INLINE void obj_set_pos(OBJ_ATTR *obj, int x, int y);
INLINE void obj_hide(OBJ_ATTR *oatr);
INLINE void obj_unhide(OBJ_ATTR *obj, u16 mode);

INLINE const u8 *obj_get_size(const OBJ_ATTR *obj);
INLINE int obj_get_width(const OBJ_ATTR *obj);
INLINE int obj_get_height(const OBJ_ATTR *obj);

void obj_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count);
void obj_hide_multi(OBJ_ATTR *obj, u32 count);
void obj_unhide_multi(OBJ_ATTR *obj, u16 mode, uint count);

// --- Obj affine only ---
void obj_aff_copy(OBJ_AFFINE *dst, const OBJ_AFFINE *src, uint count);

INLINE void obj_aff_set(OBJ_AFFINE *oaff,
                        FIXED pa, FIXED pb, FIXED pc, FIXED pd);
INLINE void obj_aff_identity(OBJ_AFFINE *oaff);
INLINE void obj_aff_scale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy);
INLINE void obj_aff_shearx(OBJ_AFFINE *oaff, FIXED hx);
INLINE void obj_aff_sheary(OBJ_AFFINE *oaff, FIXED hy);

void obj_aff_rotate(OBJ_AFFINE *oaff, u16 alpha);
void obj_aff_rotscale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy, u16 alpha);
void obj_aff_premul(OBJ_AFFINE *dst, const OBJ_AFFINE *src);
void obj_aff_postmul(OBJ_AFFINE *dst, const OBJ_AFFINE *src);

void obj_aff_rotscale2(OBJ_AFFINE *oaff, const AFF_SRC *as);
void obj_rotscale_ex(OBJ_ATTR *obj, OBJ_AFFINE *oaff, const AFF_SRC_EX *asx);


// inverse (object -> screen) functions, could be useful
// inverses (prototypes)
INLINE void obj_aff_scale_inv(OBJ_AFFINE *oa, FIXED wx, FIXED wy);
INLINE void obj_aff_rotate_inv(OBJ_AFFINE *oa, u16 theta);
INLINE void obj_aff_shearx_inv(OBJ_AFFINE *oa, FIXED hx);
INLINE void obj_aff_sheary_inv(OBJ_AFFINE *oa, FIXED hy);

/*!	\}	*/


// --------------------------------------------------------------------
// INLINES
// --------------------------------------------------------------------


/*!	\addtogroup grpVideoObj	*/
/*! \{	*/

//! Set the attributes of an object.
INLINE OBJ_ATTR *obj_set_attr(OBJ_ATTR *obj, u16 a0, u16 a1, u16 a2)
{
    obj->attr0= a0; obj->attr1= a1; obj->attr2= a2;
    return obj;
}

//! Set the position of \a obj
INLINE void obj_set_pos(OBJ_ATTR *obj, int x, int y)
{
    BFN_SET(obj->attr0, y, ATTR0_Y);
    BFN_SET(obj->attr1, x, ATTR1_X);
}

//! Copies \a count OAM entries from \a src to \a dst.
INLINE void oam_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count)
{	memcpy32(dst, src, count*2);	}

//! Hide an object.
INLINE void obj_hide(OBJ_ATTR *obj)
{	BFN_SET2(obj->attr0, ATTR0_HIDE, ATTR0_MODE);		}

//! Unhide an object.
/*! \param obj	Object to unhide.
*	\param mode	Object mode to unhide to. Necessary because this affects
*	  the affine-ness of the object.
*/
INLINE void obj_unhide(OBJ_ATTR *obj, u16 mode)
{	BFN_SET2(obj->attr0, mode, ATTR0_MODE);				}


//! Get object's sizes as a byte array
INLINE const u8 *obj_get_size(const OBJ_ATTR *obj)
{	return oam_sizes[obj->attr0>>14][obj->attr1>>14];	}

//! Get object's width
INLINE int obj_get_width(const OBJ_ATTR *obj)
{	return obj_get_size(obj)[0];						}

//! Gets object's height
INLINE int obj_get_height(const OBJ_ATTR *obj)
{	return obj_get_size(obj)[1];						}


// --- Affine only ---


//! Set the elements of an \a object affine matrix.
INLINE void obj_aff_set(OBJ_AFFINE *oaff,
                        FIXED pa, FIXED pb, FIXED pc, FIXED pd)
{
    oaff->pa= pa;	oaff->pb= pb;
    oaff->pc= pc;	oaff->pd= pd;
}

//! Set an object affine matrix to the identity matrix
INLINE void obj_aff_identity(OBJ_AFFINE *oaff)
{
    oaff->pa= 0x0100l;	oaff->pb= 0;
    oaff->pc= 0;		oaff->pd= 0x0100;
}

//! Set an object affine matrix for scaling.
INLINE void obj_aff_scale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy)
{
    oaff->pa= sx;	oaff->pb= 0;
    oaff->pb= 0;	oaff->pd= sy;
}

INLINE void obj_aff_shearx(OBJ_AFFINE *oaff, FIXED hx)
{
    oaff->pa= 0x0100;	oaff->pb= hx;
    oaff->pc= 0;		oaff->pd= 0x0100;
}

INLINE void obj_aff_sheary(OBJ_AFFINE *oaff, FIXED hy)
{
    oaff->pa= 0x0100;	oaff->pb= 0;
    oaff->pc= hy;		oaff->pd= 0x0100;
}


// --- Inverse operations ---

INLINE void obj_aff_scale_inv(OBJ_AFFINE *oaff, FIXED wx, FIXED wy)
{	obj_aff_scale(oaff, ((1<<24)/wx)>>8, ((1<<24)/wy)>>8);	}

INLINE void obj_aff_rotate_inv(OBJ_AFFINE *oaff, u16 theta)
{	obj_aff_rotate(oaff, -theta);		}

INLINE void obj_aff_shearx_inv(OBJ_AFFINE *oaff, FIXED hx)
{	obj_aff_shearx(oaff, -hx);								}

INLINE void obj_aff_sheary_inv(OBJ_AFFINE *oaff, FIXED hy)
{	obj_aff_sheary(oaff, -hy);								}


/*! \}	*/


void obj_aff_copy(OBJ_AFFINE *dst, const OBJ_AFFINE *src, u32 count)
{
    int ii;
    for(ii=0; ii<count; ii++)
    {
        dst->pa= src->pa;
        dst->pb= src->pb;
        dst->pc= src->pc;
        dst->pd= src->pd;
        src++;
        dst++;
    }
}

//! Set obj matrix to counter-clockwise rotation.
/*!
	\param oaff	Object affine struct to set.
	\param alpha	CCW angle. full-circle is 10000h.
*/
void obj_aff_rotate(OBJ_AFFINE *oaff, u16 alpha)
{
    int ss= lu_sin(alpha)>>4, cc= lu_cos(alpha)>>4;
    oaff->pa= cc;	oaff->pb= -ss;
    oaff->pc= ss;	oaff->pd= cc;
}

//! Post-multiply \a dst by \a src: D= D*S
void obj_aff_postmul(OBJ_AFFINE *dst, const OBJ_AFFINE *src)
{
    FIXED tmp_a, tmp_b, tmp_c, tmp_d;
    tmp_a= dst->pa; tmp_b= dst->pb;
    tmp_c= dst->pc; tmp_d= dst->pd;

    dst->pa= (tmp_a*src->pa + tmp_b*src->pc)>>8;
    dst->pb= (tmp_a*src->pb + tmp_b*src->pd)>>8;
    dst->pc= (tmp_c*src->pa + tmp_d*src->pc)>>8;
    dst->pd= (tmp_c*src->pb + tmp_d*src->pd)>>8;
}

#endif // TONC_OAM

