#include "tds.h"
#include "tdsutil.h"
#include <unistd.h>

static unsigned char seq=0;
int (*g_tds_msg_handler)() = NULL;
int (*g_tds_err_handler)() = NULL;


int tds_process_default_tokens(TDSSOCKET *tds, int marker)
{
int more_results;
int order_len;

	switch(marker) {
		case TDS_ENV_CHG_TOKEN:
			tds_process_env_chg(tds);
			break;
		case TDS_END2_TOKEN:
			more_results=tds_get_byte(tds) & 0x1;
			if (tds->res_info) 
				tds->res_info->more_results=more_results;
			tds_get_n(tds,NULL,7);
			break;
		case TDS_END3_TOKEN:
			more_results=tds_get_byte(tds) & 0x1;
			if (tds->res_info)
				tds->res_info->more_results=more_results;
			tds_get_n(tds,NULL,7);
			break;
		case TDS_124_TOKEN:
			tds_get_n(tds,NULL,8);
			break;
		case TDS_RET_STAT_TOKEN:
			tds_get_n(tds,NULL,4);
			break;
		case TDS_ERR_TOKEN:
			tds_process_error(tds);
			break;
		case TDS_MSG_TOKEN:
		case TDS_MSG50_TOKEN:
			tds_process_msg(tds);
			break;
		case TDS_226_TOKEN:
			tds_get_n(tds,NULL,tds_get_smallint(tds));
			break;
		case TDS_LOGIN_ACK_TOKEN:
			tds_get_n(tds,NULL,tds_get_smallint(tds));
			break;
		case TDS_END_TOKEN:
			more_results=tds_get_byte(tds) & 0x1;
			if (tds->res_info) 
				tds->res_info->more_results=more_results;
			tds_get_n(tds,NULL,7);
			break;
		case TDS_169_TOKEN:
			order_len = tds_get_smallint(tds);
			tds_get_n(tds, NULL, order_len);
			/* get the next token which is ROW_TOKEN (209)
			tds_get_byte(tds);
			if(orderLen > 1)
				tds_process_column_row(tds); */
			break;
		case TDS_174_TOKEN: 
			tds_get_n(tds, NULL, tds_get_smallint(tds));
			break;
		case TDS_RESULT_TOKEN:
			tds_process_result(tds); 
			break;
		case TDS_COL_NAME_TOKEN:
			tds_process_col_name(tds); 
			break;
		case TDS_COL_INFO_TOKEN:
			tds_process_col_info(tds); 
			break;
		case TDS_ROW_TOKEN:
			tds_process_row(tds); 
			break;
		default:
			fprintf(stderr,"Unknown marker: %d!!\n",marker); 
			return 0;
	}	
}	
int tds_process_login_tokens(TDSSOCKET *tds)
{
int succeed=0;
int marker;

	//get_incoming(tds->s);
	do {
		marker=tds_get_byte(tds);
		switch(marker) {
			case TDS_LOGIN_ACK_TOKEN:
				tds_get_n(tds,NULL,tds_get_smallint(tds));
				succeed=1;
				break;
			default:
				tds_process_default_tokens(tds,marker);
				break;
		}
	} while (marker!=TDS_END_TOKEN);
	return succeed;
}
int tds_process_result_tokens(TDSSOCKET *tds)
{
int result=0;
int marker;

	do {
		marker=tds_get_byte(tds);
		switch(marker) {
			case TDS_RESULT_TOKEN:
				if (!result) {
					tds_process_result(tds);
					result++;
				} else {
					tds_unget_byte(tds);
					return TDS_SUCCEED;
				}
				break;
			case TDS_COL_NAME_TOKEN:
				if (!result) {
					tds_process_col_name(tds);
					result++;
				} else {
					tds_unget_byte(tds);
					return TDS_SUCCEED;
				}
				break;
			case TDS_ROW_TOKEN:
				tds->res_info->rows_exist=1;
				tds_unget_byte(tds);
				return TDS_SUCCEED;
			case TDS_END_TOKEN:
			case TDS_END2_TOKEN:
			case TDS_END3_TOKEN:
				tds_process_default_tokens(tds,marker);
				if(tds->res_info)
				{
					if (tds->res_info->more_results)
						break;
					else
						return TDS_NO_MORE_RESULTS;
				}
				else
					return TDS_NO_MORE_RESULTS;
			default:
				tds_process_default_tokens(tds,marker);
				break;
		}
	} while (marker!=TDS_END_TOKEN);
}
int tds_process_row_tokens(TDSSOCKET *tds)
{
int marker;
int aByte;
int aLen, aVal;
 

	while (1) {
		marker=tds_get_byte(tds);
		switch(marker) {
			case TDS_RESULT_TOKEN:
				tds_unget_byte(tds);
				return TDS_NO_MORE_ROWS;
			case TDS_ROW_TOKEN:
				tds_process_row(tds);
				return TDS_SUCCEED;
			case TDS_END_TOKEN:
			case TDS_END2_TOKEN:
			case TDS_END3_TOKEN:
				tds_unget_byte(tds);
				return TDS_NO_MORE_ROWS;
			default:
				tds_process_default_tokens(tds,marker);
				break;
		}
	} 
}
int tds_process_col_name(TDSSOCKET *tds)
{
int hdrsize, len=0;
int i,col,num_cols=0;
int colnamelen;
struct tmp_col_struct {
	char *column_name;
	struct tmp_col_struct *next;
};
struct tmp_col_struct *head=NULL, *cur=NULL, *prev;
TDSCOLINFO *curcol;
TDSRESULTINFO *info;

	hdrsize = tds_get_smallint(tds);

	/* this is a little messy...TDS 5.0 gives the number of columns
	** upfront, while in TDS 4.2, you're expected to figure it out
	** by the size of the message. So, I use a link list to get the
	** colum names and then allocate the result structure, copy
	** and delete the linked list */
	while (len<hdrsize) {
		prev = cur;
		cur = (struct tmp_col_struct *) 
			malloc(sizeof (struct tmp_col_struct));
		if (prev) prev->next=cur;
		if (!head) head = cur;

		colnamelen = tds_get_byte(tds);
		cur->column_name = (char *) malloc(colnamelen+1);
		tds_get_n(tds,cur->column_name, colnamelen);
		cur->column_name[colnamelen]='\0';
		cur->next=NULL;

		len += colnamelen + 1;
		num_cols++;
	}

	if (tds->res_info) tds_free_results(tds->res_info);
	tds->res_info = tds_alloc_results(num_cols);
	info = tds->res_info;
	cur=head;
	for (col=0;col<info->num_cols;col++) 
	{
		curcol=info->columns[col];
		strcpy(curcol->column_name,cur->column_name);
		prev=cur; cur=cur->next;
		free(prev->column_name);
		free(prev);
	}

} 
int tds_process_col_info(TDSSOCKET *tds)
{
int col,hdrsize;
TDSCOLINFO *curcol;
TDSRESULTINFO *info;
int theRest;
int tmpRest;


	hdrsize = tds_get_smallint(tds);

	info = tds->res_info;
	for (col=0;col<info->num_cols;col++) 
	{
		curcol=info->columns[col];
		tds_get_n(tds, NULL, 4);
		curcol->column_type = tds_get_byte(tds);
		if (!is_fixed_type(curcol->column_type)) {
			curcol->column_size = tds_get_byte(tds);
		} else { 
			curcol->column_size = get_size_by_type(curcol->column_type);
		}
	}
	/* get the rest of the bytes */
	tmpRest = hdrsize - (6 * info->num_cols);
	/* theRest = tds_get_byte(tds); */
	if(tmpRest > 0)
		tds_get_n(tds, NULL, tmpRest);

}
int tds_process_result(TDSSOCKET *tds)
{
int hdrsize;
int colnamelen;
int savepos;
int i,col, num_cols;
char column[255];
TDSCOLINFO *curcol;
TDSRESULTINFO *info;

	info = tds->res_info;

	if (info) tds_free_results(info);

	hdrsize = tds_get_smallint(tds);

	savepos = tds->in_pos;
	num_cols = tds_get_smallint(tds);

	tds->res_info = tds_alloc_results(num_cols);
	info = tds->res_info;

	for (col=0;col<info->num_cols;col++) 
	{
		curcol=info->columns[col];
		colnamelen = tds_get_byte(tds);
		tds_get_n(tds,curcol->column_name, colnamelen);
		curcol->column_name[colnamelen]='\0';
		tds_get_byte(tds); /* skip null */
		curcol->column_usertype = tds_get_smallint(tds);
		tds_get_smallint(tds);  /* another unknown */
		curcol->column_type = tds_get_byte(tds);
		if (!is_fixed_type(curcol->column_type)) {
			curcol->column_size = tds_get_byte(tds);
		} else { 
			curcol->column_size = get_size_by_type(curcol->column_type);
		}
		tds_get_byte(tds);
	}
	return 1;  /* SUCCEED */
}
int tds_process_row(TDSSOCKET *tds)
{
int colsize, i;
char tempstr[256];
TDSCOLINFO *curcol;
TDSRESULTINFO *info;
int marker;

	info = tds->res_info;
	info->row_count++;
	for (i=0;i<info->num_cols;i++)
	{
		curcol = info->columns[i];
		if (!is_fixed_type(curcol->column_type)) {
			colsize = tds_get_byte(tds);
		} else {
			colsize = get_size_by_type(curcol->column_type);
		}
		tds_get_n(tds,curcol->column_value,colsize);
		curcol->column_value[colsize]='\0';
		//printf("%s\n",curcol->column_value);
	}
	return TDS_SUCCEED;
}
int tds_submit_query(TDSSOCKET *tds, char *query)
{
unsigned char *buf;
int	bufsize;

#if TDS50
	bufsize = strlen(query)+6;
	buf = (unsigned char *) malloc(bufsize);
	memset(buf,'\0',bufsize);
	buf[0]=33; /* this number stays the same even if I change the query */
	buf[1]=strlen(query)+1; /* length of string + 1 ?! */
	memcpy(&buf[6],query,strlen(query));
	tds->out_flag=0x0F;
#else
	bufsize = strlen(query);
	buf = (unsigned char *) malloc(bufsize);
	memset(buf,'\0',bufsize);
	memcpy(&buf[0],query,strlen(query));
	tds->out_flag=0x01;
#endif
	tds_put_n(tds, buf, bufsize);
	tds_flush_packet(tds);
	
	free(buf);

	//get_incoming(tds->s);
}
int tds_process_env_chg(TDSSOCKET *tds)
{
int size, type;

	size = tds_get_smallint(tds);
	type = tds_get_byte(tds);
	tds_get_n(tds,NULL,tds_get_byte(tds));
	tds_get_n(tds,NULL,tds_get_byte(tds));

	return TDS_SUCCEED;
}

