/* Copyright 2013 Akira Ohta (akohta001@gmail.com)
    This file is part of ntch.

    The ntch 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 3 of the License, or
    (at your option) any later version.

    The ntch 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 ntch.  If not, see <http://www.gnu.org/licenses/>.
    
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <wchar.h>

#include "utils/nt_std_t.h"
#include "utils/text.h"
#include "_2ch/parse_2ch.h"

static nt_link_tp parse_number_list_local (const char *source, 
				const char **endpp);
static nt_link_tp w_parse_number_list_local (const wchar_t *source, 
				const wchar_t **endpp);

int nt_comp_int(int a, int b)
{
	if(a == b) return 0;
	else if(a < b) return 1;
	else return -1;
}

nt_link_tp nt_parse_number_list (const char *source, const char **endpp)
{
	nt_link_tp linkp =
		parse_number_list_local(source, endpp);
	if(!linkp)
		return NULL;
	nt_link_n_sort(&linkp, nt_comp_int);
	return linkp;
}

nt_link_tp nt_w_parse_number_list (const wchar_t *source, const wchar_t **endpp)
{
	nt_link_tp linkp =
		w_parse_number_list_local(source, endpp);
	if(!linkp)
		return NULL;
	nt_link_n_sort(&linkp, nt_comp_int);
	return linkp;
}

wchar_t* nt_w_format_number_list (nt_link_tp num_listp)
{
	const int delta = 32;
	wchar_t *buf;
	int buf_size, len;
	nt_link_tp linkp;
	int prev_num, s_num, num, ret;
	
	assert(num_listp);
	
	buf = nt_w_str_resize(NULL, 0, delta);
	if(!buf)
		return NULL;
	buf_size = delta;
	len = 0;
	s_num = -1;
	prev_num = -1;
	linkp = num_listp;
	do{
		num = linkp->n_data;
		if(num <= 0){
			break;
		}else if(num == prev_num){
			;
		}else if(num == prev_num+1){
			if(s_num < 0)
				s_num = prev_num;
		}else if(prev_num != -1){
			if(s_num < 0){
				if(len == 0)
					ret = swprintf(buf, buf_size, L"%d", prev_num);
				else
					ret = swprintf(buf+len, buf_size-len, L",%d", prev_num);
			}else{
				if(len == 0)
					ret = swprintf(buf, buf_size, 
								L"%d-%d", s_num, prev_num);
				else
					ret = swprintf(buf+len, buf_size-len, 
								L",%d-%d", s_num, prev_num);
				s_num = -1;
			}
			if(ret < 0){
				free(buf);
				return NULL;
			}
			len += ret;
			if(len+1 >= buf_size-12){
				buf = nt_w_str_resize(buf, buf_size, buf_size + delta);
				if(!buf)
					return NULL;
				buf_size += delta;
			}
		}
		prev_num = num;
		linkp = linkp->next;
	}while(linkp != num_listp);
	if(prev_num != -1){
		if(s_num < 0){
			if(len == 0)
				ret = swprintf(buf, buf_size, L"%d", prev_num);
			else
				ret = swprintf(buf+len, buf_size-len, L",%d", prev_num);
		}else{
			if(len == 0)
				ret = swprintf(buf, buf_size, 
							L"%d-%d", s_num, prev_num);
			else
				ret = swprintf(buf+len, buf_size-len, 
							L",%d-%d", s_num, prev_num);
		}
		if(ret < 0){
			free(buf);
			return NULL;
		}
	}
	return buf;
}


static nt_link_tp parse_number_list_local (const char *source, 
				const char **endpp)
{
	nt_link_tp linkp, top_linkp;
	int i , len, state;
	const char *start;
	int n, prev;

	assert(source);

	if(endpp)
		*endpp = source;
	
	top_linkp = NULL;
	linkp = NULL;
	len = strlen(source);
	state = 0;
	prev = 0;

	for(i = 0; i < len; i++){
		switch(source[i]){
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			if(state == 0){
				state = 1;
				start = source + i;
			}else if(state == 2){
				state = 3;
				start = source + i;
			}else if(state != 1 && state != 3){
				assert(0);
			}
			break;
		case ' ':
		case ',':
			if(state == 1){
				n = atoi(start);
				linkp = nt_link_add_n_data(top_linkp, n);
				if(!top_linkp)
					top_linkp= linkp;
				if(endpp)
					*endpp = source + i;
			}else if(state == 3){
				n = atoi(start);
				if(prev > n){
					linkp = nt_link_add_n_data(top_linkp, prev);
					if(!top_linkp)
						top_linkp= linkp;
					return top_linkp;
				}
				while(prev <= n){
					linkp = nt_link_add_n_data(top_linkp, prev);
					if(!top_linkp)
						top_linkp = linkp;
					prev++;
				}
				if(endpp)
					*endpp = source + i;
			}else if(state == 2){
				linkp = nt_link_add_n_data(top_linkp, prev);
				if(!top_linkp)
					top_linkp = linkp;
				return top_linkp;
			}else{
				return top_linkp;
			}
			state = 0;
			break;
		case '-':
			if(state == 1){
				prev = atoi(start);
				if(endpp)
					*endpp = source + i;
				state = 2;
				break;
			}
			/* fall through */
		default:
			goto END_FOR;
		}/* end switch */
	}/* end for */
