/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            widgetgenerator.cc
 *
 *  Mon May 19 09:58:41 CEST 2008
 *  Copyright 2008 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 "debug.h"
#include "widgetgenerator.h"

#include "configuration.h"
#include "xml_encode_decode.h"

static std::string automap(std::string name)
{
  std::string group;
  std::string groupcheck = "if(";

  for(size_t i = 0; i < name.length(); i++) {
    group += name[i];
    if(name[i] == '.') groupcheck += " and " + group;
    else groupcheck += name[i];
  }
  groupcheck += " and " + name + ".value and " + name + ".timestamp and " + name + ".source";
  groupcheck += ")\n";

  std::string automapstring =
    "-- Returning 0, 0 invalidates the result\n"
    "value = 0\n"
    "timestamp = 0\n"
    "source = 0\n"
    "\n"
    + groupcheck + 
    "then\n"
    "  value = " + name + ".value\n"
    "  timestamp = " + name + ".timestamp\n"
    "  source = " + name + ".source\n"
    "end\n"
    "return value, timestamp, source\n";

  PRACRO_DEBUG(widget, "Automap:\n%s\n", automapstring.c_str());

  return automapstring;
}

static std::string send_macro_widget(Macro &macro,
                                     Widget &widget,
                                     std::string tabs,
                                     LUAQueryMapper &mapper,
                                     Values &values)
{
  std::string result;

  std::string prefilled;
  time_t timestamp = 0;
  time_t now = time(NULL);

  result = tabs + "<" + widget.attributes["tagname"];
  std::map< std::string, std::string >::iterator p = widget.attributes.begin();

  PRACRO_DEBUG(prefill, "%s: %s\n",
               widget.attributes["tagname"].c_str(),
               widget.attributes["name"].c_str());

  PRACRO_DEBUG(prefill, "0: (%s, %s, %d)\n",
               prefilled.c_str(),
               widget.attributes["value"].c_str(),
               (int)timestamp);

  // Check if the field has a map, and fill in the value if it has...
  if(widget.attributes.find("map") != widget.attributes.end()) {
    std::string luamap;

    std::vector< Map >::iterator li = macro.maps.begin();
    while(li != macro.maps.end()) {
      Map &map = *li;
      if(map.attributes["name"] == widget.attributes["map"]) {
        luamap = map.attributes["lua"];
      }
      li++;
    }

    // Check to see if we should automap
    if(luamap == "") {
      luamap = automap(widget.attributes["map"]);
    }
    
    //    printf("LUAMAP: %s\n", luamap.c_str()); fflush(stdout);

    if(luamap != "") {
      Value value = mapper.map(luamap);
      if(value.timestamp > now - Conf::pentominos_max_ttl) {
        widget.attributes["value"] = xml_encode(value.value);
        timestamp = value.timestamp;
        prefilled = xml_encode(value.source);
      }

      PRACRO_DEBUG(prefill, "map: (%s, %d)\n",
                   value.value.c_str(),
                   (int)value.timestamp);

    }

    //    widget.attributes.erase(widget.attributes.find("map"));
  }

  PRACRO_DEBUG(prefill, "1: (%s, %s, %d)\n",
               prefilled.c_str(),
               widget.attributes["value"].c_str(),
               (int)timestamp);

  // Check if there is a previously stored value in the db...
  if(values.find(widget.attributes["name"]) != values.end()) {

    PRACRO_DEBUG(prefill, "db: (%s, %d)\n",
                 values[widget.attributes["name"]].value.c_str(),
                 (int)values[widget.attributes["name"]].timestamp);
    
    if(values[widget.attributes["name"]].timestamp > timestamp) {
      if(values[widget.attributes["name"]].timestamp > now - Conf::db_max_ttl) {
        widget.attributes["value"] = xml_encode(values[widget.attributes["name"]].value);
        timestamp = values[widget.attributes["name"]].timestamp;
        prefilled = "pracro";
      }
    }
  }

  PRACRO_DEBUG(prefill, "2: (%s, %s, %d)\n",
               prefilled.c_str(),
               widget.attributes["value"].c_str(),
               (int)timestamp);

  while(p != widget.attributes.end()) {
    if(p->first != "tagname" && p->first != "map") {
      if( ! (p->first == "name" && p->second == "") )
        result += " " + p->first + "=\"" + p->second + "\"";
    }
    p++;
  }

  if(prefilled != "") result += " prefilled=\"" + prefilled + "\"";

  if(widget.widgets.size() == 0) { // If node is empty, use short tag form
    result += "/>\n";
    return result;
  }

  result += ">\n";

  std::vector< Widget >::iterator w = widget.widgets.begin();
  while(w != widget.widgets.end()) {
    result += send_macro_widget(macro, *w, tabs + "  ", mapper, values);
    w++;
  }
  result += tabs + "</" + widget.attributes["tagname"] + ">\n";

  return result;
}

