// src/testsuite/test-history.cc - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This 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.
//
// SGPEMv2 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 SGPEMv2; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

/* This executable tests for workingness of the whole History services,
 * and nothing else. */

#include "config.h"
#include "gettext.h"

#include <glibmm/ustring.h>

#include <cassert>
#include <iostream>
#include <sstream>

#include "backend/concrete_environment.hh"
#include "backend/concrete_history.hh"
#include "backend/history_observer.hh"
#include "xml_serializer.hh"

#include <string.h>

using namespace sgpem;
using namespace std;

/*
 * This program tests the XML serialization via the XMLSerializer class.
 *
 */


// insert some resources, processes, threads, requests, subrequests
void fillHistory(History &hist);

// add some other data to verify difference from previous configuration
void addSomethingHistory(History &hist);

// print entire environment into the passed ostream
void dumpEnvironment(const Environment& env, ostream &os);



int
main(int argc, char** argv)
{
  // output file name
  Glib::ustring outfile("xml-visit.xml");

  // string stream to compare print dump of environment
  ostringstream os1;
  ostringstream os2;
  ostringstream os3;
  ostringstream os4;

  ConcreteHistory hist;
  cout << "create ConcreteHistory hist" << endl;

  // create and fill a history
  fillHistory(hist);
  cout << "fill hist with data" << endl;

  // print on a string stream os1
  dumpEnvironment(hist.get_last_environment(), os1);
  os1 << ends; // null terminated string
  // show on consolle
  cout << "dump hist(t1) in print format into os1" << endl;

  // save to the file
  XMLSerializer xmlser;
  xmlser.save_snapshot(outfile , hist);
  cout << "put hist(t1) in XML format into output file " << outfile << endl;

  // add something other to history
  addSomethingHistory(hist);
  cout << "add other data to hist" << endl;

  // print on a string stream os2
  dumpEnvironment(hist.get_last_environment(), os2);
  os2 << ends; // null terminated string
  cout << "dump hist(t2) in print format into os2" << endl;

  cout << "Comparing dump of hist(t1) and hist(t2): " << (os1.str() == os2.str() ? "equals" : "not equals") << endl;

  ConcreteHistory hist2;
  xmlser.restore_snapshot(outfile, hist2);
  cout << "create ConcreteHistory hist" << endl;
  cout << "read XML data from file " << outfile << " and put into hist2(t3)" << endl;

  dumpEnvironment(hist2.get_last_environment(), os3);
  os3 << ends; // null terminated string
  cout << "dump hist2(t3) in print format into os3" << endl;

  cout << "Comparing dump of hist(t1) and hist2(t3): " << (os1.str() == os3.str() ? "equals" : "not equals") << endl;

  xmlser.restore_snapshot(outfile, hist);
  cout << "read XML data from file " << outfile << " and put into hist(t4)" << endl;

  dumpEnvironment(hist.get_last_environment(), os4);
  os4 << ends; // null terminated string
  cout << "dump hist(t3) in print format into os4" << endl;

  cout << "Comparing dump of hist(t1) and hist(t4): " << (os1.str() == os4.str() ? "equals" : "not equals") << endl;

  int ret = 1;
  if (os1.str() != os2.str()
      && os1.str() == os3.str()
      && os1.str() == os4.str())
  {
    cout << "test successful" << endl;
    ret = 0;
  }
  else
  {
    cout << "test failed" << endl;
    ret = 1;
  }
  return ret;
}


