#define _KM_USE_VERTEX_MACRO_L4_
#include <shinobi.h>
#include <kamui2.h>
#include <km2util.h>
#include <string.h>
#include <stdlib.h>
#include "dispstrex.h"
#include "command.h"
#include "km2kamuix.h"
#include "gte.h"
#include "model.h"
#include "nd.h"

void *ReadNinjaBinary(Sint8 *wbuff, Uint32 *fpos, Uint32 *ptype);
PKMDWORD LoadBinaryBasicModel(char *modelfile);
int LoadTextureList(PKG_TEXLIST pTexList);

static PROCESSHANDLE hProcessDrawBasic;
static KMDWORD frameCount;
static PKG_TEXLIST 	pCurrentTexList = NULL;
static PKG_OBJECT 	pCurrentObject = NULL;
static PKMDWORD 	pModelBuffer = NULL;
extern PKMSURFACEDESC gTextureList;

void DrawBasicCommand(int argc, char *argv[])
{
	Uint32 fpos = 0;
	Uint32 ptype;
	void *ppp;

	if(argc == 1)
	{
		PRINTOUT(("Usage: %s BinaryModelData[.nj]\n", argv[0]));
		return;
	}

	/* Free Previous Object List */
	FreeAllTextureList();
	if(pCurrentObject) 	syFree(pCurrentObject);
	if(pCurrentTexList) syFree(pCurrentTexList);

	pCurrentObject = pCurrentTexList = NULL;
	if((pModelBuffer = LoadBinaryBasicModel(argv[1])) == NULL)
	{
		PRINTOUT(("Model Data Load Error : %s\n", argv[1]));
		return;
	}

	while(1)
	{
		ppp = ReadNinjaBinary(pModelBuffer, &fpos, &ptype);
		if(ppp == NULL) break;
		switch(ptype)
		{
			case iff_NJTL:
			{
				pCurrentTexList = (PKG_TEXLIST)ppp;
				break;
			}
			case iff_NJBM:
			{
				pCurrentObject = (PKG_OBJECT)ppp;
				break;
			}
		}
	}

	syFree(pModelBuffer);

	if(pCurrentTexList) LoadTextureList(pCurrentTexList);

	kgInitGeometry(320.0f, 240.0f, 2.0f);

	kgSetView(&ViewMatrix, 0.0, 0.0, 20.0, 0.0f, 0.0f, 0.0f);

	kgSetAmbientColor		(0.5f, 0.5f, 0.5f);
	kgSetParallelLightDir	(0, 0.0f, -1.0f, 0.0f);	/* No. 0 Light Dir */
	kgSetParallelLightColor	(0, 0.6f, 0.6f, 0.6f);	/* No. 0 Light Color */

	kgSetParallelLightDir	(1, 0.0f, 1.0f, 0.0f);	/* No. 1 Light Dir */
	kgSetParallelLightColor	(1, 0.0f, 0.0f, 0.0f);	/* No. 1 Light Color */

	kgSetParallelLightDir	(2, 0.0f, 1.0f, 1.0f);	/* No. 2 Light Dir */
	kgSetParallelLightColor	(2, 0.0f, 0.0f, 0.0f);	/* No. 2 Light Color */

	SetWindowArea(hProcessDrawBasic, 100, 100, 300, 300);
	ActivateProcess(hProcessDrawBasic);
	kgSetTexList(&gTextureList);
	Invalidate();

	frameCount = 0;
}

void DrawBasicModel(PKMVERTEXBUFFDESC pBufferDesc)
{
	KMRECT Rect;
	float ScreenCenterX, ScreenCenterY;
	float ScreenSizeX, ScreenSizeY;

	GetClientRect(hProcessDrawBasic, &Rect);
	ScreenSizeX = Rect.nXmax - Rect.nXmin;
	ScreenSizeY = Rect.nYmax - Rect.nYmin;
	ScreenCenterX = (float)Rect.nXmin + ScreenSizeX / 2.0f;
	ScreenCenterY = (float)Rect.nYmin + ScreenSizeY / 2.0f;

	kgChangeScreenCenter(ScreenCenterX, ScreenCenterY);
    kgChangeScreenScale(ScreenSizeX / 8, ScreenSizeY / 8);

	kgInitMatrix(&ViewMatrix);
	kgRotateZ((KMDWORD)frameCount*300);
	kgRotateY((KMDWORD)frameCount*200);
	kgRotateX((KMDWORD)frameCount*100);
	kgBasicDrawObject(pBufferDesc, pCurrentObject);

	frameCount++;
}

void InitDrawBasic(void)
{
	hProcessDrawBasic = RegisterCommand(	DrawBasicCommand,
											(PKMCALLBACKFUNC)DrawBasicModel, NULL,
											"drawbasic",
											PROCESSTYPE_W_DRAW
										);
}

/*
 *			Load Binary Model
 *
 */
