/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            queryhandlerpentominos.cc
 *
 *  Thu Jan 15 11:35:31 CET 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 "queryhandlerpentominos.h"

#include "debug.h"

#include <config.h>

// For time
#include <time.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

// For time
#include <time.h>

// For strerror and errno
#include <errno.h>

// For socket and friends
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>

// For ioctl
#include <sys/ioctl.h>

#include "queryparser.h"

typedef struct {
  in_addr_t ip;
  time_t time;
  pid_t pid;
  unsigned short int count;
} UID;

#define SIOCGIFCONF 0x8912 // get iface list

static in_addr_t getIP(const char *interface)
{
  in_addr_t ret = 0;
  int numreqs = 30, sd, n;
  struct ifconf ifc;
  struct ifreq *ifr;
  struct in_addr *ia;

  sd = socket(AF_INET, SOCK_STREAM, 0);
  if(sd == -1) {
    //    throw Pentominos::UIDCouldNotConnectException(strerror(errno));
  }

  ifc.ifc_buf = NULL;
  ifc.ifc_len = sizeof(struct ifreq) * numreqs;

  ifc.ifc_buf = (char*)malloc(ifc.ifc_len);
  if(ifc.ifc_buf == NULL) {
    //    throw Pentominos::UIDOutOfMemoryException();
  }

  if (ioctl(sd, SIOCGIFCONF, &ifc) < 0) {
    //    throw Pentominos::UIDInterfaceListException(strerror(errno));
  } 

  ifr = ifc.ifc_req; 
  for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
    ia = (struct in_addr *)((ifr->ifr_ifru.ifru_addr.sa_data)+2);
    if(!strcmp(ifr->ifr_ifrn.ifrn_name, interface)) {
      ret = *(in_addr_t*)ia;
    }
    ifr++; 
  }

  if(!ret) { // Still no interface... We're fucked!
    //    throw Pentominos::UIDInterfaceException(interface);
  }

  free(ifc.ifc_buf);
  return ret;
}


static unsigned short counter = 0;
static unsigned short getCounter()
{
  return counter++;
}

static UID uid = {0,0,0,0};
static std::string getUID(const char *interface)
{
  if(!uid.ip) uid.ip = getIP(interface);

  time_t t = time(NULL);
  if(uid.time != t) counter = 0; // If time differes, reset the counter
  uid.time = t; // We need this value every time.

  if(!uid.pid) uid.pid = getpid();

  uid.count = getCounter();

  char buf[32];
  sprintf(buf, "%08x%08x%04x%04x", uid.ip, (unsigned int)uid.time, uid.pid, uid.count);
  return std::string(buf);
}


QueryHandlerPentominos::QueryHandlerPentominos(TCPSocket &_socket, std::string cpr)
  : QueryHandler(), socket(_socket)
{
  this->cpr = cpr;
}

QueryResult QueryHandlerPentominos::exec(Query &query)
{
  time_t timestamp = time(NULL);
  std::string uid = getUID("eth0");

  char buf[512];
  char header[] =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    "<artefact xmlns=\"http://www.aasimon.org/pentominos\"\n"
    "          xmlns:pentominos=\"http://www.aasimon.org/pentominos\"\n"
    "          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
    "          xsi:schemaLocation=\"http://www.aasimon.org/pentominos schema.xsd\">\n";
#ifndef WITHOUT_PENTOMINOS
  socket.write(header, strlen(header));
#endif/*WITHOUT_PENTOMINOS*/

  PRACRO_DEBUG(queryhandler, header);

  sprintf(buf, "  <pentominos:entry replyformat=\"xml\"\n"
          "                    cpr=\"%s\"\n"
          "                    operator=\"%s\"\n"
          "                    src_addr=\"%s\"\n"
          "                    dst_addr=\"%s\"\n"
          "                    timestamp=\"%d\"\n"
          "                    uid=\"%s\"/>\n",
          cpr.c_str(),
          "pracro",
#ifndef WITHOUT_PENTOMINOS
          socket.srcaddr().c_str(),
          socket.dstaddr().c_str(),
#else
          "127.0.0.1",
          "127.0.0.1",
#endif/*WITHOUT_PENTOMINOS*/
          (unsigned int)timestamp,
          uid.c_str());
#ifndef WITHOUT_PENTOMINOS
  socket.write(buf, strlen(buf));
#endif/*WITHOUT_PENTOMINOS*/

  PRACRO_DEBUG(queryhandler, buf);

  sprintf(buf, "  <pentominos:query device_id=\"%s\"\n"
          "                    device_type=\"%s\"\n"
          "                    filter=\"latest\"\n"
          "                    location=\"all\"/>\n",
          query.attributes["class"].c_str(),
          query.attributes["class"].c_str());
  
#ifndef WITHOUT_PENTOMINOS
  socket.write(buf, strlen(buf));
#endif/*WITHOUT_PENTOMINOS*/

  PRACRO_DEBUG(queryhandler, buf);

  sprintf(buf, "</artefact>");

#ifndef WITHOUT_PENTOMINOS
  socket.write(buf, strlen(buf));
#endif/*WITHOUT_PENTOMINOS*/

  PRACRO_DEBUG(queryhandler, buf);

  QueryResult result;
  
#ifndef WITHOUT_PENTOMINOS
  QueryParser parser;

  ssize_t size;

  // Read until we've got the entire result.
  while((size = socket.read(buf, sizeof(buf))) > 0) {
    //    fwrite(buf, size, 1, stdout); fflush(stdout);
    if(parser.parse(buf, size)) break;
  }

  result = parser.result;
#endif/*WITHOUT_PENTOMINOS*/

  PRACRO_INFO(queryhandler, "Done handling query\n");

  result.print();

  return result;
}

#ifdef TEST_QUERYHANDLERPENTOMINOS

int main()
{
#ifdef WITHOUT_PENTOMINOS
  printf("The project need to be configured for use of Pentominos in order to run this test.\n");
  return 1;
#endif/*WITHOUT_PENTOMINOS*/

 TCPSocket s;
  try {
    s.connect("localhost", 11108);
  } catch(Exception &e) {
    printf("ERROR: %s\n", e.what());
    printf("A running instance of the artefact server in needed on localhost, port 11108 in order for this test to run.\n");
    return 1;
  }

  QueryHandlerPentominos qh(s, "2003791613");

  Query q1;
  q1.attributes["device_id"] = "lensmeter";
  q1.attributes["device_type"] = "lensmeter";
  QueryResult res = qh.exec(q1);
  res.print();

  return 0;
}

#endif/*TEST_QUERYHANDLERPENTOMINOS*/