/*
 * $Id: graph.h,v 1.1 2003/06/06 00:15:12 tong Exp $
 *
 * グラフィックスレッドクラス
 *
 *
 */

#ifndef __GRAPH_H_
#define __GRAPH_H_

#ifdef _LANGUAGE_C_PLUS_PLUS
extern "C" {
#endif
#if 0
}
#endif

#include "u64basic.h"
#include "u64types.h"
#include "THA_GA.h"
#include "sched.h"

#define GRAPH_STACKSIZE 0x1800	/* 実績 0x1200以上 */

typedef union {    
    struct {
        short     type;
    } gen;
    struct {
        short     type;
    } done;
    OSScMsg       app;
} GraphMsg;

typedef enum graph_doing_point_e {
    GRAPH_DOING_POINT_ZERO,
    GRAPH_DOING_POINT_CT,                    /* コンストラクト完了 */
    GRAPH_DOING_POINT_GAME_CT,               /* ゲームクラスコンストラクト */
    GRAPH_DOING_POINT_GAME_CT_DONE,          /* ゲームクラスコンストラクト終了 */
    GRAPH_DOING_POINT_GAME_MAIN,             /* ゲームクラスメイン */
    GRAPH_DOING_POINT_GAME_MTM_TIME,         /* 時間計算終了 */
    GRAPH_DOING_POINT_GAME_MTM_TIME_DONE,    /* 時間計算終了 */
    GRAPH_DOING_POINT_GAME_EXEC,             /* ゲーム本体 */
    GRAPH_DOING_POINT_GAME_EXEC_DONE,        /* ゲーム本体終了 */
    GRAPH_DOING_POINT_GAME_MBGM_MAIN,        /* ＢＧＭ処理終了 */
    GRAPH_DOING_POINT_GAME_MBGM_MAIN_DONE,   /* ＢＧＭ処理終了 */
    GRAPH_DOING_POINT_GAME_MAIN_DONE,        /* ゲームクラスメイン終了 */
    GRAPH_DOING_POINT_TASK_SET,              /* タスクセット */
    GRAPH_DOING_POINT_WAIT_TASK,             /* タスク完了待ち */
    GRAPH_DOING_POINT_WAIT_TASK_DONE,        /* タスク完了待ち終了 */
    GRAPH_DOING_POINT_TASK_SET_DONE,         /* タスクセット終了 */
    GRAPH_DOING_POINT_GAME_AUDIO,            /* オーディオゲームフレーム処理 */
    GRAPH_DOING_POINT_GAME_AUDIO_DONE,       /* オーディオゲームフレーム処理終了 */
    GRAPH_DOING_POINT_SYNC_GRAPH,            /* グラフィックタスクが空になるのを待つ */
    GRAPH_DOING_POINT_GAME_DT,               /* ゲームクラスデストラクト */
    GRAPH_DOING_POINT_GAME_DT_DONE,          /* ゲームクラスデストラクト終了 */
    GRAPH_DOING_POINT_DT,                    /* デストラクト開始 */
    GRAPH_DOING_POINT_MAX
} graph_doing_point_e;

#define SET_GRAPH_DOING_POINT(g, e)     ((g)->doing_point = (u8)((e) - GRAPH_DOING_POINT_ZERO))
#define SET_GRAPH_DOING_POINT2(g, e2)   SET_GRAPH_DOING_POINT(g, GRAPH_DOING_POINT_##e2)
#define GET_GRAPH_DOING_POINT(g)        ((graph_doing_point_e)(g)->doing_point)

/*
 * グラフィックステートとディスプレイリスト
 * この構造体をグラフのディスプレイリストを生成するすべての関数の第1引数にする
 * ただし、非独立型に限る
 * 関数の先頭と末尾で、OPEN_DISP/CLOSE_DISPを呼び出す
 * 
 */
