#include "table.h"
#include <cstdio>

/*      The constructor initialises its members.        */

Table::Table()
{ tab.colNum         = 0;    tab.colName        = "<empty>";
  tab.colType        = 0;    tab.colTypeName    = "<empty>";
  tab.colLength      = 0;    tab.colScale       = 0;
  tab.colNullable    = 0;    tab.binding        = NULL;
  tab.bytesReturned  = 0;    tab.colNameLen     = 0;
  attribs.cRows = attribs.cColumns = 0;
  tableInstances++;
 };

/* This has no practical use */
int Table::tableInstances = 0;

Table::~Table()
{ tableInstances--; };


void Table::initTable(COLATTRIBUTES a)
{  Schema.push_back(a); };


char * Table::getSchema(int c)
{ return (char *) Schema[c].colName.c_str(); };


void Table::setSchema(COLATTRIBUTES a)
{  Schema.push_back(a); };

RESULTATTR Table::getAttribs()
{  return attribs; };

void Table::setAttribs(int r, int c)
{
  attribs.cRows = r;
  attribs.cColumns = c;
};

/*
        bindColumns is used to assign storage for the results of an SQL statement. 
        Each table has a set of column attributes which is stored in the Schema
        vector. Each column is described by the structure COLATTRIBUTES and one
        of the fields of this structure is binding which is a pointer to a piece
        of memory allocated to the data for this column.

        It also (via SQLBindCol) takes care of conversion of the native data to
        chars.
*/

SQLRETURN Table::bindColumns(HSTMT &hstmt)
{ SQLRETURN rc = 0;
  int       i  = 0;

  for (i = 0; i < Schema.size(); i++)
   { Schema[i].binding = new char[Schema[i].colLength + 10];    //allocate storage for data
     memset(Schema[i].binding, 0, sizeof(Schema[i].binding));   //and zero it
     rc = SQLBindCol ( hstmt,
                       Schema[i].colNum,
                       SQL_C_CHAR,
                       Schema[i].binding,
                       Schema[i].colLength,
                       &Schema[i].bytesReturned
                     );
   }
  return rc;
};


/*      buildSchema calls SQLDescribeCol (from the ODBC library) to
        determine the relevant information for each attribute of the
        result set. The schema is stored in the member Schema which
        is a vector of the COLATTRIBUTES structure.

        Minimal error checking is done! and this method should probably
        be private. It is called with a statement handle reference and
        if this points to nowhere in particular - yikes! This would be
        a good place to throw an exception or at least use an assertion.
*/

SQLRETURN Table::buildSchema(HSTMT &hstmt, int numCols)
{ int         i;
  SQLCHAR     colName[32];
  SWORD       colNamelen, colType, scale, nullable;
  SQLUINTEGER colLen;
  SQLRETURN   rc;
  COLATTRIBUTES a;

  for (i = 0; i < numCols; i++)
   { rc = SQLDescribeCol( hstmt, i + 1, colName, 32, &colNamelen,
                          &colType, &colLen, &scale, &nullable);
     a.colNum      = i + 1;
     a.colName     = (char *) colName;
     a.colType     = colType;
     a.colLength   = max(colLen + 4, strlen((char *)colName));
     a.colNullable = nullable;
     a.colScale    = scale;
     a.colNameLen  = colNamelen;
     switch (colType)
      { case SQL_VARCHAR:  a.colTypeName = "VARCHAR";  break;
        case SQL_CHAR:     a.colTypeName = "CHAR";     break;
        case SQL_FLOAT:    a.colTypeName = "FLOAT";    break;
        case SQL_DOUBLE:   a.colTypeName = "DOUBLE";   break;
        case SQL_NUMERIC:  a.colTypeName = "NUMERIC";  break;
        case SQL_REAL:     a.colTypeName = "REAL";     break;
        case SQL_DECIMAL:  a.colTypeName = "DECIMAL";  break;
        case SQL_INTEGER:  a.colTypeName = "INTEGER";  break;   //Don't forget to
        case SQL_SMALLINT: a.colTypeName = "SMALLINT"; break;   //add other data types
        default:           a.colTypeName = "UNKNOWN";  break;
      }
     a.bytesReturned = 0;
     Schema.push_back(a);
   }
};


/*      Get the next row from the raw result hstmt and move it to an accessible
        storage. It is called with a statement handle reference and uses the
        tData member to store the data; tData is a vector of the structure
        tableData. At the moment this is just plain text, ie everything, ie the
        column data, is converted to text (see bindColumns) above. It might be
        better to use the native data type returned by the execution of a
        select statement but at the moment this isn't urgent. It should be a
        trivial matter to convert data back to some type in the user application.
*/

SQLRETURN Table::transferData(HSTMT & hstmt)
{ SQLRETURN rc = 0;
  int       i = 0;

  while ( (rc = SQLFetch(hstmt)) != SQL_ERROR )
   { if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
      { for (i = 0; i < Schema.size(); i++)
         { rc = SQLGetData( hstmt,
                            Schema[i].colNum,
                            SQL_C_CHAR,
                            Schema[i].binding,
                            Schema[i].colLength,
                            &Schema[i].bytesReturned
                          );

           tableData * d;
           d = new tableData;
           d->text = new char [Schema[i].colLength];
           strcpy(d->text, Schema[i].binding);
           tData.push_back(*d);
           delete d;
         }
      }
     else //Something is wrong
       { return rc;
       }
   }
  return rc;
};


char * Table::getField(int row, int col, int numCols)
{   return tData[(row * numCols) + col].text; };