#include "stdafx.h"
#include <stdlib.h>
#include "ogEngine.h"

DWORD	__pipelineModuleOffset;


// RxHeap structures
struct rxHeapSuperBlockDescriptor {
    void *		start;
    DWORD		size;
    rxHeapSuperBlockDescriptor *next;
};

struct rxHeapBlockHeader;
struct rxHeapFreeBlock {
    DWORD				size;
    rxHeapBlockHeader  *ptr;
};

struct rxHeapBlockHeader {	//	present in all blocks (used & unused)     
    rxHeapBlockHeader *	prev;
	rxHeapBlockHeader *	next;
    DWORD				size;
    rxHeapFreeBlock *	freeEntry; 
    DWORD				pad[4]; 
};

struct RxHeap {
    DWORD				superBlockSize; // < Granularity of heap growth 
    rxHeapSuperBlockDescriptor *head;   // < Internally used superblock pointer 
    rxHeapBlockHeader  *headBlock;      // < Internally used block pointer 
    rxHeapFreeBlock    *freeBlocks;     // < Internally used free blocks pointer 
    DWORD				entriesAlloced; // < Number of entries allocated 
    DWORD				entriesUsed;    // < Number of entries used 
    bool				dirty;          // < Internally used boolean, flags whether the heap needs resetting or not. 
};


/*
static rxHeapFreeBlock * HeapFreeBlocksNewEntry (RxHeap * heap){
	if (heap->entriesAlloced <= heap->entriesUsed) {
		heap->entriesAlloced += 32;
		rxHeapFreeBlock * fb = (rxHeapFreeBlock *)realloc (heap->freeBlocks, heap->entriesAlloced*8);
		if (!fb) {
			heap->entriesAlloced -= 32;
			debug ("HeapFreeBlocksNewEntry: ERROR: realloc failed\n");
			return NULL;
		}
		if (fb != heap->freeBlocks) {
			for (DWORD i = 0; i < heap->entriesUsed; i++) {
				rxHeapBlockHeader * bh = fb[i].ptr;
				bh->freeEntry = fb+i;
			}
			heap->freeBlocks = fb;
		}		
	}
	if (!heap->freeBlocks)
		return NULL;

	heap->entriesUsed++;
	return heap->freeBlocks+heap->entriesUsed-1;
}
*/
/*
bool HeapSuperBlockReset (rxHeapSuperBlockDescriptor * super, DWORD index, rxHeapFreeBlock * freeBlocks) {
	rxHeapFreeBlock * fb = HeapFreeBlocksNewEntry ();
	if (!fb)
		return false;

	rxHeapBlockHeader * bh = (rxHeapBlockHeader *)super->start;
	bh->size = 0;
	bh->freeEntry = NULL;

	rxHeapBlockHeader * next = (rxHeapBlockHeader *)((BYTE *)super->start+super->size-sizeof (rxHeapBlockHeader));
	memcpy (next, bh, sizeof (rxHeapBlockHeader));
	
	bh->next = bh+1;
	(bh+1)->prev = bh;
	(bh+1)->next = next;
	next->prev = bh+1;
	

	return true;
}
*/

struct RxPipelineNodeParam {
    void   *dataParam;  
    RxHeap *heap;       
};

struct RxPipelineNode;
struct RxNodeDefinition;
struct rxReq;
struct RxPacket;

typedef bool (*RxNodeBodyFn) (RxPipelineNode * self, const RxPipelineNodeParam *params);
typedef bool (*RxNodeInitFn) (RxNodeDefinition * self);
typedef void (*RxNodeTermFn) (RxNodeDefinition * self);
typedef bool (*RxPipelineNodeInitFn) (RxPipelineNode * self);
typedef void (*RxPipelineNodeTermFn) (RxPipelineNode * self);
typedef bool (*RxPipelineNodeConfigFn) (RxPipelineNode * self, RxPipeline * pipeline);
typedef DWORD (*RxConfigMsgHandlerFn) (RxPipelineNode * self, DWORD msg, DWORD intparam, void *ptrparam);