#define MAX_MESGS               8
typedef struct graph_s {
    Gfx		*Gfx_list00P_top; /* 不透明ポリゴン用 */
    Gfx		*Gfx_list01P_top; /* 半透明ポリゴン用 */
    void	*DepthBufferP;	/* Ｚバッファポインタ */
    Gfx		*_Gfx_list03P_top;
    Gfx		*Gfx_list04P_top; /* 上書き用 */
    Gfx		*Gfx_list07P_top; /* 文字用 1999 06/16 */
    Gfx		*Gfx_list08P_top; /* 影用 (2000 6/29 miyake) */
    Gfx		*Gfx_list09P_top; /* 影用 2000 11/13 komatu */
    Gfx		*gfxsave;
    GraphMsg 	msg;            /* completion message */
    OSMesg 	graphReplyMsgBuf[MAX_MESGS];
    OSMesgQueue *sched_cmdQ;
    OSMesgQueue graphReplyMsgQ;
    OSScTask	ossctask00p;
    OSScTask	ossctask01p;	/* 4+4+4+4+64+4+4+8+8=104 */
    OSScTask	ossctask02p;
    Gfx		*Gfx_list05P_top; /* 描画前の作業用エリア */
    THA_GA 	work_thaga;
    char        _ossctask03p[104 - sizeof(Gfx *) - sizeof(THA_GA)];
    char        _ossctask04p[4+4+4+4+64+4+4+8+8];
    OSSched 	*sc;
    OSViMode	*vimode;	/* !NULL:osViSetMode(vimode) */
    THA_GA 	_line_opa_thaga;/* 不透明ライン用THA（使用していない様です） */
    THA_GA 	_line_xlu_thaga;/* 半透明ライン用THA（使用していない様です） */
    THA_GA 	overlay_thaga;	/* 上書き用THA */
    THA_GA 	poly_opa_thaga;	/* 不透明ポリゴン用THA */	
    THA_GA 	poly_xlu_thaga;	/* 半透明ポリゴン用THA */
    THA_GA 	font_thaga;	/* 文字用THA   1999 06/06 komatu */
    THA_GA	shadow_thaga;	/* アクタ影THA 2000 6/29 miyake. */
    THA_GA	light_thaga;	/* 灯台の光THA 2000 11/11 komatu */

    int 	frame_counter;	/* グラフィックフレームカウンタ */
    u16		*FrameBufferP;	/* 描画対象フレームバッファ */
    u16		*RenderBufferP;	/* 作業用フレームバッファ */
    u32 	vispecial;	/* osViSetSpecialFeatures */
    u8		doing_point;    /* !0:実行位置 */
    u8		__padding2;     /* 未使用 */
    u8		need_viupdate;	/* !0:ビデオ再設定が必要 */
    u8		cfb_bank;	/* フレームバッファバンク */
    void	(*TaskEndCallBack)(struct graph_s *, void *);
    void	*TaskEndClientData;
    float	vixscale, viyscale; /* スケール */
    Gfx		*last_dl;	/* 最後に実行したディスプレイリスト */
} graph_t;
typedef graph_t GRAPH;

/*
 * タスク終了時のコールバック関数の登録マクロ
 * 注意）手抜きなので関数は一つしか登録できません
 *
 * ゼルダで利用していたが、どうぶつの森では利用していません
 */
#define graph_AddTaskEndCallBack(graph, callback, client_data) \
do { \
     (graph)->TaskEndCallBack = (callback); \
     (graph)->TaskEndClientData = (client_data); \
} while (0)
#define graph_RemoveTaskEndCallBack(graph, callback, client_data) \
do { \
    if ((graph)->TaskEndCallBack   == (callback) && \
	(graph)->TaskEndClientData == (client_data)) { \
        (graph)->TaskEndCallBack   = NULL; \
        (graph)->TaskEndClientData = NULL; \
    }\
} while (0)
    
/*
 * OPEN_DISP/CLOSE_DISP
 *
 * ちょっとトリッキーです。気をつけてください。
 *		(NEXT_DISP等で__graphを使うのでおまじないのように置く)
 *
 * DEBUGのときはディスプレイリスト解析用に
 * __graphcheck_opendisp/__graphcheck_closedisp を呼び出します。
 *
 */
#define _OPEN_DISP(graph) 			\
    do; while (0);				\
    {						\
        GRAPH *__graph = (graph); 		\
        const int __poly_gfx_opened = 0; 	\
	do; while (0)		/* rquire `;'  */
    
#define _CLOSE_DISP(graph) 			\
	do; while (0);				\
	(void)((graph) - (GRAPH *)0);		\
	(void)__poly_gfx_opened; 		\
    }						\
    do; while (0)		/* rquire `;'  */

#define _VOID_DISP(graph)                       \
    do {                                        \
	(void)(__graph);                        \
    } while (0)                 /* rquire `;'  */