int tds_process_column_row(TDSSOCKET *tds)
{
int colsize, i;
char tempstr[256];
TDSCOLINFO *curcol;
TDSRESULTINFO *info;
int marker;

int aByte;

      info = tds->res_info;
      info->row_count++;
#if 0
                      while(tds->in_pos < tds->in_len)
                      {
                              aByte = tds_get_byte(tds);
                              fprintf(stderr, "[%d]=>%d<=",tds->in_pos, aByte);
                              if (aByte>=' ' && aByte<'z')
                                      fprintf(stderr,"=>%c<=",aByte);
                              fprintf(stderr, "\n");
                      }
#endif
      for (i=0;i<(info->num_cols -1);i++)
      {
              curcol = info->columns[i];
              if (!is_fixed_type(curcol->column_type)) {
                      colsize = tds_get_byte(tds);
              } else {
                      colsize = get_size_by_type(curcol->column_type);
              }
              tds_get_n(tds,curcol->column_value,colsize);
              curcol->column_value[colsize]='\0';
              //printf("%s\n",curcol->column_value);
	}

	/* now skip over some stuff and get the rest of the columns */
	tds_get_n(tds,NULL,25);
	colsize = tds_get_byte(tds);
	tds_get_n(tds,NULL,3);
	curcol = info->columns[i];
	tds_get_n(tds,curcol->column_value, colsize);
	curcol->column_value[colsize]='\0';

	return TDS_SUCCEED;
}
int (*tds_err_handle(TDSSOCKET *tds, int (*handler)() ))
{
      // tds->msg_info->err_fun = *handler;
}

