/*
	SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
	http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/


#pragma once

#include <ctype.h> // for size_t
#include <cstdlib>
#include <cstring>

#include "SC_Types.h"
#include "scsynthsend.h"

////////////////////////////////////////////////////////////////////////

// replacement for calloc.
// calloc lazily zeroes memory on first touch. This is good for most purposes, but bad for realtime audio.
void* zalloc(size_t n, size_t size);
void zfree(void* ptr);

////////////////////////////////////////////////////////////////////////

void World_Run(struct World *inWorld);
void World_Start(World *inWorld);
void World_SetSampleRate(struct World *inWorld, double inSampleRate);

extern "C" {
void* World_Alloc(struct World *inWorld, size_t inByteSize);
void* World_Realloc(struct World *inWorld, void *inPtr, size_t inByteSize);
void World_Free(struct World *inWorld, void *inPtr);
void World_NRTLock(World *world);
void World_NRTUnlock(World *world);
}

size_t World_TotalFree(struct World *inWorld);
size_t World_LargestFreeChunk(struct World *inWorld);


int32 GetKey(struct Node *inNode);
int32 GetHash(struct Node *inNode);
bool World_AddNode(struct World *inWorld, struct Node* inNode);
bool World_RemoveNode(struct World *inWorld, struct Node* inNode);

extern "C" {
struct Node* World_GetNode(struct World *inWorld, int32 inID);
struct Graph* World_GetGraph(struct World *inWorld, int32 inID);
}

struct Group* World_GetGroup(struct World *inWorld, int32 inID);

int32 *GetKey(struct UnitDef *inUnitDef);
int32 GetHash(struct UnitDef *inUnitDef);
bool AddUnitDef(struct UnitDef* inUnitDef);
bool RemoveUnitDef(struct UnitDef* inUnitDef);
struct UnitDef* GetUnitDef(int32* inKey);

int32 *GetKey(struct BufGen *inBufGen);
int32 GetHash(struct BufGen *inBufGen);
bool AddBufGen(struct BufGen* inBufGen);
bool RemoveBufGen(struct BufGen* inBufGen);
struct BufGen* GetBufGen(int32* inKey);

int32 *GetKey(struct PlugInCmd *inPlugInCmd);
int32 GetHash(struct PlugInCmd *inPlugInCmd);
bool AddPlugInCmd(struct PlugInCmd* inPlugInCmd);
bool RemovePlugInCmd(struct PlugInCmd* inPlugInCmd);
struct PlugInCmd* GetPlugInCmd(int32* inKey);
int PlugIn_DoCmd(struct World *inWorld, int inSize, char *inArgs, struct ReplyAddress *inReply);

int32 *GetKey(struct GraphDef *inGraphDef);
int32 GetHash(struct GraphDef *inGraphDef);
void World_AddGraphDef(struct World *inWorld, struct GraphDef* inGraphDef);
void World_RemoveGraphDef(struct World *inWorld, struct GraphDef* inGraphDef);
struct GraphDef* World_GetGraphDef(struct World *inWorld, int32* inKey);
void World_FreeAllGraphDefs(World *inWorld);
void GraphDef_Free(GraphDef *inGraphDef);
void GraphDef_Define(World *inWorld, GraphDef *inList);
void GraphDef_FreeOverwritten(World *inWorld);

SCErr bufAlloc(struct SndBuf* buf, int numChannels, int numFrames, double sampleRate);

////////////////////////////////////////////////////////////////////////

void Rate_Init(struct Rate *inRate, double inSampleRate, int inBufLength);

////////////////////////////////////////////////////////////////////////

#define GRAPHDEF(inGraph) ((GraphDef*)((inGraph)->mNode.mDef))
#define GRAPH_PARAM_TABLE(inGraph) (GRAPHDEF(inGraph)->mParamSpecTable)

int Graph_New(struct World *inWorld, struct GraphDef *def, int32 inID, struct sc_msg_iter* args, struct Graph** outGraph,bool argtype=true);
void Graph_Ctor(struct World *inWorld, struct GraphDef *inGraphDef, struct Graph *graph, struct sc_msg_iter *msg,bool argtype);
void Graph_Dtor(struct Graph *inGraph);
int  Graph_GetControl(struct Graph* inGraph, uint32 inIndex, float& outValue);
int  Graph_GetControl(struct Graph* inGraph, int32 inHash, int32 *inName, uint32 inIndex, float& outValue);
void Graph_SetControl(struct Graph* inGraph, uint32 inIndex, float inValue);
void Graph_SetControl(struct Graph* inGraph, int32 inHash, int32 *inName, uint32 inIndex, float inValue);
void Graph_MapControl(Graph* inGraph, uint32 inIndex, uint32 inBus);
void Graph_MapControl(Graph* inGraph, int32 inHash, int32 *inName, uint32 inIndex, uint32 inBus);
void Graph_MapAudioControl(Graph* inGraph, uint32 inIndex, uint32 inBus);
void Graph_MapAudioControl(Graph* inGraph, int32 inHash, int32 *inName, uint32 inIndex, uint32 inBus);
void Graph_Trace(Graph *inGraph);

////////////////////////////////////////////////////////////////////////

int Node_New(struct World *inWorld, struct NodeDef *def, int32 inID, struct Node **outNode);
void Node_Dtor(struct Node *inNode);
void Node_Remove(struct Node* s);
void Node_RemoveID(Node *inNode);
void Node_Delete(struct Node* inNode);
void Node_AddAfter(struct Node* s, struct Node *afterThisOne);
void Node_AddBefore(struct Node* s, struct Node *beforeThisOne);
void Node_Replace(struct Node* s, struct Node *replaceThisOne);
void Node_SetControl(Node* inNode, int inIndex, float inValue);
void Node_SetControl(Node* inNode, int32 inHash, int32 *inName, int inIndex, float inValue);
void Node_MapControl(Node* inNode, int inIndex, int inBus);
void Node_MapControl(Node* inNode, int32 inHash, int32 *inName, int inIndex, int inBus);
void Node_MapAudioControl(Node* inNode, int inIndex, int inBus);
void Node_MapAudioControl(Node* inNode, int32 inHash, int32 *inName, int inIndex, int inBus);
void Node_StateMsg(Node* inNode, int inState);
void Node_Trace(Node* inNode);
void Node_SendReply(Node* inNode, int replyID, const char* cmdName, int numArgs, const float* values);
void Node_SendReply(Node* inNode, int replyID, const char* cmdName, float value);

extern "C" {
void Node_SetRun(Node* inNode, int inRun);
void Node_SendTrigger(Node* inNode, int triggerID, float value);
void Node_End(struct Node* inNode);
void Node_NullCalc(struct Node* inNode);
void Unit_DoneAction(int doneAction, struct Unit* unit);
}

////////////////////////////////////////////////////////////////////////

extern "C" {
void Group_Calc(Group *inGroup);
void Graph_Calc(struct Graph *inGraph);
}

int Group_New(World *inWorld, int32 inID, Group** outGroup);
void Group_Dtor(Group *inGroup);
void Group_DeleteAll(Group *inGroup);
void Group_DeepFreeGraphs(Group *inGroup);
void Group_AddHead (Group *s, Node *child);
void Group_AddTail (Group *s, Node *child);
void Group_Insert(Group *s, Node *child, int inIndex);
void Group_SetControl(struct Group* inGroup, uint32 inIndex, float inValue);
void Group_SetControl(struct Group *inGroup, int32 inHash, int32 *inName, uint32 inIndex, float inValue);
void Group_MapControl(Group* inGroup, uint32 inIndex, uint32 inBus);
void Group_MapControl(Group* inGroup, int32 inHash, int32 *inName, uint32 inIndex, uint32 inBus);
void Group_MapAudioControl(Group* inGroup, uint32 inIndex, uint32 inBus);
void Group_MapAudioControl(Group* inGroup, int32 inHash, int32 *inName, uint32 inIndex, uint32 inBus);
void Group_Trace(Group* inGroup);
void Group_DumpTree(Group* inGroup);
void Group_DumpTreeAndControls(Group* inGroup);
void Group_CountNodeTags(Group* inGroup, int* count);
void Group_CountNodeAndControlTags(Group* inGroup, int* count, int* controlAndDefCount);
void Group_QueryTree(Group* inGroup, big_scpacket* packet);
void Group_QueryTreeAndControls(Group* inGroup, big_scpacket *packet);

////////////////////////////////////////////////////////////////////////

struct Unit* Unit_New(struct World *inWorld, struct UnitSpec *inUnitSpec, char*& memory);
void Unit_EndCalc(struct Unit *inUnit, int inNumSamples);
void Unit_End(struct Unit *inUnit);

void Unit_Dtor(struct Unit *inUnit);

extern "C" {
void Unit_ZeroOutputs(struct Unit *inUnit, int inNumSamples);
}

////////////////////////////////////////////////////////////////////////

void SendDone(struct ReplyAddress *inReply, const char *inCommandName);
void SendDoneWithIntValue(struct ReplyAddress *inReply, const char *inCommandName, int value);
void SendDoneWithVarArgs(struct ReplyAddress *inReply, const char *inCommandName, const char *oscFormat, ...);
void SendFailure(struct ReplyAddress *inReply, const char *inCommandName, const char *errString);
void SendFailureWithIntValue(struct ReplyAddress *inReply, const char *inCommandName, const char *errString, uint32 index);
void SendFailureWithVarArgs(struct ReplyAddress *inReply, const char *inCommandName, const char *errString, const char *oscFormat, ...);
void ReportLateness(struct ReplyAddress *inReply, float32 seconds);
int32 Hash(struct ReplyAddress *inReplyAddress);

////////////////////////////////////////////////////////////////////////

extern "C" {
int32 server_timeseed();
}

////////////////////////////////////////////////////////////////////////

typedef bool (*AsyncStageFn)(World *inWorld, void* cmdData);
typedef void (*AsyncFreeFn)(World *inWorld, void* cmdData);

int PerformAsynchronousCommand
	(
		World *inWorld,
		void* replyAddr,
		const char* cmdName,
		void *cmdData,
		AsyncStageFn stage2, // stage2 is non real time
		AsyncStageFn stage3, // stage3 is real time - completion msg performed if stage3 returns true
		AsyncStageFn stage4, // stage4 is non real time - sends done if stage4 returns true
		AsyncFreeFn cleanup,
		int completionMsgSize,
		void* completionMsgData
	);

////////////////////////////////////////////////////////////////////////
