#include "common.h"
#include "pfType.h"
#include "pfScope.h"
#include "pfToken.h"
#include "pfCompile.h"
#include "pfParse.h"
#include "refCount.h"
#include "ctar.h"
#include "pfPreamble.h"
#include "cCoder.h"

static boolean varUsed(struct pfVar *var, struct pfParse *pp)
/* Return TRUE if parse subtree uses var. */
{
switch (pp->type)
    {
    case pptVarUse:
	if (pp->var == var)
	    return TRUE;
	break;
    }
for (pp = pp->children; pp != NULL; pp = pp->next)
    {
    if (varUsed(var, pp))
        return TRUE;
    }
return FALSE;
}

static boolean varWritten(struct pfVar *var, struct pfParse *pp)
/* Return TRUE if parse subtree  writes to var. */
{
switch (pp->type)
    {
    case pptAssign:
    case pptPlusEquals:
    case pptMinusEquals:
    case pptMulEquals:
    case pptDivEquals:
        if (varUsed(var, pp->children))
	    return TRUE;
	break;
    }
for (pp = pp->children; pp != NULL; pp = pp->next)
    {
    if (varWritten(var, pp))
        return TRUE;
    }
return FALSE;
}

#if defined(PARALLEL)
void codeParaDo(struct pfCompile *pfc, FILE *f, struct pfParse *para)
/* Emit C code for para ... do statement. */
{
struct pfParse *collectionPp = para->children;
struct pfParse *enclosingFunction = pfParseEnclosingFunction(para);
struct pfBaseType *base = collectionPp->ty->base;
if (base == pfc->indexRangeType)
    {
    struct pfParse *fromPp = collectionPp->children;
    struct pfParse *toPp = fromPp->next;
    int offset = codeExpression(pfc, f, fromPp, 0, FALSE);
    codeExpression(pfc, f, toPp, offset, FALSE);
    fprintf(f, "_pf_paraRunRange(");
    codeParamAccess(pfc, f, pfc->longType, 0);
    fprintf(f, ", ");
    codeParamAccess(pfc, f, pfc->longType, offset);
    }
else if (base == pfc->arrayType)
    {
    codeExpression(pfc, f, collectionPp, 0, FALSE);
    fprintf(f, "_pf_paraRunArray(");
    codeParamAccess(pfc, f, collectionPp->ty->base, 0);
    }
else if (base == pfc->dirType)
    {
    codeExpression(pfc, f, collectionPp, 0, FALSE);
    fprintf(f, "_pf_paraRunDir(");
    codeParamAccess(pfc, f, collectionPp->ty->base, 0);
    }
else
    internalErrAt(para->tok);
fprintf(f, ", ");
if (enclosingFunction)
    fprintf(f, "&_pf_l, sizeof(_pf_l), ");
else
    fprintf(f, "0, 0, ");
fprintf(f, "%s, ", para->name);
fprintf(f, "%d, 0);\n", prtParaDo);
}

static char *paraBlockName(int *pCounter)
/* Generate a unique-to-module name for this block. */
{
char buf[256];
safef(buf, sizeof(buf), "_pf_para%d", ++(*pCounter));
return cloneString(buf);
}

static char *figureDeref(struct pfCompile *pfc,
	struct pfParse *collectionPp, struct pfParse *elPp)
/* Figure out how many levels to dereference - a single for arrays,
 * double for dirs of objects, triple for dirs of numbers. */
{
if (collectionPp->ty->base == pfc->dirType)
    {
    struct pfBaseType *base = elPp->ty->base;
    if (base->parent == pfc->numType)
        return "***";
    else
        return "**";
    }
else
    return "*";
}

static void codeParaBlockFunction(struct pfCompile *pfc, FILE *f, 
	struct pfParse *para, struct pfParse *enclosingFunction,
	enum paraRunType pftType)
/* Code para block function */
{
struct pfParse *collectionPp = para->children;
struct pfBaseType *colBase = collectionPp->ty->base;
char *deref;
struct pfParse *elIxPp = collectionPp->next;
struct pfParse *body = elIxPp->next;
struct dyString *elName;
struct dyString *keyName = NULL;
struct pfParse *elPp, *ixPp = NULL;
struct pfBaseType *elBase;

if (elIxPp->type == pptKeyVal)
    {
    ixPp = elIxPp->children;
    elPp = ixPp->next;
    keyName = codeVarName(pfc,ixPp->var);
    }
else
    elPp = elIxPp;

deref = figureDeref(pfc, collectionPp, elPp);
elBase = elPp->ty->base;
elName = codeVarName(pfc, elPp->var);

fprintf(f, "static void %s(%s *%s, char *_pf_key, void *_pf_item, void *_pf_localVars)\n", 
	para->name, cStackType, cStackName);
fprintf(f, "{\n");
if (enclosingFunction != NULL)
    {
    struct ctar *ctar = enclosingFunction->var->ctar;
    ctarCodeLocalStruct(ctar, pfc, f, "_pf_localVars");
    }
else
    codeScopeVars(pfc, f, para->scope, FALSE);
fprintf(f, "%s = %s((", elName->string, deref);
codeBaseType(pfc, f, elBase);
fprintf(f, "%s)_pf_item);\n", deref);
if (keyName != NULL)
    {
    fprintf(f, "%s = ", keyName->string);
    if (ixPp->ty->base == pfc->stringType)
	fprintf(f, "_pf_string_from_const(_pf_key);\n");
    else
        fprintf(f, "_pf_key - (char*)0;\n");
    }
codeStatement(pfc, f, body);
if (varWritten(elPp->var, body))
    {
    struct pfVar *var = elPp->var;
    struct dyString *elName = codeVarName(pfc, var);
    fprintf(f, "%s((", deref);
    codeBaseType(pfc, f, var->ty->base);
    fprintf(f, "%s)_pf_item) = %s;\n", deref, elName->string);
    dyStringFree(&elName);
    }
if (colBase == pfc->dirType && ixPp != NULL)
    codeCleanupVar(pfc, f, ixPp->var);
fprintf(f, "}\n\n");
dyStringFree(&elName);
dyStringFree(&keyName);
}