#define VOID_DISP(graph)        _VOID_DISP(graph)

#if !DEBUG
#define OPEN_DISP(graph) 			\
	_OPEN_DISP(graph)
#define CLOSE_DISP(graph) 			\
        _CLOSE_DISP(graph)
#else
typedef struct {
#define XXX(type, TYPE)  Gfx *tmp_##type
        XXX(poly_opa, POLY_OPA);
        XXX(poly_xlu, POLY_XLU);
        XXX(overlay, OVERLAY);
#if 01                          /* 01/01/12 16:48 hayakawa  */
        XXX(font, FONT);
        XXX(shadow, SHDW);
        XXX(light, LIGHT);
        XXX(work, WORK);
#endif
#undef XXX
} __GraphCheck;
extern void __graphcheck_opendisp(__GraphCheck *this, GRAPH *graph, char *file, int line);
extern void __graphcheck_closedisp(__GraphCheck *this, GRAPH *graph, char *file, int line);
#define OPEN_DISP(graph) 			\
    _OPEN_DISP(graph);				\
    {						\
	__GraphCheck __graphcheck; 		\
	__graphcheck_opendisp(&__graphcheck, graph, __FILE__, __LINE__); \
	do; while (0)		/* rquire `;'  */

#define CLOSE_DISP(graph) 			\
	do; while (0);				\
	__graphcheck_closedisp(&__graphcheck, graph, __FILE__, __LINE__); \
    }						\
    _CLOSE_DISP(graph)
#endif

/*
 * 描画の順番は WORK OPA SHDW XLU LIGHT FONT OVERLAY となる
 * (graph_draw_finishで制御しています)
 */	
#define NEXT_POLY_OPA_DISP 	THA_GA_NEXT_DISP(&__graph->poly_opa_thaga)
#define NEXT_POLY_XLU_DISP 	THA_GA_NEXT_DISP(&__graph->poly_xlu_thaga)
#define NEXT_OVERLAY_DISP 	THA_GA_NEXT_DISP(&__graph->overlay_thaga)
#define NEXT_WORK_DISP 		THA_GA_NEXT_DISP(&__graph->work_thaga)
#define NEXT_FONT_DISP 		THA_GA_NEXT_DISP(&__graph->font_thaga) /* 1999 06/16 komatu */
#define NEXT_SHDW_DISP		THA_GA_NEXT_DISP(&__graph->shadow_thaga) /* 2000 6/29 miyake */
#define NEXT_LIGHT_DISP		THA_GA_NEXT_DISP(&__graph->light_thaga) /* 2000 11/13 komatu */
#define NEXT_DISP 		NEXT_POLY_OPA_DISP

#define NOW_POLY_OPA_DISP 	(Gfx *)THA_GA_getHeadPtr(&__graph->poly_opa_thaga)
#define NOW_POLY_XLU_DISP 	(Gfx *)THA_GA_getHeadPtr(&__graph->poly_xlu_thaga)
#define NOW_OVERLAY_DISP 	(Gfx *)THA_GA_getHeadPtr(&__graph->overlay_thaga)
#define NOW_FONT_DISP 		(Gfx *)THA_GA_getHeadPtr(&__graph->font_thaga) /* 1999 06/16 komatu */
#define NOW_SHDW_DISP		(Gfx *)THA_GA_getHeadPtr(&__graph->shadow_thaga) /* 2000 6/29 miyake */
#define NOW_LIGHT_DISP		(Gfx *)THA_GA_getHeadPtr(&__graph->light_thaga) /* 2000 11/13 komatu */
#define NOW_WORK_DISP 		(Gfx *)THA_GA_getHeadPtr(&__graph->work_thaga)
#define NOW_DISP 		NOW_POLY_OPA_DISP

#define SET_NOW_POLY_OPA_DISP(ptr)      THA_GA_setHeadPtr(&__graph->poly_opa_thaga, ptr)
#define SET_NOW_POLY_XLU_DISP(ptr)      THA_GA_setHeadPtr(&__graph->poly_xlu_thaga, ptr)
#define SET_NOW_OVERLAY_DISP(ptr)       THA_GA_setHeadPtr(&__graph->overlay_thaga, ptr)
#define SET_NOW_FONT_DISP(ptr)          THA_GA_setHeadPtr(&__graph->font_thaga, ptr) /* 1999 06/16 komatu */
#define SET_NOW_SHDW_DISP(ptr)          THA_GA_setHeadPtr(&__graph->shadow_thaga, ptr) /* 2000 6/29 miyake */
#define SET_NOW_LIGHT_DISP(ptr)         THA_GA_setHeadPtr(&__graph->light_thaga, ptr) /* 2000 11/13 komatu */
#define SET_NOW_WORK_DISP(ptr)          THA_GA_setHeadPtr(&__graph->work_thaga, ptr)
#define SET_NOW_DISP(ptr)               SET_NOW_POLY_OPA_DISP(ptr)

