/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set et sw=2 ts=2: */
/***************************************************************************
 *            pracrodaotest.cc
 *
 *  Fri Aug  7 10:25:07 CEST 2009
 *  Copyright 2009 Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of Pracro.
 *
 *  Pracro 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  Pracro 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 Pracro; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "pracrodaotest.h"

#include <stdlib.h>
#include <sstream>

#include "debug.h"

PracroDAOTest::PracroDAOTest(Data &data, bool ignore_fieldnames)
  : PracroDAO("", "", "", "", "")
{
  this->data = data;
  this->ignore_fieldnames = ignore_fieldnames;
  DEBUG(db, "New test (memory only) database\n");
}

PracroDAOTest::~PracroDAOTest()
{
  DEBUG(db, "Delete test (memory only) database\n");
}

void PracroDAOTest::commitTransaction(std::string user, std::string patientid,
                                      Macro &_macro, Fields &fields, time_t now)
{
  DEBUG(db, "(%s, %s, %s, <%u fields>, %ld)\n",
        user.c_str(),
        patientid.c_str(),
        _macro.attributes["name"].c_str(),
        fields.size(),
        now);
  if(fields.size() == 0) return;

  std::string version = _macro.attributes["version"];
  std::string macro = _macro.attributes["name"];
  std::stringstream timestamp; timestamp << now;

  dbrow_t t;
  t["uid"] = data.trseq.nextval();
  t["patientid"] = patientid;
  t["macro"] = macro;
  t["version"] = version;
  t["timestamp"] = timestamp.str();
  t["user"] = user;
  data.transactions.push_back(t);

  // Iterate fields...
  Fields::iterator fi = fields.begin();
  while(fi != fields.end()) {
    
    if(ignore_fieldnames == false) {
      // Search for it in fieldnames table
      dbtable_t::iterator ti = data.fieldnames.begin();
      while(ti != data.fieldnames.end()) {
        
        // If found, insert the field values into the fields table.
        if(fi->first == (*ti)["name"]) {
          
          dbrow_t f;
          f["transaction"] = data.trseq.currval();
          f["name"] = fi->first;
          f["value"] = fi->second;
          data.fields.push_back(f);
          
        }
        
        ti++;
      }
    } else {
      dbrow_t f;
      f["transaction"] = data.trseq.currval();
      f["name"] = fi->first;
      f["value"] = fi->second;
      data.fields.push_back(f);
    }
    
    fi++;
  }
}

Values PracroDAOTest::getLatestValues(std::string patientid, Macro *macro,
                                      Fieldnames &fieldnames, time_t oldest)
{
  std::string macro_name = macro ? macro->attributes["name"].c_str() : "(null)";
  DEBUG(db, "(%s, %s, <%u fieldnames>, %ld)\n",
        patientid.c_str(),
        macro_name.c_str(), fieldnames.size(),
        oldest);
  Values values;

  // TODO: Take Macro* into account. If supplied (not NULL) the macro name, and
  //  optionally version number should match the transaction.

  Fieldnames::iterator fi = fieldnames.begin();
  while(fi != fieldnames.end()) {
    std::string fieldname = *fi;

    // Find matching transactions
    dbtable_t::iterator ti = data.transactions.begin();
    while(ti != data.transactions.end()) {
      std::map<std::string, std::string> &transaction = *ti;
      time_t timestamp = atol(transaction["timestamp"].c_str());
      if(transaction["patientid"] == patientid && timestamp >= oldest && (transaction["macro"] == macro_name || macro == NULL)) {
        std::string tid = transaction["uid"];

        // Find transaction values
        dbtable_t::iterator vi = data.fields.begin();
        while(vi != data.fields.end()) {
          std::map<std::string, std::string> &field = *vi;
          
          // Upon match, insert it into values
          if(field["transaction"] == tid && field["name"] == fieldname) {
            if(values.find(fieldname) == values.end() || values[fieldname].timestamp < timestamp) {
              values[fieldname].timestamp = timestamp;
              values[fieldname].value = field["value"];
              values[fieldname].source = "testdb";
            }
          }
          vi++;
        }
    
      }
      ti++;
    }
    
    fi++;
  }

  return values;
}


unsigned PracroDAOTest::nrOfCommits(std::string patientid, std::string macroname, time_t oldest)
{
  unsigned num = 0;

  // Find and count matching transactions
  dbtable_t::iterator ti = data.transactions.begin();
  while(ti != data.transactions.end()) {
    std::map<std::string, std::string> &transaction = *ti;
    time_t timestamp = atol(transaction["timestamp"].c_str());
    if(transaction["patientid"] == patientid &&
       transaction["macro"] == macroname &&
       timestamp >= oldest) {
      num++;
    }
    ti++;
  }

  return num;
}

void PracroDAOTest::addFieldname(std::string name, std::string description)
{
  // TODO
  
  /*
  std::stringstream timestamp; timestamp << time(NULL);
  std::string ts;
  try {
    pqxx::work W(*conn);
    ts = "INSERT INTO fieldnames (name, description, \"timestamp\") VALUES ("
      " '" + W.esc(name) + "', "
      " '" + W.esc(description) + "', "
      " '" + W.esc(timestamp.str()) + "' "
      ")"
      ;
    DEBUG(sql, "Query: %s\n", ts.c_str());
    pqxx::result R = W.exec(ts);
    W.commit();
  } catch (std::exception &e) {
    ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str());
  }
  */
}