END_FOR:
	if(state == 1){
		n = atoi(start);
		linkp = nt_link_add_n_data(top_linkp, n);
		if(!top_linkp)
			top_linkp = linkp;
		if(endpp)
			*endpp = source + i;
	}else if(state == 3){
		n = atoi(start);
		if(prev > n){
			linkp = nt_link_add_n_data(top_linkp, prev);
			if(!top_linkp)
				top_linkp = linkp;
			return top_linkp;
		}
		while(prev <= n){
			linkp = nt_link_add_n_data(top_linkp, prev);
			if(!top_linkp)
				top_linkp = linkp;
			prev++;
		}
	}
	return top_linkp;
}

static nt_link_tp w_parse_number_list_local (const wchar_t *source, 
				const wchar_t **endpp)
{
	nt_link_tp linkp, top_linkp;
	int i , len, state;
	const wchar_t *start;
	int n, prev;

	assert(source);

	if(endpp)
		*endpp = source;
	
	top_linkp = NULL;
	linkp = NULL;
	len = wcslen(source);
	state = 0;
	start = source;
	prev = 0;

	for(i = 0; i < len; i++){
		switch(source[i]){
		case L'0':
		case L'1':
		case L'2':
		case L'3':
		case L'4':
		case L'5':
		case L'6':
		case L'7':
		case L'8':
		case L'9':
			if(state == 0){
				state = 1;
				start = source + i;
			}else if(state == 2){
				state = 3;
				start = source + i;
			}else if(state != 1 && state != 3){
				assert(0);
			}
			break;
		case L' ':
		case L',':
			if(state == 1){
				n = (int)wcstol(start, NULL, 10);
				linkp = nt_link_add_n_data(top_linkp, n);
				if(!top_linkp)
					top_linkp= linkp;
				if(endpp)
					*endpp = source + i;
			}else if(state == 3){
				n = (int)wcstol(start, NULL, 10);
				if(prev > n){
					linkp = nt_link_add_n_data(top_linkp, prev);
					if(!top_linkp)
						top_linkp= linkp;
					return top_linkp;
				}
				while(prev <= n){
					linkp = nt_link_add_n_data(top_linkp, prev);
					if(!top_linkp)
						top_linkp = linkp;
					prev++;
				}
				if(endpp)
					*endpp = source + i;
			}else if(state == 2){
				linkp = nt_link_add_n_data(top_linkp, prev);
				if(!top_linkp)
					top_linkp = linkp;
				return top_linkp;
			}else{
				return top_linkp;
			}
			state = 0;
			break;
		case L'-':
			if(state == 1){
				prev = (int)wcstol(start, NULL, 10);
				if(endpp)
					*endpp = source + i;
				state = 2;
				break;
			}
			/* fall through */
		default:
			goto END_FOR;
		}/* end switch */
	}/* end for */
END_FOR:
	if(state == 1){
		n = (int)wcstol(start, NULL, 10);
		linkp = nt_link_add_n_data(top_linkp, n);
		if(!top_linkp)
			top_linkp = linkp;
		if(endpp)
			*endpp = source + i;
	}else if(state == 3){
		n = (int)wcstol(start, NULL, 10);
		if(prev > n){
			linkp = nt_link_add_n_data(top_linkp, prev);
			if(!top_linkp)
				top_linkp = linkp;
			return top_linkp;
		}
		while(prev <= n){
			linkp = nt_link_add_n_data(top_linkp, prev);
			if(!top_linkp)
				top_linkp = linkp;
			prev++;
		}
	}
	return top_linkp;
}