/*
 *	メモリ省エネバージョン
 */
/* 不透明＆半透明用 */
#define OPEN_POLY_OPA_XLU_DISP_S(graph) 	\
    OPEN_DISP(graph);				\
    { 						\
	Gfx *gp_opa = NOW_POLY_OPA_DISP;	\
	Gfx *gp_xlu = NOW_POLY_XLU_DISP;	\
        const int __gp_opa_xlu_opened = 0; 	\
	do; while (0)		/* rquire `;'  */
#define CLOSE_POLY_OPA_XLU_DISP_S(graph) 	\
	do; while (0);				\
        (void)__gp_opa_xlu_opened;		\
	SET_NOW_POLY_OPA_DISP(gp_opa);		\
	SET_NOW_POLY_XLU_DISP(gp_xlu);		\
    }						\
    CLOSE_DISP(graph)

/* 不透明用 */
#define OPEN_POLY_OPA_DISP_S(graph) 		\
    OPEN_DISP(graph);				\
    { 						\
	Gfx *gp_opa = NOW_POLY_OPA_DISP;	\
        const int __gp_opa_opened = 0; 		\
	do; while (0)		/* rquire `;'  */
#define CLOSE_POLY_OPA_DISP_S(graph) 		\
	do; while (0);				\
        (void)__gp_opa_opened;			\
	SET_NOW_POLY_OPA_DISP(gp_opa);		\
    }						\
    CLOSE_DISP(graph)
#define	NEXT_POLY_OPA_DISP_S		(gp_opa++)
#define NOW_POLY_OPA_DISP_S		(gp_opa)
#define SET_NOW_POLY_OPA_DISP_S(ptr)	(gp_opa = (ptr))

/* 透明用 */
#define OPEN_POLY_XLU_DISP_S(graph)		\
    OPEN_DISP(graph);				\
    { 						\
	Gfx *gp_xlu = NOW_POLY_XLU_DISP;	\
        const int __gp_xlu_opened = 0; 		\
	do; while (0)		/* rquire `;'  */
#define CLOSE_POLY_XLU_DISP_S(graph)		\
	do; while (0);				\
        (void)__gp_xlu_opened;			\
	SET_NOW_POLY_XLU_DISP(gp_xlu);		\
    }						\
    CLOSE_DISP(graph)
#define	NEXT_POLY_XLU_DISP_S		(gp_xlu++)
#define NOW_POLY_XLU_DISP_S		(gp_xlu)
#define SET_NOW_POLY_XLU_DISP_S(ptr)	(gp_xlu = (ptr))
    
/*----------------------*/
/* 影Disp省エネ Version	*/						    
/*----------------------*/
/* Open */						    
#define OPEN_SHDW_DISP_S(graph) 		\
    OPEN_DISP(graph);				\
    { 						\
	Gfx *gp_shdw = NOW_SHDW_DISP;           \
        const int __gp_shdw_opened = 0; 	\
	do; while (0)		/* rquire `;'  */
/* Close */
#define CLOSE_SHDW_DISP_S(graph) 		\
	do; while (0);				\
        (void)__gp_shdw_opened;			\
	SET_NOW_SHDW_DISP(gp_shdw);		\
    }						\
    CLOSE_DISP(graph)
/* Next */						    
#define	NEXT_SHDW_DISP_S		(gp_shdw++)
#define NOW_SHDW_DISP_S			(gp_shdw)
#define SET_NOW_SHDW_DISP_S(ptr)	(gp_shdw = (ptr))
    
