/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2003
 */

#include "Grouping.h"

/*
 * Initializes the grouping structure (must be finally deleted, as it uses
 * dinamically allocated memory)
 * Returns 0 if OK, <0 otherwise
 */
int
groupingCreate (gl, ncb)
    group_list *gl;
    int ncb;
{
    if (!(gl->cb = malloc(sizeof(cblock_t)*ncb)))
	return -1;
    gl->cb_size=0;
    gl->p_cb=0;
    gl->gr=NULL;
    gl->gr_size=0;
    gl->lgr=NULL;
    gl->lgr_size=0;
    return 0;
}

/*
 * Frees the memory used by the grouping structure
 */
void
groupingDelete (gl)
    group_list *gl;
{
    free(gl->cb);
    free(gl->gr);
    free(gl->lgr);
}

/*
 * Returns the first block of the grouping
 * (NULL if it's empty)
 */
cblock_t*
groupingBlockFirst (gl)
    group_list *gl;
{
    gl->p_cb=0;
    if (gl->p_cb>=gl->cb_size)
	return NULL;
    return &gl->cb[0];
}

/*
 * Returns the current block of the grouping 
 * (NULL if empty)
 */
cblock_t*
groupingBlockCurrent (gl)
    group_list *gl;
{
    if (gl->cb_size==0)
	return NULL;
    return &gl->cb[gl->p_cb];
}

/*
 * Returns the next block of the grouping 
 * (NULL if empty)
 */
cblock_t*
groupingBlockNext (gl)
    group_list *gl;
{
    if (gl->p_cb >= gl->cb_size-1)
	return NULL;
    return &gl->cb[++(gl->p_cb)];
}

/*
 * Adds a code block to the grouping
 * Return 0 on success, <0 otherwise
 */
int
blockNew (gl, bstrt, bend, cstrt, cend, slang, subgrp, exectbl, exitv)
    group_list *gl;
    char *bstrt;
    char *bend;
    char *cstrt;
    char *cend;
    char *slang;
    char *subgrp;
    bool exectbl;
    bool exitv;
{
    cblock_t *bl;
    lang_t lang;
    group_t *gr;
    int i;

    /* new code block */
    bl=&gl->cb[gl->cb_size];
    gl->cb_size++;
    bl->bstrt=bstrt;
    bl->bend=bend;
    bl->cstrt=cstrt;
    bl->cend=cend;
    bl->exectbl=exectbl;
    bl->exitv=exitv;

    /* search group */
    lang=slangTolang_t(slang);
    if (lang==bError) {
	printf("Language not defined and no hashbang (!).");
	return -3;
    }
    if (strcmp(subgrp,"")!=0) {
	int done=0;
	for (i=0; !done && i<gl->gr_size; i++) {
	    gr=&gl->gr[i];
	    if (gr->lang==lang 
		    && ((lang==CUSTOM && strcmp(gr->slang,slang)==0)
			|| lang!=CUSTOM)
		    && strcmp(gr->subgrp,subgrp)==0) {
		groupAddBlock(&gl->gr,i,bl);
		done=1;
	    }
	}
	if (!done) {
	    if (groupNew(gl,bl,GROUPED,lang,slang,subgrp)<0)
		return -4;
	}
    }
    else {
	if (groupNew(gl,bl,LONE,lang,slang,subgrp)<0)
	    return -4;
    }
    return 0;
}

/*
 * Returns the start of the code block
 */
char*
blockStrt (bl)
    cblock_t *bl;
{
    if (bl!=NULL) /* out of place, but it can be called with the */
	          /* result of groupingBlockFirst/Next           */
	return bl->bstrt;
    return NULL;
}

/*
 * Returns the end of the code block
 */
char*
blockEnd (bl)
    cblock_t *bl;
{
    if (bl!=NULL) /* out of place, but it can be called with the */
	          /* result of groupingBlockCurrent              */
	return bl->bend;
    return NULL;
}

/*
 * Returns the lang_t of a code block (must be defined or CUSTOM,
 * otherwise returns an error, <0)
 */
lang_t
blockLang (bl)
    cblock_t *bl;
{
    return (*((group_t**)bl->gr))[bl->ngr].lang;
}