static void get_fields(Widget &widget, Fieldnames &fields)
{
  if(widget.attributes.find("value") != widget.attributes.end()) {
    if(widget.attributes["name"] != "") fields.push_back(widget.attributes["name"]);
  }
  
  std::vector< Widget >::iterator w = widget.widgets.begin();
  while(w != widget.widgets.end()) {
    get_fields(*w, fields);
    w++;
  }
}

std::string widgetgenerator(std::string cpr, Macro &macro, LUAQueryMapper &mapper, Database &db)
{
  Fieldnames fields;
  get_fields(macro.widgets, fields);

  Values values = db.getValues(cpr, fields);
  
  return send_macro_widget(macro, macro.widgets, "      ", mapper, values);
}

#ifdef TEST_WIDGETGENERATOR

#include <time.h>

#define PATIENTID "1234567890"

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

  printf("Test pretty printer:\n");
  {
    Macro macro;
    macro.widgets.attributes["tagname"] = "lineedit";
    macro.widgets.attributes["name"] = "mywidget";
    macro.widgets.attributes["value"] = "myvalue";
    macro.widgets.attributes["map"] = "mapvalue";
    macro.widgets.widgets.push_back(macro.widgets);

    Database db("testdb", "", "", "", "", "");
    LUAQueryMapper mapper;
    std::string result;
    Fields fields;
    QueryResult queryresult;

    // Test simple
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result != "      <lineedit name=\"mywidget\" value=\"myvalue\">\n"
       "        <lineedit name=\"mywidget\" value=\"myvalue\"/>\n"
       "      </lineedit>\n") return 1;
  }

  printf("Positive tests:\n");
  {
    Macro macro;
    macro.widgets.attributes["tagname"] = "lineedit";
    macro.widgets.attributes["name"] = "mywidget";
    macro.widgets.attributes["value"] = "myvalue";
    macro.widgets.attributes["map"] = "mapvalue";

    Database db("testdb", "", "", "", "", "");
    LUAQueryMapper mapper;
    std::string result;
    Fields fields;
    QueryResult queryresult;

    // Test simple
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result != "      <lineedit name=\"mywidget\" value=\"myvalue\"/>\n") return 1;

    // Make a database commit and test if the value shows up
    fields["mywidget"] = "testval";
    db.commitTransaction("testuser", PATIENTID, macro, fields, now);
    
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result != "      <lineedit name=\"mywidget\" value=\"testval\" prefilled=\"pracro\"/>\n") return 1;
    
    // Make a query result (newer than the db value) and see if it shows up
    queryresult.timestamp = now + 1;
    queryresult.source = "testsource";
    queryresult.values["mapvalue"] = "mymapvalue";
    mapper.addQueryResult(queryresult);
    
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result != "      <lineedit name=\"mywidget\" value=\"mymapvalue\" prefilled=\"testsource\"/>\n") return 1;
    
    // Make another db value (newer than the query result) and see if it shows up.
    fields["mywidget"] = "testval2";
    db.commitTransaction("testuser", PATIENTID, macro, fields, now + 2);
    
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result != "      <lineedit name=\"mywidget\" value=\"testval2\" prefilled=\"pracro\"/>\n") return 1;
  }

  printf("Negative tests:\n");
  {
    Macro macro;
    macro.widgets.attributes["tagname"] = "lineedit";
    macro.widgets.attributes["name"] = "mywidget";
    macro.widgets.attributes["value"] = "myvalue";
    macro.widgets.attributes["map"] = "mapvalue";

    Database db("testdb", "", "", "", "", "");
    LUAQueryMapper mapper;
    std::string result;
    Fields fields;
    QueryResult queryresult;

    // Test simple
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result != "      <lineedit name=\"mywidget\" value=\"myvalue\"/>\n") return 1;

    // Make a database commit too old, and test if the value shows up
    fields["mywidget"] = "testval";
    db.commitTransaction("testuser", PATIENTID, macro, fields, now - Conf::db_max_ttl - 1);
    
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result == "      <lineedit name=\"mywidget\" value=\"testval\" prefilled=\"pracro\"/>\n") return 1;
    
    // Make a too old query result (newer than the db value) and see if it shows up
    queryresult.timestamp = now - Conf::pentominos_max_ttl - 1;
    queryresult.source = "testsource";
    queryresult.values["mapvalue"] = "mymapvalue";
    mapper.addQueryResult(queryresult);
    
    result = widgetgenerator(PATIENTID, macro, mapper, db);
    printf("[%s]\n", result.c_str());
    if(result == "      <lineedit name=\"mywidget\" value=\"mymapvalue\" prefilled=\"testsource\"/>\n") return 1;
  }
  return 0;
}

#endif/*TEST_WIDGETGENERATOR*/