void PracroDAOTest::delFieldname(std::string name)
{
  // TODO

  /*
  std::string ts;
  try {
    pqxx::work W(*conn);
    ts = "DELETE FROM fieldnames WHERE name="
      "'" + W.esc(name) + "' ";
    DEBUG(sql, "Query: %s\n", ts.c_str());
    pqxx::result R = W.exec(ts);
    W.commit();
  } catch (std::exception &e) {
    ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str());
  }
  */
}

std::vector<Fieldname> PracroDAOTest::getFieldnames()
{
  // TODO

  std::vector<Fieldname> fieldnames;
  /*
  std::string query;
  try {
    pqxx::work W(*conn);
    query = "SELECT * FROM fieldnames";
    DEBUG(sql, "Query: %s\n", query.c_str());
    pqxx::result R = W.exec(query);
    pqxx::result::const_iterator ri = R.begin();
    while(ri != R.end()) {
      Fieldname f;
      f.name = (*ri)[0].c_str();
      f.description = (*ri)[1].c_str();
      f.timestamp = atol((*ri)[2].c_str());
      fieldnames.push_back(f);
      ri++;
    }
  } catch (std::exception &e) {
    ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str());
  }
  */
  return fieldnames;
}




#ifdef TEST_PRACRODAOTEST

#include <time.h>

#define PATIENTID "1234567890"

int main()
{
  time_t now = time(NULL);

  Data data;

  // Add some fieldnames
  dbrow_t f;
  f["name"] = "field1"; data.fieldnames.push_back(f);
  f["name"] = "field2"; data.fieldnames.push_back(f);
  f["name"] = "field3"; data.fieldnames.push_back(f);

  PracroDAOTest db(data);

  // Make a commit
  Macro macro;
  macro.attributes["version"] = "1.0";
  macro.attributes["name"] = "testmacro";

  Fields fields;
  fields["field1"] = "testval1";
  fields["field2"] = "testval2";
  fields["field3"] = "testval3";
  fields["field4"] = "testval4";
  db.commitTransaction("testuser", PATIENTID, macro, fields, now);

  // Retrieve the data again
  unsigned num = db.nrOfCommits(PATIENTID, "testmacro", now);
  if(num != 1) return 1;

  Fieldnames fieldnames;
  fieldnames.push_back("field1");
  fieldnames.push_back("field2");
  fieldnames.push_back("field3");
  fieldnames.push_back("field4");
  Values values = db.getLatestValues(PATIENTID, NULL, fieldnames, now);

  Values::iterator i = values.begin();
  while(i != values.end()) {
    printf("%s => %s\n", i->first.c_str(), i->second.value.c_str());
    i++;
  }
  if(values["field1"].value != "testval1") return 1;
  if(values["field2"].value != "testval2") return 1;
  if(values["field3"].value != "testval3") return 1;

  // This value was not committed, since it wasn't in the fieldnames table.
  if(values.find("field4") != values.end()) return 1;

  // Make another commit (one second later)
  fields["field1"] = "testval1-2";
  fields["field2"] = "testval2-2";
  fields["field3"] = "testval3-2";
  fields["field4"] = "testval4-2";
  db.commitTransaction("testuser", PATIENTID, macro, fields, now+1);

  // Retrieve the data again
  num = db.nrOfCommits(PATIENTID, "testmacro", now);
  if(num != 2) return 1;

  fieldnames.push_back("field1");
  fieldnames.push_back("field2");
  fieldnames.push_back("field3");
  fieldnames.push_back("field4");
  values = db.getLatestValues(PATIENTID, NULL, fieldnames, now);

  i = values.begin();
  while(i != values.end()) {
    printf("%s => %s\n", i->first.c_str(), i->second.value.c_str());
    i++;
  }
  if(values["field1"].value != "testval1-2") return 1;
  if(values["field2"].value != "testval2-2") return 1;
  if(values["field3"].value != "testval3-2") return 1;

  // This value was not committed, since it wasn't in the fieldnames table.
  if(values.find("field4") != values.end()) return 1;

  return 0;
}

#endif/*TEST_PRACRODAOTEST*/