/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set et sw=2 ts=2: */
/***************************************************************************
 *            session.cc
 *
 *  Tue Dec 15 13:36:49 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 "session.h"

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

// for stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include "journal.h"
#include "journal_uploadserver.h"

#include "database.h"
#include "configuration.h"
#include "connectionpool.h"
#include "sessionserialiser.h"

Session::Session(std::string sessionid)
{
  _id = sessionid;
  _journal = NULL;
  _database = NULL;
}

Session::~Session()
{
  if(_journal) delete _journal;
  if(_database) delete _database;
}

std::string Session::id()
{
  return _id;
}

void Session::lock()
{
  mutex.lock();
}

void Session::unlock()
{
  mutex.unlock();
}

void Session::commit()
{
  if(_journal != NULL) {
    _journal->commit();
    delete _journal;
    _journal = NULL;
  }
  if(_database != NULL) {
    _database->commit();
    delete _database;
    _database = NULL;
  }
}

void Session::discard()
{
  if(_journal) {
    delete _journal;
    _journal = NULL;
  }
  if(_database != NULL) {
    _database->discard();
    delete _database;
    _database = NULL;
  }
}

Journal *Session::journal()
{
  if(_journal == NULL) {
   _journal =
      new JournalUploadServer(Conf::journal_commit_addr,
                              Conf::journal_commit_port);
  }
  return _journal;
}

Database *Session::database()
{
  if(_database == NULL) {
   _database =
      new Database(Conf::database_backend, Conf::database_addr, "",
                   Conf::database_user, Conf::database_passwd, "");
  }
  return _database;
}

Sessions::Sessions()
{
}

static bool fexists(const std::string &f)
{
  bool ret;

/*
  struct stat sbuf;
  int n = stat(f.c_str(), &sbuf);
  if(n != -1) ret = true;
  ret = errno != ENOENT;
*/

  FILE *fp = fopen(f.c_str(), "r");
  ret = fp != NULL;
  if(fp) fclose(fp);

  return ret;
}

Session *Sessions::newSession()
{
  char sessionid[32];
  std::string filename;
  do {
    snprintf(sessionid, sizeof(sessionid)-1, "%d", rand());
    filename = getSessionFilename(Conf::session_path, sessionid);
  } while(sessions.find(sessionid) != sessions.end() || fexists(filename));

  Session *session = new Session(sessionid);
  sessions[session->id()] = session;
  return session;
}

Session *Sessions::session(std::string sessionid)
{
  if(sessions.find(sessionid) != sessions.end())
    return sessions[sessionid];

  std::string filename = getSessionFilename(Conf::session_path, sessionid);
  if(fexists(filename)) {
    Session *s = new Session(sessionid);
    SessionSerialiser ser(Conf::session_path, s);
    ser.load();
    sessions[s->id()] = s;

    fprintf(stderr, "s: %p\n",s);

    return s;
  }

  return NULL;
}

Session *Sessions::takeSession(std::string sessionid)
{
  Session *s = NULL;
  if(sessions.find(sessionid) != sessions.end()) {
    s = sessions[sessionid];
  }

  if(s) {
    sessions.erase(sessionid);
  }

  return s;
}

void Sessions::deleteSession(std::string sessionid)
{
  Session *s = takeSession(sessionid);
  if(s) delete s;
}

size_t Sessions::size()
{
  return sessions.size();
}

void Sessions::store()
{
  std::map<std::string, Session*>::iterator i = sessions.begin();
  while(i != sessions.end()) {
    SessionSerialiser ser(Conf::session_path, i->second);
    ser.save();
    delete i->second;
    sessions.erase(i);
    i++;
  }
  sessions.clear();
}

SessionAutolock::SessionAutolock(Session &s)
  : session(s)
{
  session.lock();
}

SessionAutolock::~SessionAutolock()
{
  session.unlock();
}

#ifdef TEST_SESSION
//deps: configuration.cc journal.cc journal_commit.cc mutex.cc debug.cc sessionserialiser.cc sessionparser.cc saxparser.cc
//cflags: -I.. $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS)
//libs: $(PTHREAD_LIBS) $(EXPAT_LIBS)
#include <test.h>

TEST_BEGIN;
Sessions sessions;

Conf::session_path = "/tmp";

srand(0); // force seed
Session *s1 = sessions.newSession();
srand(0); // force seed
Session *s2 = sessions.newSession();

TEST_NOTEQUAL(s1->id(), s2->id(), "Testing if IDs are unique.");

TEST_EQUAL(sessions.size(), 2, "Testing if size match.");

std::string sessionid = s1->id();
SessionSerialiser ser(Conf::session_path, s1);
ser.save();

sessions.deleteSession(sessionid);
TEST_EQUAL(sessions.size(), 1, "Testing if size match.");

s1 = sessions.session(sessionid);
TEST_NOTEQUAL(s1, NULL, "Did we reload the session from disk?");

sessions.store();
TEST_EQUAL(sessions.size(), 0, "Testing if size match.");

s1 = sessions.session(sessionid);
TEST_NOTEQUAL(s1, NULL, "Did we reload the session from disk?");

TEST_END;

#endif/*TEST_SESSION*/