int (*tds_msg_handle(TDSSOCKET *tds, int (*handler)() ))
{
      // tds->msg_info->msg_fun = *handler;
}

int tds_process_msg(TDSSOCKET *tds)
{
int rc;
int len;
int len_msg;
int len_svr;
int msg_type;

	tds->msg_info->priv_msg_type = 0;

	len = tds_get_smallint(tds);
	tds_get_n(tds, &tds->msg_info->msg_number, 4);

	if(!tds->msg_info->msg_number)
	{
		tds_get_n(tds,NULL, (len-4));
		return(1);
	}
	tds->msg_info->msg_state = tds_get_byte(tds);
#if TDS50
	/* junk this info for now */
	msg_type = tds_get_byte(tds);
	tds_get_n(tds,NULL,tds_get_byte(tds));
	tds_get_smallint(tds);
#endif
	tds->msg_info->msg_level = tds_get_byte(tds);

	len_msg = tds_get_smallint(tds);

	tds->msg_info->message = (char*)malloc(len_msg+1);
	tds_get_n(tds, tds->msg_info->message, len_msg);
	tds->msg_info->message[len_msg] = '\0';

	len_svr = tds_get_byte(tds);
	tds->msg_info->server = (char*)malloc(len_svr+1);
	tds_get_n(tds, tds->msg_info->server, len_svr);
	tds->msg_info->server[len_svr] = '\0';

	// junk byte for now, until I can figure out what it is for
	rc = tds_get_byte(tds);
	// line number in the sql statement where the problem occured
	tds->msg_info->line_number = tds_get_byte(tds);
	// junk byte for now, until I can figure out what it is for
	rc = tds_get_byte(tds);

	// call the global_tds_msg_handler that was set by an upper layer (dblib,
	// ctlib or some other one).  Call it with the pointer to the "parent"
	// structure.

	if(tds->parent)
	{
		g_tds_msg_handler(tds->parent);
	} else {
		if(tds->msg_info->msg_number)
			fprintf(stderr, "Msg %d, Level %d, State %d, Server %s,
Line %d\n%s\n",
			tds->msg_info->msg_number,
			tds->msg_info->msg_level,
			tds->msg_info->msg_state,
			tds->msg_info->server,
			tds->msg_info->line_number,
			tds->msg_info->message);

		if(tds->msg_info->message)
			free(tds->msg_info->message);
		if(tds->msg_info->server)
			free(tds->msg_info->server);
		if(tds->msg_info->proc_name)
			free(tds->msg_info->proc_name);
	}

#if 0
      while(tds->in_pos < tds->in_len)
      {
              fprintf(stderr, "[%d]=>%d<=",tds->in_pos,  tds_get_byte(tds));
      }
#endif
}

