#include "resultset.h"

Variant::Variant(){data=0;}
Variant::Variant(const char* datap){
  if(datap!=0)
    {
      data=new char[strlen(datap)+1];
      strcpy(data,datap);
    }
  else data=0;
}

Variant::Variant(const Variant& other){
  if(other.data!=0)
    {
      data=new char[strlen(other.data)+1];
      strcpy(data,other.data);
    }
  else data=0;
}

Variant::~Variant(){
  if(data!=0)
    delete[] data;
}

Variant Variant::operator = (const Variant& other){
  if(data!=0)delete[] data;
  if(other.data!=0)
    {
      data=new char[strlen(other.data)+1];
      strcpy(data,other.data);
    }
  return *this;
}
Variant::operator string(){
  if(data==0)return string("");
  return string(data);
}
Variant::operator double(){
  if(data==0)return 0.0;
  return (double)atof(data);
}

ostream& operator << (ostream& stream,Variant v){
  stream<<(string)v;
  return stream;
}

double abs(double d){
  if(d>=0)return d;
  else return d*-1;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

ResultSet::ResultSet(){
  conn=0;
  numrows=0;
  currentrow=-1;
}

ResultSet::ResultSet(MYSQL* conn_p){
  ResultSet();
  conn=conn_p;
  numrows=0;
  currentrow=-1;
}

ResultSet::ResultSet(string q,MYSQL* conn_p){
  ResultSet();
  conn=conn_p;
  numrows=0;
  currentrow=-1;
  query.str(q);
  store();
}

ResultSet::ResultSet(const ResultSet& other){
  conn=other.conn;
  numrows=other.numrows;
  currentrow=other.currentrow;
  result=other.result;
  row=other.row;
  names=other.names;
  fields=other.fields;
  thisquery=other.thisquery;
}

ResultSet ResultSet::operator = (const ResultSet& other){
  conn=other.conn;
  numrows=other.numrows;
  currentrow=other.currentrow;
  result=other.result;
  row=other.row;
  names=other.names;
  fields=other.fields;
  thisquery=other.thisquery;
}

ResultSet::~ResultSet(){}

MYSQL ResultSet::makeConn(string host,string user,string pass,string db){
  MYSQL conn;mysql_init(&conn); // Make and init the connection object
  if(!mysql_real_connect(&conn,host.c_str(),user.c_str(),pass.c_str(),db.c_str(),0,"",0))
    {
      fprintf(stderr, "Failed to connect to database: Error: %s\n",mysql_error(&conn));
#ifdef EXIT_ON_ERROR
      exit(1);
#endif
    }
  return conn;
}

void ResultSet::setConn(MYSQL* newconn){
  conn=newconn;
}

MYSQL* ResultSet::getConn() const{
  return conn;
}

void ResultSet::store(){
  storeQuery(query.str());
  clear();
}

void ResultSet::storeNoRes(){ // This is deprecated, as store() now deals with non-selects properly
  storeQuery(query.str());
}

void ResultSet::clear(){
  query.str("");
}

string ResultSet::print(){
  return query.str();
}

void ResultSet::storeQuery(string q){
  thisquery=q;
  names.clear();fields.clear();
  currentrow=-1;
  mysql_query(conn,q.c_str());
  if(mysql_error(conn)[0]!='\0')
    {
      cout<<"Failure to execute: \n\t";
      cout<<mysql_error(conn)<<'\n';
      cout<<"\n\tOn query: \""<<q<<"\"\n";
#ifdef EXIT_ON_ERROR
      exit(1);
#endif
      return;
    }
  int numfields=mysql_field_count(conn);
  if(numfields!=0)
    {
      result=*mysql_store_result(conn);
      numrows=mysql_num_rows(&result);
      
      for(int n=0;n!=numfields;n++)
	{
	  MYSQL_FIELD* mf=mysql_fetch_field(&result);
	  names.push_back(mf->name);
	}
    }
}

int ResultSet::tell() const{return currentrow;}

int ResultSet::size() const{return numrows;}

bool ResultSet::next(){
  if(numrows==0)
    {
      cout<<"No rows in query \""<<thisquery<<"\n";
#ifdef EXIT_ON_ERROR
      exit(1);
#endif
      return false;
    }
  if(currentrow>=numrows-1)return false;
  row=mysql_fetch_row(&result);
  currentrow++;
  return true;
}

bool ResultSet::prev(){
  if(currentrow<=0)return false;
  mysql_data_seek(&result,--currentrow);
  row=mysql_fetch_row(&result);
  return true;
}

bool ResultSet::seek(int idx){
  if(idx<0 || idx>=numrows)
    {
      cout<<"Seek to row "<<idx<<" failed in query \""<<thisquery<<"\"\n";
#ifdef EXIT_ON_ERROR
      exit(1);
#endif
      return false;
    }
  currentrow=idx;
  mysql_data_seek(&result,currentrow);
  row=mysql_fetch_row(&result);  
  return true;
}

void ResultSet::rewind(){
  seek(0);
}

Variant ResultSet::get(int idx) const{
  if(tell()<0 || tell()>=numrows)
    {
      cout<<"Row "<<tell()<<" out of bounds in query \""<<thisquery<<"\"\n";
#ifdef EXIT_ON_ERROR
      exit(1);
#endif
      return Variant();
    }
  if(idx<0 || idx>=names.size())
    {
      cout<<"Field "<<idx<<" out of bounds in query \""<<thisquery<<"\"\n";
#ifdef EXIT_ON_ERROR
      exit(1);   
#endif
      return Variant();
    }

  char* val=(char*)row[idx];
  if(val==0) // Don't let it return a null pointer
    {
#ifdef ERROR_ON_NULL
      cout<<"Field "<<idx<<" is NULL at row "<<tell()<<" in \""<<thisquery<<"\"\n"<<flush; // FIXME: Should this flush be here?
#ifdef EXIT_ON_ERROR
      exit(1);
#endif
#endif
      return Variant();
    }
  Variant v(val);
  return v;
}

Variant ResultSet::get(string name) const{
  int idx=fieldNumber(name);
  if(idx!=-1)return get(idx);
  else return Variant();
}

Variant ResultSet::operator [] (int idx) const{
  return get(idx);
}

Variant ResultSet::operator [] (string name) const{
  return get(name);
}

int ResultSet::fieldNumber(string name) const{
  for(int n=0;n!=names.size();n++)
    if(name==names[n])return n;
  cout<<"No field \""<<name<<"\" in \""<<thisquery<<"\"\n";
#ifdef EXIT_ON_ERROR
  exit(1);
#endif
  return -1;
}