struct RxNodeMethods {
    RxNodeBodyFn         nodeBody;              /**< Node body function */
    RxNodeInitFn         nodeInit;              /**< Node initialization function */
    RxNodeTermFn         nodeTerm;              /**< Node termination function */
    RxPipelineNodeInitFn pipelineNodeInit;      /**< Pipeline node initialization function */
    RxPipelineNodeTermFn pipelineNodeTerm;      /**< Pipeline node termination function */
    RxPipelineNodeConfigFn pipelineNodeConfig;  /**< Pipleline node configuation function */
    RxConfigMsgHandlerFn configMsgHandler;      /**< Configuaraton message handler function */
};

struct RxClusterRef;
struct RxOutputSpec;
struct RxPipelineCluster;

enum RxClusterValidityReq {
    rxCLREQ_DONTWANT = 0, /**<The cluster is required but any data within it is not wanted and will be overwritten */
    rxCLREQ_REQUIRED = 1, /**<The cluster is required and it must contain valid data */
    rxCLREQ_OPTIONAL = 2  /**<The cluster will be used if it is present and contains valid data, otherwise the node will make do without it. */    
};

struct RxIoSpec {
    DWORD					numClustersOfInterest;/**< Number of Clusters of interest */
    RxClusterRef         *clustersOfInterest;   /**< Clusters of interest array */
    RxClusterValidityReq *inputRequirements;    /**< Requirements of the clusters of interest on input to the node */
    DWORD					numOutputs;           /**< Number of outputs from the node */
    RxOutputSpec         *outputs;              /**< Output specification array */
};

struct RxNodeDefinition {
    char *				name;                 
    RxNodeMethods       nodeMethods;          
    RxIoSpec            io;                     /**< Input/output specification */
    DWORD				pipelineNodePrivateDataSize; /**< Size in bytes of node's private data  */
    bool				editable;               /**< Flags whether a node is editable */
    DWORD				InputPipesCnt;          /**< Count of the unlocked pipelines containing this node */
};

struct RxPipelineNodeTopSortData {
    DWORD	numIns;			// Input count 
    DWORD	numInsVisited;  // Count of inputs visited during dependencies propagation/cluster slot allocation 
    rxReq *	req;            // Req used in dependencies propagation/cluster slot allocation 
};

struct RxPipelineNode {
    RxNodeDefinition   *nodeDef;            // Node definition reference 
    DWORD				numOutputs;         // Output count 
    DWORD *				outputs;            // Output array, indexing the pipeline's array of pipeline nodes 
    RxPipelineCluster **slotClusterRefs;    // For packets passing through this node, SlotClusterRefs[n]
                                            // identifies the cluster to be found at index n in the packet's
                                            // Clusters[] array. These assignments are determined at pipeline
                                            // Unlock() time and are good for the life of the pipeline. 
    DWORD *				slotsContinue;      // Cluster persistence table for the node's outputs 
    void *				privateData;        // Pointer to the pipeline Node's private data 
    DWORD *				inputToClusterSlot; // LUT to locate clusters of interest in packets passing through this pipeline Node 
    RxPipelineNodeTopSortData *topSortData; // Internal Use 
    void               *initializationData; // Used by the pipeline node initialisation
                                            // function in setting up the Private data. 
    DWORD				initializationDataSize; // Present so that if the node is cloned we can copy the initialisation data. 
};

