/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            queryhandler.cc
 *
 *  Tue May  6 14:50:56 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 "queryhandler.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>

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);
}


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

void QueryHandler::addQuery(Query &query)
{
  queries.push_back(query);
}

std::string QueryHandler::exec()
{
  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";
  socket->write(header, strlen(header));

  sprintf(buf, "  <pentominos:entry cpr=\"%s\"\n"
          "                    src_addr=\"%s\"\n"
          "                    dst_addr=\"%s\"\n"
          "                    timestamp=\"%d\"\n"
          "                    uid=\"%s\"/>\n",
          cpr.c_str(),
          socket->srcaddr().c_str(),
          socket->dstaddr().c_str(),
          (unsigned int)timestamp,
          uid.c_str());
  socket->write(buf, strlen(buf));

  std::vector< Query >::iterator j = queries.begin();
  while(j != queries.end()) {
    Query query = *j;

    sprintf(buf, "  <pentominos:query format=\"pracroxml\"\n"
            "                    device_id=\"%s\"\n"
            "                    device_type=\"%s\"\n"
            "                    filter=\"latest\"\n"
            "                    location=\"all\"/>\n",
            query.attributes["class"].c_str(),
            query.attributes["class"].c_str());

    socket->write(buf, strlen(buf));

    j++;
  }

  sprintf(buf, "</artefact>\n");
  socket->write(buf, strlen(buf));

  // Terminate
  char term[] = "\0";
  socket->write(term, 1);

  // Wait for answer
  char abuf[64];
  int res;
  std::string answer;
  do {
    memset(abuf, 0, sizeof(abuf));
    res = socket->read(abuf, sizeof(abuf) - 1);
    answer += abuf;
  } while(res);

  return answer;
}

#ifdef TEST_QUERYHANDLER

int main()
{
  TCPSocket s;
  s.connect("localhost", 11108);

  QueryHandler qh(&s, "2003791613");

  Query q1;
  q1.attributes["device_id"] = "lensmeter";
  q1.attributes["device_type"] = "lensmeter";
  qh.addQuery(q1);

  std::string res = qh.exec();
  
  printf("%s\n", res.c_str());

  return 0;
}

#endif/*TEST_QUERYHANDLER*/