// insert some resources, processes, threads, requests, subrequests
void fillHistory(History &hist)
{
  // insert resources to delete soon
  // this create a numbering of keys starting from 2...
  // serves to prove correct save/restore independent from key numbering
  // add a resource - name, preemptable, places, availability
  History::ResourcePair respair0 = hist.add_resource(Glib::ustring("Resource 0"), false, 1, 2);
  // add a resource - name, preemptable, places, availability
  History::ResourcePair respair00 = hist.add_resource(Glib::ustring("Resource 00"), false, 1, 2);

  // add a resource - name, preemptable, places, availability
  History::ResourcePair respair = hist.add_resource(Glib::ustring("Resource 1"), false, 1, 2);

  // add a resource - name, preemptable, places, availability
  History::ResourcePair respair2 = hist.add_resource(Glib::ustring("Invalid? Resource <n> 1"), false, 1, 2);
  // and print his values

  // delete resources to kreate a key numbering hole
  hist.remove(respair0.first);
  hist.remove(respair00.first);

  // add a process - name, arrival time, priority
  Process&   p1 =  hist.add_process(Glib::ustring("Process 1"), 0, 2); // name, arrival time, priority
  // and print his values

  // add a process - name, arrival time, priority
  Process&   p2 =  hist.add_process(Glib::ustring("Process 2"), 7, 3); // name, arrival time, priority
  // and print his values

  // add a process - name, arrival time, priority
  Process&   p3 =  hist.add_process(Glib::ustring("Process 3"), 9, 1); // name, arrival time, priority
  // and print his values

  // add a process - name, arrival time, priority
  Process&   p4 =  hist.add_process(Glib::ustring("Invalid? <process/> &3 or\\4"), 9, 1); // name, arrival time, priority
  // and print his values

  // add a thread - name, parent, cpu time, arrival time, priority
  Thread&   p1_t1 =  hist.add_thread(Glib::ustring("Process 1 - Thread 1"), p1, 8, 2, 6);
  // and print his values

  // add a thread - name, parent, cpu time, arrival time, priority
  Thread&   p1_t2 =  hist.add_thread(Glib::ustring("Process 1 - Thread 2"), p1, 3, 3, 5);
  // and print his values

  // add a request - Thread, time
  Request&   req1 =  hist.add_request(p1_t1, 3);

  // add a sub request - Request, resource_key, duration, places
  SubRequest& req1_sub1 = hist.add_subrequest(req1, respair.first, 5, 2);
}

// add some other data to verify difference from previous configuration
void addSomethingHistory(History &hist)
{
  // add a resource - name, preemptable, places, availability
  History::ResourcePair respair = hist.add_resource(Glib::ustring("Resource 33"), false, 4, 1);

  // add a process - name, arrival time, priority
  Process&   p1 =  hist.add_process(Glib::ustring("Process 44"), 4, 2); // name, arrival time, priority
}

// print entire environment into the passed ostream
void dumpEnvironment(const Environment& env, ostream &os)
{
  os << "dump environment start " << endl;

  const Environment::Resources& rvect = env.get_resources();
  typedef Environment::Resources::const_iterator res_iterator;

  res_iterator riter = rvect.begin();
  while (riter != rvect.end())
  {
    Resource* r = (*riter).second;
    os << "  resource name: " << r->get_name()
    /* << " key: " << (*riter).first */
    << " places: " << r->get_places() << endl;
    riter++;
  }

  const Environment::Processes& pvect = env.get_processes();
  typedef std::vector<Process*>::const_iterator proc_iterator;

  proc_iterator iter = pvect.begin();
  proc_iterator end = pvect.end();
  while (iter != end)
  {
    Process* p = (*iter);
    os << "  process name: " << p->get_name()
    << " arrival_time: " << p->get_arrival_time()
    << " base_priority: " << p->get_base_priority() << endl;
    iter++;

    typedef std::vector<Thread*> Threads;
    typedef std::vector<Thread*>::const_iterator thr_iterator;
    const Threads& tvect = p->get_threads();
    thr_iterator iter1 = tvect.begin();
    thr_iterator end1  = tvect.end();
    while (iter1 != end1)
    {

      Thread* t = (*iter1);
      os << "    thread name: " << t->get_name()
      << " arrival_time: " << t->get_arrival_time()
      << " base_priority: " << t->get_base_priority() << endl;

      typedef std::vector<Request*> Requests;
      typedef std::vector<Request*>::const_iterator req_iterator;
      const Requests& rvect = t->get_requests();
      req_iterator iter2 = rvect.begin();
      req_iterator end2  = rvect.end();
      while (iter2 != end2)
      {

        Request* r = (*iter2);
        os << "      request arrival_time: " << r->get_instant() << endl;

        typedef std::vector<SubRequest*> SubRequests;
        typedef std::vector<SubRequest*>::const_iterator subreq_iterator;
        const SubRequests& srvect = r->get_subrequests();
        subreq_iterator iter3 = srvect.begin();
        subreq_iterator end3  = srvect.end();
        while (iter3 != end3)
        {

          SubRequest* sr = (*iter3);
          os << "        sub request: " /* << " resource_key: " << sr->get_resource_key() */;

          Environment::Resources::const_iterator pos = env.get_resources().find(sr->get_resource_key());
          if (pos != env.get_resources().end())
          {
            os << " name: " << pos->second->get_name();
          }

          os << " places: " << sr->get_places() << " length: " << sr->get_length() << endl;

          iter3++;
        }


        iter2++;
      }

      iter1++;
    }

  }
  os << "dump environment end " << endl << endl;
}