enum RwBlendFunction {
    rwBLENDNABLEND = 0,
    rwBLENDZERO,            /**<(0,    0,    0,    0   ) */
    rwBLENDONE,             /**<(1,    1,    1,    1   ) */
    rwBLENDSRCCOLOR,        /**<(Rs,   Gs,   Bs,   As  ) */
    rwBLENDINVSRCCOLOR,     /**<(1-Rs, 1-Gs, 1-Bs, 1-As) */
    rwBLENDSRCALPHA,        /**<(As,   As,   As,   As  ) */
    rwBLENDINVSRCALPHA,     /**<(1-As, 1-As, 1-As, 1-As) */
    rwBLENDDESTALPHA,       /**<(Ad,   Ad,   Ad,   Ad  ) */
    rwBLENDINVDESTALPHA,    /**<(1-Ad, 1-Ad, 1-Ad, 1-Ad) */
    rwBLENDDESTCOLOR,       /**<(Rd,   Gd,   Bd,   Ad  ) */
    rwBLENDINVDESTCOLOR,    /**<(1-Rd, 1-Gd, 1-Bd, 1-Ad) */
    rwBLENDSRCALPHASAT		/**<(f,    f,    f,    1   )  f = min (As, 1-Ad) */
};
enum RwTextureAddressMode {
    rwTEXTUREADDRESSNATEXTUREADDRESS = 0,
    rwTEXTUREADDRESSWRAP,		/**<UV wraps (tiles) */
    rwTEXTUREADDRESSMIRROR,		/**<Alternate UV is flipped */
    rwTEXTUREADDRESSCLAMP,		/**<UV is clamped to 0-1 */
    rwTEXTUREADDRESSBORDER		/**<Border color takes effect outside of 0-1 */
};
enum RwTextureFilterMode {
    rwFILTERNAFILTERMODE = 0,
    rwFILTERNEAREST,                /**<Point sampled */
    rwFILTERLINEAR,                 /**<Bilinear */
    rwFILTERMIPNEAREST,             /**<Point sampled per pixel mip map */
    rwFILTERMIPLINEAR,              /**<Bilinear per pixel mipmap */
    rwFILTERLINEARMIPNEAREST,       /**<MipMap interp point sampled */
    rwFILTERLINEARMIPLINEAR        /**<Trilinear */
};

enum RwFogType {
    rwFOGTYPENAFOGTYPE = 0,
    rwFOGTYPELINEAR,            /**<Linear fog */
    rwFOGTYPEEXPONENTIAL,       /**<Exponential fog */
    rwFOGTYPEEXPONENTIAL2      /**<Exponential^2 fog */
};


struct RwRaster;
struct RxRenderStateVector {
    DWORD					flags;			// A load of the boolean renderstate options 
    DWORD					shadeMode;		// Flat or Gouraud currently 
    RwBlendFunction			srcBlend;		// Src  alpha, 1-src  alpha, etc 
    RwBlendFunction			destBlend;		// Dest alpha, 1-dest alpha, etc 
    RwRaster *				textureRaster;	// texture raster 
    RwTextureAddressMode	addressModeU;	// U addressing mode - WRAP, MIRROR, CLAMP, BORDER 
    RwTextureAddressMode	addressModeV;	// V addressing mode - WRAP, MIRROR, CLAMP, BORDER 
    RwTextureFilterMode		filterMode;		// filtering mode - combos of NEAREST LINEAR MIP 
    DWORD					borderColor;	// RGBA: Border color for texturing address mode border 
    RwFogType				fogType;		// Select the type of fogging to use 
    DWORD					fogColor;		// RGBA: Color used for fogging 
};


static RxHeap * (*RxHeapCreate) (DWORD size) = (RxHeap * (__cdecl *)(DWORD))0x809F90;
static void (*RxHeapDestroy) (RxHeap * heap) = (void (__cdecl *)(RxHeap *))0x809F30;
static void (*rxHeapReset)(RxHeap * heap) = (void (__cdecl *)(RxHeap *))0x805710;

static RxHeap * __rxHeapGlobal;

struct RxPipelineModule {
	CBlockAllocator *	allocator;
	RxRenderStateVector	vector;
	DWORD				_f30;
	DWORD				_f34;
	DWORD				maxNodes;
	DWORD				_f3C[9];
};