/*
 * Returns the language string of a code block
 */
char*
blockSLang (bl)
    cblock_t *bl;
{
    return (*((group_t**)bl->gr))[bl->ngr].slang;
}

/*
 * Returns the code block's preferred exit value
 */
bool
blockExitV (bl)
    cblock_t *bl;
{
    return bl->exitv;
}

/*
 * Returns 1 if code block is executable, 0 otherwise
 */
int
blockExecutable (bl)
    cblock_t *bl;
{
    return bl->exectbl;
}

/*
 * Copies the start address of the specified code block and
 * it's address
 */
void
blockCodeParms (bl,cstrt,clen)
    cblock_t *bl;
    char **cstrt;
    int *clen;
{
   *cstrt=bl->cstrt;
   *clen=(bl->cend)-(bl->cstrt)+1;
}

/*
 * Gets the group of the specified code block
 */
group_t*
blockGroup (bl)
    cblock_t *bl;
{
    return &(*((group_t**)bl->gr))[bl->ngr];
}

/*
 * Creates a new group with the specified code block
 * Returns 0 on success, <0 otherwise
 */
int
groupNew (gl, bl, grp, lang, slang, subgrp)
    group_list *gl;
    cblock_t *bl;
    grouping_t grp;
    lang_t lang;
    char *slang;
    char *subgrp;
{
    group_t *gr;

    if (grp==GROUPED) {
	if (gl->gr_size>gl->gr_size+1)
	    return -1;
	if (!(gl->gr = realloc(gl->lgr,sizeof(group_t)*(gl->gr_size+1))))
	    return -2;
	gr=&gl->gr[gl->gr_size];
	bl->gr=(_group_t**)&gl->gr;
	bl->ngr=gl->gr_size;
	gl->gr_size++;
    }
    else {
	if (gl->lgr_size>gl->lgr_size+1)
	    return -1;
	if (!(gl->lgr = realloc(gl->lgr,sizeof(group_t)*(gl->lgr_size+1))))
	    return -2;
	gr=&gl->lgr[gl->lgr_size];
	bl->gr=(_group_t**)&gl->lgr;
	bl->ngr=gl->lgr_size;
	gl->lgr_size++;
    }
    gr->lang=lang;
    gr->slang=slang;
    gr->subgrp=subgrp;
    q_ini_queue(&gr->cb);
    q_enqueue(&gr->cb,(node_t*)bl);
    gr->cur=NULL;
    return 0;
}

/*
 * Adds a code block to an existing group
 */
void
groupAddBlock (gr, ngr, bl)
    group_t **gr;
    int ngr;
    cblock_t *bl;
{
    q_enqueue(&(*gr)[ngr].cb,(node_t*)bl);
    bl->gr=(_group_t**)gr;
    bl->ngr=ngr;
}

/*
 * Returns the first declared code block of the group,
 * NULL if there are no blocks
 * (only returns executable code blocks if they're bl)
 */
cblock_t*
groupBlockFirst (gr, sbl)
    group_t *gr;
    cblock_t *sbl;
{
    /* Queue is never empty */
    gr->cur=(cblock_t*)q_first(&gr->cb);
    while (!q_last(&gr->cb,(node_t*)gr->cur)
		&& gr->cur->exectbl==true && gr->cur!=sbl)
	gr->cur=(cblock_t*)q_next(&gr->cb,(node_t*)gr->cur);
    return gr->cur;
}

/*
 * Returns the next declared code block of the group,
 * NULL if there are no more
 * (only returns executable code blocks if they're bl)
 */
cblock_t*
groupBlockNext (gr, sbl)
    group_t *gr;
    cblock_t *sbl;
{
    /* Queue is never empty */
    if (q_last(&gr->cb,(node_t*)gr->cur))
	return NULL;
    gr->cur=(cblock_t*)q_next(&gr->cb,(node_t*)gr->cur);
    while (!q_last(&gr->cb,(node_t*)gr->cur)
		&& gr->cur->exectbl==true && gr->cur!=sbl)
	gr->cur=(cblock_t*)q_next(&gr->cb,(node_t*)gr->cur);
    return gr->cur;
}