wchar_t*  nt_parse_res_msg(const wchar_t *srcp, nt_link_tp *num_linkpp)
{
	int i, len;
	wchar_t ch;
	wchar_t *buf, *cptr, *wrk_buf;
	int offset;
	int buf_idx, buf_size;
	nt_link_tp wrk_linkp;
	
	assert(num_linkpp);
	
	len = wcslen(srcp);
	const int delta = 20;

	if(len == 0)
		return NULL;
	buf_size = len + delta;
	buf = malloc(sizeof(wchar_t) * (buf_size));
	if(!buf)
		return NULL;
	buf_idx = 0;

	for(i = 0; i < len; i++){
		ch = srcp[i];
		switch(ch){
		case L'&':
			if(len > (i+5)){
				if(srcp[i+1] == L'q' &&
						srcp[i+2] == L'u' &&
						srcp[i+3] == L'o' &&
						srcp[i+4] == L't' &&
						srcp[i+5] == L';'){
					buf[buf_idx] =L'\"' ;
					buf_idx++;
					i += 5;
				}
			}
			if(len > (i+4)){
				if(srcp[i+1] == L'a' &&
						srcp[i+2] == L'm' &&
						srcp[i+3] == L'p' &&
						srcp[i+4] == L';'){
					buf[buf_idx] = L'&';
					buf_idx++;
					i += 4;
				}
			}
			if(len > (i+3)){
				if(srcp[i+1] == L'g' &&
						srcp[i+2] == L't' &&
						srcp[i+3] == L';'){
					buf[buf_idx] = L'>';
					buf_idx++;
					i += 3;
					wrk_linkp = w_parse_number_list_local(srcp+i+1, NULL);
					if(wrk_linkp){
						*num_linkpp = nt_link_add_last(*num_linkpp, wrk_linkp);
					}
				}else if(srcp[i+1] == L'l' &&
						srcp[i+2] == L't' &&
						srcp[i+3] == L';'){
					buf[buf_idx] = L'<';
					buf_idx++;
					i += 3;
				}
			}
			break;
		case L'<':
			if(len > (i+3) && srcp[i+1] == L'b' &&
					srcp[i+2] == L'r' &&
					srcp[i+3] == L'>'){
				buf[buf_idx] = L'\n';
				buf_idx++;
				i += 3;
			}else if(len > (i+7) && srcp[i+1] == L'a' &&
					srcp[i+2] == L' ' &&
					srcp[i+3] == L'h' &&
					srcp[i+4] == L'r' &&
					srcp[i+5] == L'e' &&
					srcp[i+6] == L'f' &&
					srcp[i+7] == L'='){
				cptr = wcschr(srcp+i+8, L'>');
				if(cptr){
					offset = cptr - &(srcp[i]);
					i += offset;
				}
			}else if(len > (i+3) && srcp[i+1] == L'/' &&
					srcp[i+2] == L'a' &&
					srcp[i+3] == L'>'){
				i += 3;
			}
			break;
	/*	case L'>':
			wrk_linkp = w_parse_number_list_local(srcp+i, NULL);
			if(wrk_linkp){
				*num_linkpp = nt_link_add_last(*num_linkpp, wrk_linkp);
			}
			goto DEF_LABEL; */
		case L't':
			if(len > (i+5) && (i == 0 || (i > 0 && 
				srcp[i-1] != L'h' && srcp[i-1] != L'H')) &&
					srcp[i+1] == L't' &&
					srcp[i+2] == L'p' &&
					srcp[i+3] == L':' &&
					srcp[i+4] == L'/' &&
					srcp[i+5] == L'/'){
				buf[buf_idx] = L'h';
				buf_idx++;
			}
			/* fall through */
		default:
//DEF_LABEL:
			buf[buf_idx] = ch;
			buf_idx++;
			break;
		}/* end switch */
		if(buf_idx >= (buf_size-1)){
			wrk_buf = nt_w_str_resize(buf, buf_size, (buf_size + delta));
			if(!wrk_buf){
				free(buf);
				return NULL;
			}
			buf = wrk_buf;
			buf_size += delta;
		}
	} /* end for */
	buf[buf_idx] = L'\0';
	return buf;
}
