#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>

#ifdef HAVE_SSL
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#endif

#include "sbuf.h"
#include "struct.h"
#include "send.h"


int send_message(struct lsock *cptr, char *message, int length)
{
	sbuf_put(&cptr->sendq, message, length);
	return 0;
}

#define to_digit(ch) ((ch) - '0')
#define to_char(num) ((num) + '0')
#define is_digit(ch) ((unsigned)to_digit(ch) <= '9')


#define zeropad 1
#define altfmt 2
// #define numprefix 4

#define flagshortint 8
#define flaglongint 16
#define flagquadint 32

#define leftjust 64
#define centjust 128

/* only accessible through S and c */
#define flagcap 256

const char numupper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char numlower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
int tvprintf(struct lsock *cptr, const char *format, va_list ap)
{
	char *fmt;
	char *cp;
	char ch;
	char *src;
	char *eos;
	char *dest;
	char *eod;
	int tlen;
	int collen;
	int num;
/* writeout settings */
	int width; /* width (ie. %3d) */
	int prec;  /* precision (ie. %.4d) */
	unsigned long flags;
	char sign;
	const char *numfmt;
/* aftercalc settings */
	int lpad;
	int rpad;
	int calcsz;
/* buffers */
	int base;
	unsigned long ulval;
	char preout[512];
	char buf[68]; /* buf space for %c %[oubxX...] etc */

	fmt = (char *)format;
	dest = preout;
	eod = dest + sizeof(preout);
	
	tlen = 0;
	collen = 0;
	for(;;)
	{
		/* reset settings */
		flags = 0;
		width = 0;
		prec = -1;
		sign = '\0';
		numfmt = numlower;
		/* scan for a format, or end of string */
		for(cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++);
		if(fmt - cp > 0)
		{
			src = cp;
			eos = fmt;
			goto writeout;
		}
		if(*fmt == '\0')
			break; /* done processing */
		fmt++; /* skip the '%' */
		
getnchar:
		ch = *fmt++;
reschar:
		switch(ch)
		{
			case ' ':
				if(!sign)
					sign = ' ';
				goto getnchar;
			case '?':
				if( va_arg(ap, int) )
					flags |= altfmt;
				goto getnchar;
			case '#':
				flags |= altfmt;
				goto getnchar;
			case '*':
				/* XXX */
				goto getnchar;
			case '-':
				if(flags & leftjust)
					flags |= centjust;
				else
					flags |= leftjust;
				goto getnchar;
			case '+':
				sign = '+';
				goto getnchar;
			case '.':
				ch = *fmt++;
				if(ch == '*')
				{
					/* XXX */
					goto getnchar;
				}
				num = 0;
				while(is_digit(ch))
				{
					num *= 10;
					num += to_digit(ch);
					ch = *fmt++;
				}
				prec = num < 0 ? -1 : num;
				goto reschar;
				break;
			case '0':
				flags |= zeropad;
				goto getnchar;
				break;
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				num = 0;
				do
				{
					num *= 10;
					num += to_digit(ch);
					ch = *fmt++;
				} while( is_digit(ch) );
				
				width = num;
				goto reschar;
			case 'h':
				flags |= flagshortint;
				goto getnchar;
			case 'l':
				if(flags & flaglongint)
					flags |= flagquadint;
				else
					flags |= flaglongint;
				
				goto getnchar;
			case 'q':
				flags |= flagquadint;
				goto getnchar;
			case 'c':
				if(flags & altfmt)
					flags |= flagcap;
				sign = '\0';
				src = buf;
				eos = src + 1;
				*src = (char)va_arg(ap, int);
				goto writeout;
			case 's':
				src = va_arg(ap, char *);
				if(src == NULL)
					src = "(null)";
				if(flags & altfmt)
					flags |= flagcap;
				if(prec < 0)
					for(eos = src; *eos; eos++);
				else
				{
					char *eob;
					eob = src + prec;
					for(eos = src; eos < eob && *eos; eos++);
				}
				goto writeout;
			case 'X':
				numfmt = numupper;
				base = 16;
				goto nosign;
			case 'x':
				numfmt = numlower;
				base = 16;
				goto nosign;
			case 'o':
				base = 8;
				goto nosign;
			case 'b':
				base = 2;
				goto nosign;
			case 'u':
				base = 10;
				goto nosign;
nosign:
				ulval = flags & flaglongint ? va_arg(ap, long)
				: flags & flagshortint ? (long)(short)va_arg(ap,int)
				: (long)va_arg(ap,int) ;
				
				goto number;
			case 'p':
				base = 16;
				ulval = (unsigned long)(void *)va_arg(ap, void *);
				
				goto number;
			case 'D':
				flags |= flaglongint;
			case 'd':
			case 'i':
				ulval = flags & flaglongint ? va_arg(ap, long)
				: flags & flagshortint ? (long)(short)va_arg(ap,int)
				: (long)va_arg(ap,int) ;
				
				if((long)ulval < 0)
				{
					ulval = -ulval;
					sign = '-';
				}
				base = 10;
				goto number;
number:
				if(prec >= 0)
					flags &= ~zeropad;
				
				eos = buf + sizeof(buf);
				src = eos;
				if(base <= 10)
				{
					int dprec = prec;
					
					if(dprec == 0)
						goto writeout;
						
					if(ulval == 0)
					{
						*(--src) = '0';
						goto writeout;
					}
					for(;ulval > 0 && dprec; ulval /= base, dprec--)
						*(--src) = to_char( ulval % base );
					if(flags & altfmt)
					{
						*(--src) = ch;
						*(--src) = '0';
					}
					goto writeout;
				}
				else
				{
					int dprec = prec;
					
					if(dprec == 0)
						goto writeout;
						
					if(ulval == 0)
					{
						*(--src) = '0';
						goto writeout;
					}
					for(;ulval > 0 && dprec; ulval /= base, dprec--)
						*(--src) = numfmt[ ulval % base];
					if(flags & altfmt)
					{
						*(--src) = ch;
						*(--src) = '0';
					}
					goto writeout;
				}
				
				continue;
			case 'z':
				src = eos = buf;
				width -= collen;
				if(width < 0)
					continue; /* nothing to do */
				flags &= ~(centjust);
				flags |= leftjust;
			
				goto writeout;
			default:
				if(!ch)
					goto done;
				sign = '\0';
				src = buf;
				eos = src + 1;
				*src = ch;
				goto writeout;
		}

writeout:
		calcsz = eos - src;
		if(sign)
			calcsz++;
		lpad = rpad = 0;
		if(width > calcsz)
		{
			if(flags & centjust)
			{
				rpad = width - calcsz;
				lpad = rpad / 2;
				rpad -= lpad;
			}
			else if(flags & leftjust)
				rpad = width - calcsz;
			else
				lpad = width - calcsz;
		}


		if((flags & (leftjust|centjust)) && !(flags & zeropad))
		{
			for(;lpad;lpad--)
			{
				if(dest >= eod)
				{
					send_message(cptr, preout, dest - preout);
					dest = preout;
				}
				collen++;
				tlen++;
				*dest++ = ' ';
			}
		}
		if(sign)
		{
			if(dest >= eod)
			{
				send_message(cptr, preout, dest - preout);
				dest = preout;
			}
			collen++;
			tlen++;
			*dest++ = sign;
		}

		if(flags & zeropad)
		{
			for(;lpad;lpad--)
			{
				if(dest >= eod)
				{
					send_message(cptr, preout, dest - preout);
					dest = preout;
				}
				collen++;
				tlen++;
				*dest++ = '0';
			}
		}

		if(flags & flagcap)
		{
			if(src < eos)
			{
				if(dest >= eod)
				{
					send_message(cptr, preout, dest - preout);
					dest = preout;
				}
				collen++;
				tlen++;
				*dest++ = toupper(*src++);
			}
		}
		while(src < eos)
		{
			if(dest >= eod)
			{
				send_message(cptr, preout, dest - preout);
				dest = preout;
			}
			if(*src == '\r' || *src == '\n')
				collen = 0;
			else if(*src >= ' ')
				collen++;
			tlen++;
			*dest++ = *src++;
		}

		for(;rpad;rpad--)
		{
			if(dest >= eod)
			{
				send_message(cptr, preout, dest - preout);
				dest = preout;
			}
			collen++;
			tlen++;
			*dest++ = ' ';
		}
	}
done:		
	if(dest > preout)
	{
		send_message(cptr, preout, dest - preout);
	}
	
	
	
	return tlen;
}

int tprintf(struct lsock *cptr, const char *format, ...)
{
	va_list ap;
	int res;
	va_start(ap, format);
	res = tvprintf(cptr, format, ap);
	va_end(ap);
	return res;
}


