#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
#include <string.h>
#include <errno.h>
#include "ctlString.h"
#include "ctlError.h"


/* Internal prototype */
void setCmStrErr(enum cmStrErrCode ecode, char *func, int line, char *msg);



/* Create/Extend character string */
char *reallocChar(char *str, size_t size)
{
	char *ret;

	ret = (char *)realloc(str, size + 1);
	if(ret == NULL) {
		setCmStrErr(CM_STR_ERRNO, "reallocChar", __LINE__, strerror(errno));
		return NULL;
	}
	if(str == NULL) {
		*ret = '\0';
	}
	return ret;
}


/* Get one line */
int getLine(char *str, char *result, size_t max)
{
	int cnt = 0;
	char c;

	if((str == NULL) || (result == NULL)) {
		setCmStrErr(CM_STR_NULL, "getLine", __LINE__, NULL);
		return -1;
	}
	*result = '\0';
	while((c = *(str + cnt)) != '\0') {
		if(c == '\n') {
			return ++cnt;
		}
		if(cnt < max) {
			*result++ = *(str + cnt);
			*result = '\0';
		}
		cnt++;
	}
	return cnt;
}


/* String matching function */
int matchRegex(char *str, char *pattern, int cflags)
{
	int ret, err;
	char emsg[CTL_ERROR_MESSAGE_MAX + 1];
	regex_t reg;

	if((str == NULL) || (pattern == NULL)) {
		setCmStrErr(CM_STR_NULL, "matchRegex", __LINE__, NULL);
		return REGEX_RESULT_ERROR;
	}
	err = regcomp(&reg, pattern, cflags);
	if(err != 0) {
		regerror(err, &reg, emsg, CTL_ERROR_MESSAGE_MAX);
		regfree(&reg);
		setCmStrErr(CM_STR_REGEX, "matchRegex", __LINE__, emsg);
		return REGEX_RESULT_ERROR;
	}
	ret = regexec(&reg, str, 0, NULL, 0);
	regfree(&reg);
	ret = (ret == 0) ? REGEX_RESULT_MATCH : REGEX_RESULT_UNMATCH;
	return ret;
}


/* Number of space and TAB from head of string */
int skipSpaceTab(char *line)
{
	int cnt = 0;
	char c;

	if(line == NULL) {
		setCmStrErr(CM_STR_NULL, "skipSpaceTab", __LINE__, NULL);
		return -1;
	}
	while((c = *(line + cnt)) != '\0') {
		if((c == ' ') || (c == '\t')) {
			cnt++;
			continue;
		}
		break;
	}
	return cnt;
}


/* Create/Copy character string for sub-string */
char *getSubString(char *line, int size, size_t max)
{
	char *tmp;
	int length, i;

	if(line == NULL) {
		setCmStrErr(CM_STR_NULL, "getSubString", __LINE__, NULL);
		return NULL;
	}
	if(size < max) {
		length = size;
	} else {
		length = max;
	}
	tmp = (char *)malloc(sizeof(char) * (length + 1));
	if(tmp == NULL) {
		setCmStrErr(CM_STR_ERRNO, "getSubString", __LINE__, strerror(errno));
		return NULL;
	}
	for(i = 0; i < length; i++) {
		if(*(line + i) == '\0') {
			length = i;
			break;
		}
		*(tmp + i) = *(line + i);
	}
	tmp[length] = '\0';
	return tmp;
}


/* The string copy by the limited number of characters */
int truncateString(char *result, char *src, size_t max)
{
	int cnt = 0;

	if((src == NULL) || (result == NULL)) {
		setCmStrErr(CM_STR_NULL, "truncateString", __LINE__, NULL);
		return -1;
	}
	while(cnt < max) {
		result[cnt] = src[cnt];
		if(src[cnt] == '\0') {
			return cnt;
		}
		cnt++;
	}
	result[cnt] = '\0';
	return cnt;
}