/*----------------------*/
/* 灯台の光Disp省エネ Version */
/*----------------------*/
/* Open */						    
#define OPEN_LIGHT_DISP_S(graph) 		\
    OPEN_DISP(graph);				\
    { 						\
	Gfx *gp_light = NOW_LIGHT_DISP;         \
        int __gp_light_opened = 0; 		\
	do; while (0)		/* rquire `;'  */
/* Close */
#define CLOSE_LIGHT_DISP_S(graph) 		\
	do; while (0);				\
        (void)__gp_light_opened;		\
	SET_NOW_LIGHT_DISP(gp_light);		\
    }						\
    CLOSE_DISP(graph)
/* Next */						    
#define	NEXT_LIGHT_DISP_S		(gp_light++)
#define NOW_LIGHT_DISP_S		(gp_light)
#define SET_NOW_LIGHT_DISP_S(ptr)	(gp_light = (ptr))
    
	
extern void graph_proc(void *arg);
extern void graph_ct(GRAPH *);
extern void graph_dt(GRAPH *);

/*
 * ダイナミックディスプレイ領域からのアロケーション
 * 高速かつ自動解放で手間要らず。
 * 当然ながら、次の描画が終わると破壊されるので注意。
 */

/*
 * graph_alloc   : おすすめ
 * graph_alloc15 : 汎用
 * graph_alloc16 : 高速(size が 16 の倍数の時専用)
 * サイズで確保
 */

#define graph_alloc16(graph, size) 	((void *)((graph)->poly_opa_thaga.tha.tail_p = (char *)((int)(graph)->poly_opa_thaga.tha.tail_p - ((int)(size)))))
#define graph_alloc15(graph, size) 	graph_alloc16(graph, (((size) + 15) & ~15))
#define graph_alloc(graph, size) 	graph_alloc15(graph, ((unint)(size)))
#define graph_alloc_Mtx(graph, num) 	(Mtx *)graph_alloc16(graph, sizeof(Mtx) * ((unint)(num)))
#define graph_alloc_Vtx(graph, num) 	(Vtx *)graph_alloc16(graph, sizeof(Vtx) * ((unint)(num)))
#define graph_alloc_Gfx(graph, num) 	(Gfx *)graph_alloc(graph, sizeof(Gfx) * ((unint)(num)))

/*
 * graph_new   : おすすめ
 * graph_new15 : 汎用
 * graph_new16 : 高速(sizeof(type) が 16 の倍数の時専用)
 * graph_newa  : 配列用
 * 型で確保
 */
#define graph_new16(graph, type) (type *)graph_alloc16(graph, sizeof(type))
#define graph_new15(graph, type) (type *)graph_alloc15(graph, sizeof(type))
#define graph_new(graph, type) (((sizeof(type) & 15) == 0) ? graph_new16(graph, type) : graph_new15(graph, type))

#define graph_newa(graph, type, count) ((type *)graph_alloc(graph, sizeof(type) * (count)))
#define graph_newa16(graph, type, count) ((type *)graph_alloc16(graph, sizeof(type) * (count)))

#define Mtx_graph_new(graph)            graph_new16(graph, Mtx)
#define Mtx_graph_newa(graph, count)    graph_newa16(graph, Mtx, count)
#define Vtx_graph_new(graph)            graph_new16(graph, Vtx)
#define Vtx_graph_newa(graph, count)    graph_newa16(graph, Vtx, count)
#define Gfx_graph_new(graph)            graph_new16(graph, Gfx)
#define Gfx_graph_newa(graph, count)    graph_newa16(graph, Gfx, count)

#define Graph_alloc(size) 	graph_alloc(graph, size)
#define Graph_new(type) 	graph_new(graph, type)
#define Graph_newa(type, count) graph_newa(graph, type, count)


/*
 * 表示フレームバッファのスワップ制御用マクロ
 */
#define IsDisableNextSwapFrame()        ((GETSREG(33) & 1) != 0)
#define DisableNextSwapFrame()          CALC_REG(SREG, 33, |, 1)
#define EnableNextSwapFrame()           CALC_REG(SREG, 33, &, ~1)
#define IsDisableNextGfxTask()          ((GETSREG(33) & 2) != 0)
#define DisableNextGfxTask()            CALC_REG(SREG, 33, |, 2)
#define EnableNextGfxTask()             CALC_REG(SREG, 33, &, ~2)

extern void graph_wait_task(GRAPH *this);
extern void graph_dummy_task(GRAPH *this);

extern GRAPH graph_class;

#if 0
{
#endif
#ifdef _LANGUAGE_C_PLUS_PLUS
} /* extern "C" */
#endif

#endif /* __GRAPH_H_ */