static void codeParaDoBlock(struct pfCompile *pfc, FILE *f, 
	int *pCounter, struct pfParse *enclosingFunction, 
	struct pfParse *enclosingClass, struct pfParse *para)
/* Generate a function for inside of para do. */
{
bool oldIsFunc = pfc->isFunc;
bool oldCodingPara = pfc->codingPara;
pfc->codingPara = TRUE;
pfc->isFunc = (enclosingFunction != NULL);
para->name = paraBlockName(pCounter);
codeParaBlockFunction(pfc, f, para, enclosingFunction, prtParaDo);
pfc->isFunc = oldIsFunc;
pfc->codingPara = oldCodingPara;
}

static void rCodeParaBlocks(struct pfCompile *pfc, FILE *f, 
	int *pCounter, struct pfParse *enclosingFunction, 
	struct pfParse *enclosingClass, struct pfParse *pp)
/* Generate a little subroutine for each para block. */
{
switch (pp->type)
    {
    case pptToDec:
    case pptFlowDec:
        enclosingFunction = pp;
	break;
    case pptClass:
        enclosingClass = pp;
	break;
    case pptParaDo:
        codeParaDoBlock(pfc, f, pCounter, enclosingFunction, enclosingClass,
		pp);
	break;
    }
for (pp = pp->children; pp != NULL; pp = pp->next)
    rCodeParaBlocks(pfc, f, pCounter, enclosingFunction, enclosingClass, pp);
}

void codeParaBlocks(struct pfCompile *pfc, FILE *f, struct pfParse *module)
/* Create functions for code inside of para blocks. */
{
int count = 0;
rCodeParaBlocks(pfc, f, &count, NULL, NULL, module);
}

#else /* defined(PARALLEL) */

static void saveBackToCollection(struct pfCompile *pfc, FILE *f,
	int stack, struct pfParse *elPp, struct pfParse *collectionPp)
/* Save altered value of element back to collection. */
{
struct pfBaseType *base = collectionPp->ty->base;
struct dyString *elName = codeVarName(pfc, elPp->var);
if (elPp->ty->base->needsCleanup)
    codeVarIncRef(f, elName->string);
if (base == pfc->arrayType)
    {
    fprintf(f, "*((");
    codeBaseType(pfc, f, elPp->ty->base);
    fprintf(f, "*)(_pf_collection->elements + _pf_offset)) = %s;\n",
    	elName->string);
    }
else if (base == pfc->stringType)
    {
    internalErr();	/* Type check module should prevent this */
    }
else if (base == pfc->dyStringType)
    {
    fprintf(f, "_pf_collection->s[_pf_offset] = %s;\n", elName->string);
    }
else if (base == pfc->dirType)
    {
    codeParamAccess(pfc, f, elPp->ty->base, stack);
    fprintf(f, " = %s;\n", elName->string);
    if (elPp->ty->base->needsCleanup)
	{
	fprintf(f, "_pf_dir_add_obj(_pf_collection, _pf_key, %s+%d);\n",  
		cStackName, stack);
	}
    else 
	{
	fprintf(f, "_pf_dir_add_num(_pf_collection, _pf_key, %s+%d);\n",  
		cStackName, stack);
	}
    }
else if (base == pfc->indexRangeType)
    {
    /* Do nothing. */
    }
else
    {
    internalErr();
    }
dyStringFree(&elName);
}

void codeParaDo(struct pfCompile *pfc, FILE *f,
	struct pfParse *para)
/* Emit C code for para ... do statement. */
{
struct pfParse *collectionPp = para->children;
struct pfParse *elPp = collectionPp->next;
struct pfParse *body = elPp->next;
struct pfParse *elVarPp = NULL;

codeStartElInCollectionIteration(pfc, f, 0, 
	para->scope, elPp, collectionPp, TRUE);
codeStatement(pfc, f, body);
if (elPp->type == pptKeyVal)
    {
    struct pfParse *keyPp = elPp->children;
    struct pfParse *valPp = keyPp->next;
    elVarPp = valPp;
    }
else if (elPp->type == pptVarInit)
    elVarPp = elPp;
else
    internalErrAt(elPp->tok);
if (varWritten(elVarPp->var, body))
    saveBackToCollection(pfc, f, 0, elVarPp, collectionPp);
codeEndElInCollectionIteration(pfc, f, para->scope, elPp, collectionPp, 
	TRUE);
}

void codeParaBlocks(struct pfCompile *pfc, FILE *f, struct pfParse *module)
/* Create functions for code inside of para blocks. */
{
}

#endif /* defined(PARALLEL) */