/* Trim string */
char *trimString(char *str, size_t max)
{
	int head, last = 0, len, n;
	char *ret, c;

	if(str == NULL) {
		setCmStrErr(CM_STR_NULL, "trimString", __LINE__, NULL);
		return NULL;
	}
	head = skipSpaceTab(str);
	len = strlen(str);
	n = len - 1;
	while(n >= 0) {
		c = *(str + n);
		if((c == ' ') || (c == '\t')) {
			n--;
			last++;
			continue;
		}
		break;
	}
	if(n == -1) {
		ret = getSubString(str + head, len - head, max);
	} else {
		ret = getSubString(str + head, len - head - last, max);
	}
	if(ret == NULL) {
		setCmStrErr(CM_GET_SUB_STRING_FAIL, "trimString", __LINE__, NULL);
		return NULL;
	}
	return ret;
}


/* Extract string */
int extractString(char *result, char *src, char delim, int flg, size_t max)
{
	char c;
	int in_flg = 0;
	size_t gl_idx = 0, lo_idx = 0;

	if((result ==NULL) || (src == NULL)) {
		setCmStrErr(CM_STR_NULL, "extractString", __LINE__, NULL);
		return -1;
	}
	if(max < 1) {
		setCmStrErr(CM_INVALID_NUM, "extractString", __LINE__, NULL);
		return -1;
	}
	while((c = *(src + gl_idx++)) != '\0') {
		if(in_flg == 0) {
			if(c == delim) {
				if(flg == WITH_DELIMITER) {
					*(result + lo_idx++) = c;
				}
				in_flg = 1;
			}
			continue;
		}
		if(c == delim) {
			if(flg == WITH_DELIMITER) {
				*(result + lo_idx++) = c;
			}
			if(lo_idx == max) {
				setCmStrErr(CM_NOT_BUNDLED, "extractString", __LINE__, NULL);
				return -1;
			}
			*(result + lo_idx) = '\0';
			break;
		}
		if((lo_idx == max) || (*(src + gl_idx) == '\0')) {
			setCmStrErr(CM_NOT_BUNDLED, "extractString", __LINE__, NULL);
			return -1;
		}
		if(c == '\\') {
			if(*(src + gl_idx) == delim) {
				c = delim;
				gl_idx++;
			}
		}
		*(result + lo_idx++) = c;
	}
	if(in_flg == 0) {
		*result = '\0';
	}
	return lo_idx;
}


/* Add string(Re-allocate array of character when small array of character) */
char *addString(char *src, char *dest, size_t *dest_size, size_t increment)
{
	int src_len, dest_len = 0;
	int inc, tmpsize;
	char *tmpstr;

	if(src == NULL) {
		setCmStrErr(CM_STR_NULL, "addString", __LINE__, NULL);
		return NULL;
	}
	src_len = strlen(src);
	if(increment > src_len) {
		inc = increment;
	} else {
		inc = src_len;
	}
	if(dest == NULL) {
		tmpstr = reallocChar(NULL, inc);
		if(tmpstr == NULL) {
			setCtlParamErr(CM_STR_ERRNO, "addString", __LINE__, strerror(errno), NULL);
			return NULL;
		}
		strcat(tmpstr, src);
		*dest_size = inc;
		return tmpstr;
	}
	dest_len = strlen(dest);
	if((dest_len + src_len) > *dest_size) {
		tmpsize = *dest_size + inc;
		tmpstr = reallocChar(dest, tmpsize);
		if(tmpstr == NULL) {
			setCtlParamErr(CM_STR_ERRNO, "addString", __LINE__, strerror(errno), NULL);
			return NULL;
		}
		dest = tmpstr;
		*dest_size = tmpsize;
	}
	strcat(dest, src);
	return dest;
}


/* Set error message */
void setCmStrErr(enum cmStrErrCode ecode, char *func, int line, char *msg)
{
	switch(ecode) {
		case CM_STR_ERRNO:
			addErrMSG(__FILE__, func, line, msg);
			break;
		case CM_STR_NULL:
			addErrMSG(__FILE__, func, line, "NULL pointer exception");
			break;
		case CM_STR_REGEX:
			addErrMSG(__FILE__, func, line, msg);
			break;
		case CM_GET_SUB_STRING_FAIL:
			addErrMSG(__FILE__, func, line, "Failed to get sub-string");
			break;
		case CM_INVALID_NUM:
			addErrMSG(__FILE__, func, line, "Invalid number was specified");
			break;
		case CM_NOT_BUNDLED:
			addErrMSG(__FILE__, func, line, "The string isn't bundled by the delimiter");
			break;
		default:
			addErrMSG(__FILE__, func, line, "Unexplained error");
			break;
	}
}


