/*

(C) Copyright 2011.  All rights reserved.
This source code is the intellectual property of Matthew William Coan.

HTTP CGI session object.

Author: Matthew W. Coan
Date: Sat Jun  4 15:16:25 EST 2011

*/

#ifndef _SESSION_H
#define _SESSION_H

#include <iostream>
#include <fstream>
#include <string>
#include <list>

#include <cstring>
#include <cstdlib>
#include <ctime>

#include "cookie.h"
#include "csv.h"
#include "bfstream.h"
#include "lock_file.h"

#define SESSION_KEY_SIZE 20
#define CHOICE_SIZE 36
#define COOKIE_KEY "$SESSION$"
#define TIMEOUT_LENGTH 60

namespace cgi_tools {

using namespace std;
using namespace csv_reader;
using namespace file_tools;

typedef list< csv_row_type > csv_row_list_type;

class Session {
   string file_name;
   string session_key;
   string session_dir;
   LockFile lock_file;
   CookieListType * p_cookie_list;

public:
   Session(const string & fname, const string & dir, const string & lock)
   :file_name(fname), session_dir(dir), lock_file(lock.c_str())
   {
      p_cookie_list = getCookieList();

      if(p_cookie_list) {
         for(CookieListType::iterator ptr = p_cookie_list->begin(); ptr != p_cookie_list->end(); ptr++) {
            if(strcmp((*ptr)->name(), COOKIE_KEY) == 0) {
               session_key = (*ptr)->value();
               break;
            }
         }
      }

      srand(time(NULL));
   }

   ~Session()
   {
      if(p_cookie_list) {
         deleteCookieList(p_cookie_list);
      }
   }

   void print_cookie_list() 
   {
      if(p_cookie_list) {
         for(CookieListType::iterator ptr = p_cookie_list->begin(); ptr != p_cookie_list->end(); ptr++) {
            cout << "name: " << (*ptr)->name() << "<BR>" << endl << flush;
            cout << "value: " << (*ptr)->value() << "<BR>" << endl << flush;
         }
         cout << "<BR>" << endl << flush;
      }
   }

   string get_random_key()
   {
      const char * choice = "abcdefghijklmnopqrstuvwxyz0123456789";

      string value;

      for(size_t i = 0; i < SESSION_KEY_SIZE; i++)
         value += choice[rand() % CHOICE_SIZE];
 
      return value;
   }

   void set_cookie()
   {
      if(!session_key.size()) 
         session_key = get_random_key();

      Cookie cookie(COOKIE_KEY, session_key.c_str(), 0, 0, false, (time(NULL) + (TIMEOUT_LENGTH)));

      cookie.setCookie();
   }

   template< class T >
   bool save(const T & data)
   {
      lock_file.write_lock();

      bool ret = false;

      string fn = session_dir + session_key + ".dat";

      bfstream fout2(fn.c_str(), ios::out | ios::trunc);

      if(fout2) {
         fout2 << data;

         fout2.flush();

         fout2.close();
      }

      bool found = false;

      csv_row_type row;
      csv_row_map_type row_map;
      
      fstream fin(file_name.c_str(), ios::in);

      if(fin) {
         fin >> row;

         if(fin) {
            map_row(row, row_map);

            while(fin >> row) {
               if(row[row_map["KEY"]] == session_key) {
                  found = true;

                  break;
               }
            }
         }

         fin.close();
      }

      if(!found) {
         fstream fout(file_name.c_str(), ios::out | ios::app);

         if(fout) {
            fout << "\"" << session_key << "\",\"" << fn << "\",\"" << time(NULL) << "\"" << endl << flush;

            fout.close();
         }
      }

      lock_file.write_unlock();

      return ret;
   }

   template< class T >
   bool load(T & data) 
   {
      lock_file.write_lock();

      bool ret = false;

      fstream fin(file_name.c_str(), ios::in);

      if(fin) {
         ret = true;

         csv_row_type row;

         csv_row_map_type row_map;

         fin >> row;

         if(fin)
            map_row(row, row_map);

         string k, fn, lm;

         // "KEY","FILE_NAME","LAST_MOD"
         csv_row_list_type the_list;

         bool found = false;

         while(fin >> row) {
            if(row[row_map["key"]] == session_key) {
               k = row[row_map["key"]];
               fn = row[row_map["file_name"]];
               lm = row[row_map["last_mod"]];
               found = true;
            }
            the_list.push_back(row); 
         }

         fin.close();

         fstream fout(file_name.c_str(), ios::out | ios::trunc);

         if(fout) {
            fout << "\"key\",\"file_name\",\"last_mod\"" << endl << flush;
            for(csv_row_list_type::iterator ptr = the_list.begin(); ptr != the_list.end(); ptr++) {
               if((*ptr)[row_map["key"]] == session_key) {
                  fout << "\"" << (*ptr)[row_map["key"]] << "\",\"" << (*ptr)[row_map["file_name"]] 
                       << "\",\"" << time(NULL) << "\"" << endl << flush;
               }
               else {
                  fout << "\"" << (*ptr)[row_map["key"]] << "\",\"" << (*ptr)[row_map["file_name"]] 
                       << "\",\"" << (*ptr)[row_map["last_mod"]] << "\"" << endl << flush;
               }
            }

            if(!found) {
               fout << "\"" << session_key << "\",\"" << session_dir << session_key << ".dat"
                    << "\",\"" << time(NULL) << "\"" << endl << flush;
            }

            fout.close();
         }
         
         if(fn.size()) {
            bfstream bin(fn.c_str(), ios::in);

            if(bin) {
               bin >> data;

               if(bin)
                  ret = true;

               bin.close();
            }

         }
      }

      lock_file.write_unlock();

      return ret;
   }
};

}

#endif /* _SESSION_H */
