/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            server.cc
 *
 *  Wed Aug 22 12:16:03 CEST 2007
 *  Copyright 2007 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 "server.h"

#include "tcpsocket.h"
#include <errno.h>

#include <stdlib.h>

// For fork
#include <sys/types.h>
#include <unistd.h>

#include "configuration.h"
#include "transaction.h"
#include "transactionparser.h"
#include "templateparser.h"
#include "macroparser.h"
#include "queryhandler.h"
#include "queryparser.h"
#include "luaquerymapper.h"
#include "database.h"
#include "widgetgenerator.h"
#include "resumeparser.h"
#include "journal_commit.h"
#include "xml_encode_decode.h"

static std::string error_box(std::string message)
{
  std::string errorbox =
    "  <course name=\"error\">\n"
    "    <macro name=\"error\">\n"
    "      <window caption=\"ERROR!\" height=\"240\" layout=\"vbox\" name=\"error\" width=\"320\">\n"
    "        <textedit name=\"errorlabel\" value=\"" + message + "\"/>\n"
    "        <button action=\"cancel\" caption=\"Luk\" name=\"cancel\"/>\n"
    "      </window>\n"
    "    </macro>\n"
    "  </course>\n";
  return errorbox;
}

static void connection(TCPSocket &socket)
{
  socket.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  socket.write("<pracro version=\"1.0\">\n");

  try {
    Transaction transaction;
    TransactionParser parser(socket, transaction);
    parser.parse();

    Database db;

    //
    // Handle commits
    //
    if(transaction.commits.size() > 0) {
      Commits::iterator i = transaction.commits.begin();
      while(i != transaction.commits.end()) {
        Commit &commit = *i;
        
        /*
          Macro macro;
          MacroParser parser(commit.macro, macro);
          parser.parse();
        */       
        
        Macro macro;
        macro.attributes["name"] = commit.macro;
        macro.attributes["version"] = "1.0";//commit.version;
        
        db.commit(transaction.user, transaction.cpr, macro, commit.fields);
        
        /*
          std::string resume = resume_parser(macro.format.c_str(), commit);
          
          std::string journal_commit_addr = config()->lookup("journal_commit_addr");
          int journal_commit_port = config()->lookup("journal_commit_port");
          
          journal_commit(transaction.cpr.c_str(), transaction.user.c_str(),
          journal_commit_addr.c_str(), journal_commit_port,
          resume.c_str(), resume.length());
        */
        i++;
      }
    }
    
    //
    // Handle requests
    //
    Requests::iterator i = transaction.requests.begin();
    while(i != transaction.requests.end()) {
      Request &request = *i;

      std::string answer;

      MacroParser mp(request.macro);
      mp.parse();
      Macro *m = mp.getMacro();
      
      printf("Handling request - macro: %s, course: %s\n", request.macro.c_str(), request.course.c_str());
      
      // Read and parse the template file.
      TemplateParser tp(request.course);
      tp.parse();
      
      Template *templ = tp.getTemplate();
      
      // Send the queries to Pentominos (if any)
      TCPSocket s;
      s.connect("localhost", 11108);
      QueryHandler qh(&s, transaction.cpr);
      
      std::vector< Macro >::iterator mi = templ->course.macroes.begin();
      while(mi != templ->course.macroes.end()) {
        Macro &macro = (*mi);
        if(macro.attributes["name"] == request.macro) {

          std::vector< Query >::iterator qi = m->queries.begin();
          while(qi != m->queries.end()) {
            qh.addQuery(*qi);
            qi++;
          }
        }
        mi++;
      }
      std::string result = qh.exec();
      
      printf("Got result: [%s]\n", result.c_str());
      
      // Parse the result from the queries to pentominos
      QueryParser qp(result);
      qp.parse();
      
      // Map the results
      LUAQueryMapper lqm(qp.result);
      
      answer += "  <course name=\"";
      answer += templ->course.attributes["name"];
      answer += "\">\n";
      
      // Generate the macro and return it to the client
      std::vector< Macro >::iterator mi2 = templ->course.macroes.begin();
      while(mi2 != templ->course.macroes.end()) {
        Macro &macro = (*mi2);
        
        answer += "    <macro name=\"" + macro.attributes["name"] + "\" completed=";
        if(db.checkMacro(transaction.cpr, macro.attributes["name"])) answer += "\"true\"";
        else answer += "\"false\"";
        answer += ">\n";
        
        if(macro.attributes["name"] == request.macro) {

          // Handle lua programs
          if(m->luaprograms.size()) {
            answer += "      <luaprograms>\n";
            
            std::vector< LUAProgram >::iterator lpi = m->luaprograms.begin();
            while(lpi != m->luaprograms.end()) {
              answer += "        <luaprogram name=\"" + lpi->attributes["name"] + "\">\n";
              answer += xml_encode(lpi->attributes["lua"]);
              answer += "\n        </luaprogram>\n";
              lpi++;
            }
            answer += "      </luaprograms>\n";
          }
          answer += widgetgenerator(*m, lqm, db);
        }
        answer += "    </macro>\n";
        mi2++;
      }
      
      answer += "  </course>\n";
      socket.write(answer);
      i++;
    }
  } catch(std::exception &e) {
    socket.write(error_box(xml_encode(e.what())));
  }

  socket.write("</pracro>\n");
}


void server()
{
  int port;
  try {
    port = config()->lookup("port");
  } catch( ... ) {
    fprintf(stderr, "Could not read port.");
    return;
  }

  TCPSocket *socket = NULL;
  
  try {
    socket = new TCPSocket();
    socket->listen(port);
  } catch (Exception &e) {
    fprintf(stderr, "Error during parsing:\n%s\n",
            e.what());
    delete socket;
    socket = NULL;
    return;
  }

  while(socket->connected()) {

    { // Reload if new port is assigned.
      int old_port = port;
      try {
        port = config()->lookup("port");
      } catch( ... ) {
        fprintf(stderr, "Could not read port.");
        return;
      }

      if(port != old_port) {
        // Start listening on the new port
        delete socket;
        socket = new TCPSocket();
        socket->listen(port);
      }
    }

    TCPSocket child = socket->accept();
    if(child.connected()) {
      //socket->disconnect();
      connection(child);
      //delete socket;

      /*
      switch(fork()) {
      case -1: // error
        fprintf(stderr, "Could not fork: %s\n", strerror(errno));
        break;
        
      case 0: // child
        socket->disconnect();
        connection(child);
        delete socket;
        return;
        
      default: // parent
        child.disconnect();
        break;
      }
      */
    }
  }

  delete socket;
  fprintf(stderr, "Oups... dropped out of the main loop\n");
}

#ifdef TEST_SERVER

char request[] = 
  "<?xml version='1.0' encoding='UTF-8'?>\n"
  "<pracro cpr=\"2003791613\" version=\"1.0\">\n"
  "  <request macro=\"example\" course=\"example\"/>\n"
  "</pracro>\n";

int main()
{
  Configuration conf("../etc/pracrod.conf");
  initConfig(&conf);

  switch(fork()) {
  case -1: // error
    return 1;
    
  case 0: // child
    server();
    return 0;
    
  default: // parent
    {
      TCPSocket socket;
      int port = config()->lookup("port");
      socket.connect("localhost", port);
      
      socket.write(request);
      char buf[32];
      memset(buf, 0, sizeof(buf));
      while(socket.read(buf, 31)) {
        printf(buf); fflush(stdout);
        memset(buf, 0, sizeof(buf));
      }
    }
    return 0;
  }
  
  return 1;
}

#endif/*TEST_SERVER*/