#ifndef SPECIAL_EFFECTS
#define SPECIAL_EFFECTS
#include <string.h>
#include <stdlib.h>
#include <errno.h>

/*
**Author: COM
**I'a Nyarlathotep!
*/

char *implode(char*, char**, unsigned int);
char **explode(char*,unsigned int, char*, unsigned int);
int explode_expand(char***, char***, unsigned int);
void freeExplode(char**);

/*
**Returns an array of C-strings that are the substrings in tosplit delimited by delim, mimicking PHP's explode function.
**The returned array is dynamically allocated and it is suggested that you use freeExplode on the returned pointer when done with it.
**On error, errno is set with one of the following values and NULL is returned:
**1 - Could not allocate space for a substring
**2 - Could not allocate space to hold the substrings
**3 - Length of delimiter is greater than length of string to split
**4 - String to explode has no length
*/
char **explode(char *delim, unsigned int dellen, char *tosplit, unsigned int splitlen)
{
	char **subs, **tmp;
	unsigned int cur, i, offs, size;
	if(dellen>splitlen)
	{
		errno=3;
		return NULL;
	}
	if(splitlen==0)
	{
		errno=4;
		return NULL;
	}
	if(delim==NULL || dellen==0)
	{
		subs=(char**)malloc((splitlen+1)*sizeof(char*));
		if(subs==NULL)
		{
			errno=2;
			return NULL;
		}
		for(i=0;i<splitlen;i++)
		{
			subs[i]=(char*)malloc(2*sizeof(char));
			if(subs[i]==NULL)
			{
				freeExplode(subs);
				errno=1;
				return NULL;
			}
			subs[i][0]=tosplit[i];
			subs[i][1]=0;
		}
		subs[i]=NULL;
		return subs;
	}
	subs=(char**)malloc(sizeof(char*));
	if(subs==NULL)
	{
		errno=2;
		return NULL;
	}
	subs[0]=NULL;
	size=0;
	for(cur=offs=0;cur+offs<splitlen-dellen+1;)
	{
		if(!strncmp(delim, tosplit+cur+offs, dellen))
		{
			if(offs==0)
			{
				cur+=dellen;
				continue;
			}
			if(explode_expand(&subs, &tmp, size))
				return NULL;
			subs[size]=(char*)malloc((offs+1)*sizeof(char));
			if(subs[size]==NULL)
			{
				errno=1;
				freeExplode(subs);
				return NULL;
			}
			strncpy(subs[size], tosplit+cur, offs);
			subs[size][offs]=0;
			size++;
			cur+=offs+dellen;
			offs=0;
			subs[size]=NULL;
		}
		else
			offs++;
	}
	if(cur<splitlen)
	{
		if(explode_expand(&subs, &tmp, size))
			return NULL;
		subs[size]=(char*)malloc((splitlen-cur+1)*sizeof(char));
		if(subs[size]==NULL)
		{
			errno=1;
			freeExplode(subs);
			return NULL;
		}
		strncpy(subs[size], tosplit+cur, splitlen-cur);
		subs[size][splitlen-cur]=0;
		subs[size+1]=NULL;
	}
	return subs;
}

int explode_expand(char ***subs, char ***tmp, unsigned int size)
{
	unsigned int i, j;
	*tmp=(char**)realloc(*subs, (size+2)*sizeof(char*));
	if(tmp==NULL)
	{
		*tmp=(char**)malloc((size+2)*sizeof(char*));
		if(tmp!=NULL)
		{
			for(i=0;i<size;i++)
			{
				(*tmp)[i]=(char*)malloc((strlen((*subs)[i])+1)*sizeof(char));
				if((*tmp)[i]==NULL)
				{
					for(j=0;j<i;j++)
						free((*tmp)[j]);
					free(*tmp);
					for(;i<size;i++)
						free((*subs)[i]);
					free(*subs);
					errno=2;
					return 2;
				}
				strcpy((*subs)[i],(*tmp)[i]);
				free((*subs)[i]);
			}
			free(*subs);
		}
	}
	*subs=*tmp;
	return 0;
}

/*
**Frees any array of C-strings allocated with explode.
*/
void freeExplode(char **subs)
{
	unsigned int i;
	for(i=0;subs[i]!=NULL;i++)
		free(subs[i]);
	free(subs);
}

/*
**Returns a new null terminated C-string made of the parts in toglue with the string glue between them, mimicking PHP's implode function.
**As the returned value is a simple C-string, a regular call to free will suffice to free the memory pointed to by the returned value.
**On error, errno is set with one of the following values and NULL is returned:
**1 - NULL pointer supplied as array
**2 - NULL pointer in array
**3 - Failed to allocate space for string sizes
**4 - Failed to allocate space for the new string
*/
char *implode(char *glue, char **toglue, unsigned int size)
{
	char *whole;
	unsigned int i, glueLen, *sizes, totalSize;
	if(toglue==NULL)
	{
		errno=1;
		return NULL;
	}
	glueLen=glue==NULL?0:strlen(glue);
	sizes=(unsigned int*)malloc(size*sizeof(unsigned int));
	if(sizes==NULL)
	{
		errno=3;
		return NULL;
	}
	for(i=totalSize=0;i<size;i++)
	{
		if(toglue[i]==NULL)
		{
			free(sizes);
			errno=2;
			return NULL;
		}
		sizes[i]=strlen(toglue[i]);
		totalSize+=sizes[i];
	}
	totalSize+=(size-1)*glueLen;
	whole=(char*)malloc((totalSize+1)*sizeof(char));
	if(whole==NULL)
	{
		free(sizes);
		errno=4;
		return NULL;
	}
	for(totalSize=i=0;i<size-1;i++)
	{
		memcpy(whole+totalSize, toglue[i], sizes[i]);
		totalSize+=sizes[i];
		memcpy(whole+totalSize, glue, glueLen);
		totalSize+=glueLen;
	}
	memcpy(whole+totalSize, toglue[i], sizes[i]);
	whole[totalSize+sizes[i]]=0;
	free(sizes);
	return whole;
}
#endif