static RxPipelineModule * __pipelineModule = NULL;	// structure inside engine block
static DWORD *__pipelineModileOffset = (DWORD *)0xC9BC60;

RxRenderStateVector * (*RxRenderStateVectorSetDefaultRenderStateVector)(RxRenderStateVector *rsvp) = (RxRenderStateVector * (__cdecl *)(RxRenderStateVector *))0x80EA40;
void (*rxPacketDestroy) (RxPacket * packet) = (void (__cdecl *)(RxPacket *))0x80F070;

void * _rwRenderPipelineOpen (void * object, int offset, int size){		// RenderPipeline constructor
	// NOTE: inlined _rxPipelineOpen
	__rxHeapGlobal = RxHeapCreate (4096);	
	if (!__rxHeapGlobal)
		return NULL;

	*__pipelineModileOffset = offset;
	__pipelineModule = (RxPipelineModule *)(((BYTE *)(*engine))+offset);

	if ((__pipelineModule->allocator = new CBlockAllocator (sizeof (RxPipeline), 64)) == NULL) {
		RxHeapDestroy (__rxHeapGlobal);
		__rxHeapGlobal = NULL;
		return NULL;
	}
	
	__pipelineModule->maxNodes = 64;
	RxRenderStateVectorSetDefaultRenderStateVector (&__pipelineModule->vector);
	__pipelineModule->_f30 = 0;
	__pipelineModule->_f34 = 0;
	return object;
}

void * _rwRenderPipelineClose (void * object, int offset, int size) {	// RenderPipeline destructor
	// NOTE: inlined _rxPipelineClose
	if (__pipelineModule->allocator) {
		delete __pipelineModule->allocator;
		__pipelineModule->allocator = NULL;
	}
	RxHeapDestroy (__rxHeapGlobal);
	__rxHeapGlobal = NULL;

	return object;
}

RxPipeline * RxPipelineExecute(RxPipeline  * pipeline, void * data, bool heapReset) {
	if (heapReset && __rxHeapGlobal->dirty)
		rxHeapReset (__rxHeapGlobal);

	RxPipelineNodeParam param;
	param.dataParam = data;
	param.heap = __rxHeapGlobal;
	bool ok = true;

	RxPipelineNode * node = pipeline->nodes;
	if (!node->nodeDef->nodeMethods.nodeBody (node, &param))
		ok = false;
	if (pipeline->embeddedPacketState > 1) {
		pipeline->embeddedPacketState = (rxEmbeddedPacketState)2;
		rxPacketDestroy (pipeline->embeddedPacket);	// TODO: inline it
	}
	return ok ? pipeline : NULL;
}

RxPipeline * RxPipelineCreate () {	
	RxPipeline * pipe = (RxPipeline *)__pipelineModule->allocator->allocateEntry ();
	if (!pipe) 
		debug ("RxPipelineCreate: ERROR: allocator failed\n");		
	else 
		memset (pipe, 0, sizeof (RxPipeline));

	debug ("RxPipelineCreate: %08x\n", (DWORD)pipe);
	return pipe;
}

void (*RxPipelineNodeDestroy)(RxPipelineNode * node, RxPipeline * pipe) = (void (__cdecl *)(RxPipelineNode * node, RxPipeline * pipe))0x8050C0;
//void PipelineNodeDestroy (RxPipelineNode * node, RxPipeline * pipe)  {

//}

void RxPipelineDestroy (RxPipeline * pipe) {
	if (pipe->nodes) {
		for (DWORD i = 0; i < pipe->numNodes; i++)
			RxPipelineNodeDestroy (pipe->nodes+i, pipe);		
		pipe->nodes = NULL;
	}
	if (pipe->superBlock) {
		(*engine)->mem.rwfree (pipe->superBlock);
		pipe->superBlock = NULL;
		pipe->superBlockSize = 0;
	}
	__pipelineModule->allocator->freeEntry ((BYTE *)pipe);
	debug ("RxPipelineDestroy: %08x\n", (DWORD)pipe);
}
