#ifndef DS_H_
#define DS_H_

//#define USE_EXTRA_RAM

#ifdef USE_EXTRA_RAM
#define MAX_MANAGED_TEXTURES 700
#else
#define MAX_MANAGED_TEXTURES 400
#endif


void ds_exram_unlock(void);
void ds_exram_lock(void);
void ds_try_exram_unlock(void);
void ds_try_exram_lock(void);
void *ds_exram_base(void);
void ds_set_exram_base(void *base);
unsigned int ds_exram_size(void);
void ds_set_exram_size(unsigned int size);
int ds_find_dldi_slot(void);

void ds_set_exram_timings(unsigned int first, unsigned int second);

void ds_dmacopywords_async(unsigned char channel, const void *src, void *dest, unsigned int size);
void ds_waitfordma(unsigned char channel);
void ds_fifo_dma_async(unsigned char channel, const unsigned int *src);
	

void ds_lock_vram(void);
void ds_unlock_vram(void);

void ram_test(void);
void dump_ram(char *filename, void *base, unsigned int length);

void print_top_four_blocks(void);
void print_top_four_blocks_kb(void);
int count_largest_block_kb(void);
int count_largest_block(void);

void file_unlock(void);
void file_lock(void);
int files_locked(void);

void ipc_block_ready_9to7(void);
bool ipc_test_ready_9to7(void);
void ipc_set_ready_9to7(void);
void ipc_block_ready_7to9(void);

void handle_ipc(void);

bool bind_texture(int);
void ds_bindtexture(int id);
int ds_gentexture(void);
void init_textures(void);
int next_size_up(int size);
int tex_size(int size);
void unload_textures(void);

void init_texture_cache(void *base, unsigned int size);
void *find_texture(unsigned int id);
void add_texture(unsigned int id, void *data, unsigned int size);
void remove_texture(unsigned int id);

void ds_translatef(float x, float y, float z);
void ds_pushmatrix(void);
void ds_popmatrix(void);
void ds_push_projection(void);
void ds_pop_projection(void);
void ds_rotateX(float angle);
void ds_rotateY(float angle);
void ds_rotateZ(float angle);
void ds_scalef(float x, float y, float z);

void ds_vertex3f(float x, float y, float z);
void ds_vertex3v16(short int x, short int y, short int z);
void ds_color3f(float red, float green, float blue);
void ds_color3b(unsigned char red, unsigned char green, unsigned char blue);
void ds_texcoord2f(float u, float v);
void ds_texcoord2t16(short u, short v);

void ds_begin_triangle(void);
void ds_begin_quad(void);
void ds_begin_triangle_strip(void);
void ds_end(void);

bool ds_too_much_geometry(void);

#define RGB15(r,g,b)  ((r)|((g)<<5)|((b)<<10))
#define RGB5(r,g,b)  ((r)|((g)<<5)|((b)<<10))
#define RGB8(r,g,b)  (((r)>>3)|(((g)>>3)<<5)|(((b)>>3)<<10))

#ifndef NDS_INCLUDE

#define floattov16(n)         ((short int)((n) * (1 << 12)))

#define GFX_COLOR				(*(volatile unsigned int *) 0x4000480)
#define GFX_TEX_COORD			(*(volatile unsigned int *) 0x04000488)
#define GFX_VERTEX16			(*(volatile unsigned int *) 0x0400048C)
#define TEXTURE_PACK(u,v)    (((u) << 16) | (v & 0xFFFF))
#define GFX_BEGIN			(*(volatile unsigned int *) 0x04000500)

#define DS_COLOUR3B(red, green, blue) GFX_COLOR = (volatile unsigned int)RGB15(red>>3, green>>3, blue>>3)
#define DS_TEXCOORD2T16(x, y) GFX_TEX_COORD = TEXTURE_PACK(x, y)
#define DS_VERTEX3V16(x, y, z) GFX_VERTEX16 = (y << 16) | (x & 0xFFFF); GFX_VERTEX16 = ((unsigned int)(unsigned short)z)
#define DS_BEGIN_TRIANGLE() GFX_BEGIN = 0
#define DS_BEGIN_QUAD() GFX_BEGIN = 1

#endif

int register_texture(bool bsp, char *name, int fp, int seek, int sizeX, int sizeY, bool transparency, int transparent_colour, int half_width, int half_height);
int register_texture_deferred(char *name, unsigned char *data, int sizeX, int sizeY, int transparency, int transparent_colour);
int register_texture_immediate(char *name, unsigned char *data, int sizeX, int sizeY, int transparency, int transparent_colour);

void ds_ortho(void);
void ds_perspective(void);

int next_size_up(int size);
void dump_texture_state(void);
void ds_polyfmt(int poly_id, int trans, int depth);


void byte_write(void *ptr, unsigned char value);
void ds_memset(void *addr, unsigned char value, unsigned int length);
void ds_memset_basic(void *addr, unsigned char value, unsigned int length);
void ds_memcpy_basic(void *dest, void *source, unsigned int length);
void ds_memcpy(void *dest, void *source, unsigned int length);

void clear_ram(unsigned int *base, unsigned int size);
void ds_exram_cache_enable(void);
void ds_exram_cache_disable(void);

inline void byte_write(void *ptr, unsigned char value) __attribute__ ((no_instrument_function));

inline void byte_write(void *ptr, unsigned char value)
{
#ifdef USE_EXTRA_RAM
	unsigned int aligned_addr = ((unsigned int)ptr) & 0xfffffffe;
	unsigned short existing = *(unsigned short *)aligned_addr;
	
	unsigned short result;
	
	unsigned short is_lower = (unsigned int)ptr & 0x1;
	unsigned short value_shift = is_lower << 3;
	
	unsigned short shifted_value = value << value_shift;
	unsigned short existing_masked = existing & (0xff00 >> value_shift);
	
	result = shifted_value | existing_masked;
	
//	if ((((unsigned int)ptr) & 0x1) == 0)
//		result = (existing & 0xff00) | value;
//	else
//		result = (value << 8) | (existing & 0xff);
	
	*(unsigned short *)aligned_addr = result;
#else
	*(unsigned char *)ptr = value;
#endif
}

#endif /*DS_H_*/