int tds_process_error(TDSSOCKET *tds)
{
int rc;
int len;
int len_msg;
int len_svr;
int len_proc;
int tmp_len;

      tds->msg_info->priv_msg_type = 1;

      len = tds_get_smallint(tds);
      tds_get_n(tds, &(tds->msg_info->msg_number), 4);

      tds->msg_info->msg_state = tds_get_byte(tds);
      tds->msg_info->msg_level = tds_get_byte(tds);

      len_msg = tds_get_smallint(tds);

      tds->msg_info->message = (char*)malloc(len_msg+1);
      tds_get_n(tds, tds->msg_info->message, len_msg);
      tds->msg_info->message[len_msg] = '\0';

      len_svr = tds_get_byte(tds);
      tds->msg_info->server = (char*)malloc(len_svr+1);
      tds_get_n(tds, tds->msg_info->server, len_svr);
      tds->msg_info->server[len_svr] = '\0';

      len_proc = tds_get_byte(tds);

      if (len_proc > 0)
      {
              /* its the length of a stored procedure name */
              tds->msg_info->proc_name = (char*)malloc(len_proc+1);
              tds_get_n(tds, tds->msg_info->proc_name, len_proc);
              tds->msg_info->proc_name[len_proc] = '\0';

              // get the rest of the bytes
              tmp_len = len - (len_msg+len_svr+len_proc+10);

              if(tmp_len)
                      tds_get_n(tds, NULL, tmp_len);
      }
      else
      {

              // set the procname to null since there isn't one in the stream
              tds->msg_info->proc_name = (char*)NULL;

              // line number in the sql statement where the problem occured
              tds->msg_info->line_number = tds_get_byte(tds);

              // junk byte for now, until I can figure out what it is for
              rc = tds_get_byte(tds);
      }

      // call the global_tds_err_handler that was set by an upper layer (dblib,
      // ctlib or some other one).  Call it with the pointer to the "parent"
      // structure.

      if(tds->parent)
      {
              g_tds_err_handler(tds->parent);
      }
      else
      {
              if(tds->msg_info->msg_number)
                      fprintf(stderr, "Msg %d, Level %d, State %d, Server %s,
Line %d\n%s\n",
                                      tds->msg_info->msg_number,
                                      tds->msg_info->msg_level,
                                      tds->msg_info->msg_state,
                                      tds->msg_info->server,
                                      tds->msg_info->line_number,
                                      tds->msg_info->message);

              if(tds->msg_info->message)
                      free(tds->msg_info->message);
              if(tds->msg_info->server)
                      free(tds->msg_info->server);
              if(tds->msg_info->proc_name)
                      free(tds->msg_info->proc_name);
      }

#if 0
      while(tds->in_pos < tds->in_len)
      {
              fprintf(stderr, "[%d]=>%c<=\n",tds->in_pos,  tds_get_byte(tds));-       }
#endif
}

int tds_reset_msg_info(TDSSOCKET *tds)
{
      tds->msg_info->priv_msg_type = 0;
      tds->msg_info->msg_number = 0;
      tds->msg_info->msg_state = 0;
      tds->msg_info->msg_level = 0;
      tds->msg_info->line_number = 0;

      if( tds->msg_info->message)
              free(tds->msg_info->message);

      if(tds->msg_info->server)
              free(tds->msg_info->server);
      if(tds->msg_info->proc_name)
              free(tds->msg_info->proc_name);
  }