int LoadTextureList(PKG_TEXLIST pTexList)
{
	PKG_TEXNAME 	pTexName;
	KMDWORD			nLoop, NumTextures;
	PKMDWORD		pBuffer, pTexture;
	PKG_TEXHEADER	pHeader;
	char ReadFileName[50];

	pTexName  = pTexList->textures;
	NumTextures = (KMDWORD)pTexList->nbTexture;
	for(nLoop = 0; nLoop < NumTextures; nLoop++)
	{
		sprintf(ReadFileName,"%s.pvr", pTexName->filename);
		LoadTextureFmGD(ReadFileName);
		pTexName++;
	}
	return 1;
}

PKMDWORD LoadBinaryBasicModel(char *modelfile)
{
	PKMDWORD pReadBuffer;
	GDFS	gdfs;
	Sint32 	size;
	Sint32 	ret;

	gdfs = gdFsOpen(modelfile, NULL);
	if (gdfs == NULL) return NULL;

	gdFsGetFileSize(gdfs, &size);
	size = gdFsCalcSctSize(size);

	pReadBuffer = syMalloc(size * 2048);
	if (pReadBuffer == NULL)
	{
		gdFsClose(gdfs);
		return NULL;
	}

	ret = gdFsRead(gdfs, size, pReadBuffer);
	/* close handle */
	gdFsClose(gdfs);

	if(ret != GDD_ERR_OK)
	{
		syFree(pReadBuffer);
		return NULL;
	}

	return pReadBuffer;
}

Uint32 SwapCNK(Uint32 cnk)
{
    Uint32 rv;
    rv = (cnk&0x000000ff)<<24|(cnk&0x0000ff00)<<8|(cnk&0x00ff0000)>>8|(cnk&0xff000000)>>24;
    return rv;
}

void *ReadNinjaBinary(Sint8 *wbuff, Uint32 *fpos, Uint32 *ptype)
{
    Sint8 *current;
    Sint8 *tmp;
    Uint8 buf0, buf1, buf2, buf3;
    Uint32 *addr;
    Uint32 cnk, size;
    Uint32 offset,  offset_from_base = 0;

    current = &wbuff[*fpos];

    memcpy(&cnk, current, 4);
    current += 4;

    memcpy(&size, current, 4);
    current += 4;

    *fpos += 8;
    cnk = SwapCNK(cnk);
    switch(cnk){
        case iff_NJINFO:
            *ptype = iff_NJINFO;
            goto _allocate_data_area_;
        case iff_NJCM:
            *ptype = iff_NJCM;
            goto _allocate_data_area_;
        case iff_NJBM:
            *ptype = iff_NJBM;
            goto _allocate_data_area_;
        case iff_NJTL:
            *ptype = iff_NJTL;
            goto _allocate_data_area_;
        case iff_NJLI:
            *ptype = iff_NJLI;
            goto _allocate_data_area_;
        case iff_NJCA:
            *ptype = iff_NJCA;
            goto _allocate_data_area_;
        case iff_NMDM:
            *ptype = iff_NMDM;
            goto _allocate_data_area_;
        case iff_NLIM:
            *ptype = iff_NLIM;
            goto _allocate_data_area_;
        case iff_NCAM:
            *ptype = iff_NCAM;
            goto _allocate_data_area_;
        case iff_NSSM:
            *ptype = iff_NSSM;
        _allocate_data_area_:
            tmp = (Sint8 *)syMalloc(size);
            if(tmp == NULL)
            	return NULL;
            memcpy(tmp, current, size);
            current += size;
            *fpos += size;
            break;
        default:
            *ptype = '****';
            return NULL;
    }
    /*-- Add BaseAddr ---------------------------------------*/
    memcpy(&cnk, current, 4);
    current += 4;
    *fpos += 4;
    cnk = SwapCNK(cnk);
    if(cnk == 'POF0'){
        memcpy(&size, current, 4);
        current += 4;
        *fpos += 4;
        while(size > 0){
            memcpy(&buf0, current, 1);
            current += 1;
            *fpos += 1;
            size--;
            switch(buf0&NJD_POF_CHAR_MASK)
            {
                case NJD_POF_TYPE_PAD:
                    break;
                case NJD_POF_TYPE_CHAR:
                    offset = (Uint32)(buf0&NJD_POF_DATA_MASK);
                    goto _ADD_BASE_ADDRESS_;
                case NJD_POF_TYPE_SHORT:
                    memcpy(&buf1, current, 1);
                    current += 1;
                    size--;
                    *fpos += 1;
                    offset = (Uint32)((buf0&NJD_POF_DATA_MASK)<<8|buf1);
                    goto _ADD_BASE_ADDRESS_;
                case NJD_POF_TYPE_LONG:
                    memcpy(&buf1, current, 1);
                    current += 1;
                    size--;
                    memcpy(&buf2, current, 1);
                    current += 1;
                    size--;
                    memcpy(&buf3, current, 1);
                    current += 1;
                    size--;
                    *fpos += 3;
                    offset = (Uint32)((buf0&NJD_POF_DATA_MASK)<<24|(buf1<<16)|(buf2<<8)|buf3);
                _ADD_BASE_ADDRESS_:
                    offset *= 4;
                    offset_from_base += offset;
                    addr = (Uint32 *)(tmp+offset_from_base);
                    if(*addr)
                        *addr += (Uint32)tmp;
                    break;
            }
        }
    }
    return (void*)tmp;
}