#include <applicat.h>
#include <mask.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <tabutil.h>
#include <toolfld.h>
#include <reputils.h>
#include <urldefid.h>
#include <hashtable.h>

#include <comuni.h>

class TProfiler_mask : public TMask
{
public:
  TProfiler_mask();
};

TProfiler_mask::TProfiler_mask() : TMask(TR("Test"), 1, 60, 20)
{
  add_button_tool(DLG_OK,   PR("Elabora"), TOOL_ELABORA);
  add_button_tool(DLG_INFO, PR("Info"),    TOOL_INFO);
  add_button_tool(DLG_HELP, PR("Help"),    TOOL_HELP);
  add_button_tool(DLG_QUIT, "",            TOOL_QUIT);
  
  TToken_string codes, items;
  codes.add(0); items.add(TR("Tutti i test"));
  codes.add(1); items.add(TR("Lettura sequenziale tabella"));
  codes.add(2); items.add(TR("Lettura cursore su chiave primaria"));
  codes.add(3); items.add(TR("Lettura cursore con ordinamento fuori chiave"));
  codes.add(4); items.add(TR("Lettura causale con cache"));
  codes.add(5); items.add(TR("Lettura causale senza cache"));
  codes.add(6); items.add(TR("Creazione e cancellazione records"));
  codes.add(7); items.add(TR("Creazione e cancellazione records in modo esclusivo"));
  codes.add(8); items.add(TR("Confronto TAssoc_array THash_table"));
  add_radio (101, 0, TR("@bSelezione test"),     1, 1, 58, codes, items);
  add_number(102, 0, PR("Numero di iterazioni"), 1, 0,  3, "U");

}

///////////////////////////////////////////////////////////
// Testfile
///////////////////////////////////////////////////////////

class TTest_application : public TSkeleton_application
{
  WINDOW _pi;
  long _total;
  clock_t _start;

  void start_progind(long items, const char* prompt);
  bool update_progind(long item);
  clock_t stop_progind();

protected:
  bool test1(TLog_report& log);
  bool test2(TLog_report& log);
  bool test3(TLog_report& log);
  bool test4(TLog_report& log);
  bool test5(TLog_report& log);
  bool test6(TLog_report& log);
  bool test7(TLog_report& log);
  bool test8(TLog_report& log);
 
public:
  bool test(int n, TLog_report& log);
  virtual void main_loop();

  TTest_application() : _pi(NULL) { }
};

void TTest_application::start_progind(long items, const char* msg)
{
  CHECK(_pi == NULL_WIN, "Double progress indicator");
  _pi = xvt_dm_progress_create(NULL_WIN, TR("Performance profiler"), _total = items, TRUE);
  xvt_dm_progress_set_text(_pi, msg);
  clock_t t = clock();
  while (clock() == t);
  _start = clock();
}

bool TTest_application::update_progind(long item)
{
  return xvt_dm_progress_set_status(_pi, item, _total) != 0;
}

clock_t TTest_application::stop_progind()
{
  const clock_t t = clock();
  if (_pi != NULL)
  {
    xvt_dm_progress_destroy(_pi);
    _pi = NULL_WIN;
  }
  return t - _start;
}


bool TTest_application::test1(TLog_report& log)
{          
  bool ok = true;
  TLocalisamfile tab(LF_COMUNI);
  
  TRecnotype r = 0;
  TString80 msg;
	
  const int times = 10;
	start_progind(times, TR("Lettura file comuni"));
  for (int i = 1; i <= times; i++)
  {
	  for (tab.first(); tab.good(); tab.next())
      r++;

		if (!update_progind(i)) 
    {
      log.log(1, TR("Interrotto dall'utente"));
      ok = false;
      break;
    }
  }
	
	const clock_t t = stop_progind();
	msg.format("Lettura file comuni - "
             "%ld records in %ld msec - %lg records per sec", r, t, 1000.0*r/t);             
	log.log(0, msg);
	return ok;
}

bool TTest_application::test2(TLog_report& log)
{            
  bool ok = true;
  TRelation rel(LF_COMUNI);
  TCursor tab(&rel);

  const TRecnotype n = tab.items();

  tab.freeze();
  
  TRecnotype r = 0;
  TString80 msg;
  const int times = 10;
  start_progind(times, TR("Lettura cursore comuni"));
	{
    for (int i = 1; i <= times; i++)
    {
		  for (tab = 0; tab.pos() < n; ++tab)
        r++;
		  
		  if (!update_progind(i))
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
	}
	const clock_t t = stop_progind();
  msg.format("Lettura cursore comuni: %ld records in %ld msec - %lg records per sec\n", 
             r, t, 1000.0*r/t);             
	log.log(0, msg);
	return ok;
}

bool TTest_application::test3(TLog_report& log)
{            
  bool ok = true;
  TRelation rel(LF_COMUNI);
  TSorted_cursor tab(&rel, "CAPCOM|DENCOM");

  start_progind(1, TR("Inizializzazione"));
  const TRecnotype n = tab.items();
  const clock_t istop = stop_progind();

  tab.freeze();
  
  TRecnotype r = 0;
  TString256 msg;
  const int times = 10;
  start_progind(times, TR("Lettura cursore C.A.P."));
	{
    for (int i = 1; i <= times; i++)
    {
		  for (tab = 0; tab.pos() < n; ++tab)
        r++;
		  
			if (!update_progind(i)) 
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
	}
	const clock_t t = stop_progind();
	msg.format("Lettura cursore ordinato %ld records in %ld msec - %lg records per sec - "
             "%ld msec for initialization", 
             r, t, 1000.0*r/t, istop);             
	log.log(0, msg);
	return ok;
}

bool TTest_application::test4(TLog_report& log)
{            
  bool ok = true;
  long r = 0;
  srand(r);
  TString8 cod;

	{
    const int times = 10;
    cache().get(LF_COMUNI, cod);  // Inizializzazione
		start_progind(times, TR("Lettura casuale tramite cache"));
    for (int i = 1; i <= times; i++)
    {
      for (long j = 0; j < 10000; j++)
      {
        const int rn = rand();
        cod.format(" |%c%03d", 'A'+(rn%2), rn % 1000);
        cache().get(LF_COMUNI, cod);
        r++;
      }
			if (!update_progind(i))
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
	}

	const clock_t t = stop_progind();
  TString msg;
	msg.format("Lettura cached di %ld records in %ld msec - %lg records per sec", 
             r, t, 1000.0*r/t);             
	log.log(0, msg);
	return ok;
}

bool TTest_application::test5(TLog_report& log)
{            
  bool ok = true;
  long r = 0;
  srand(r);
  TString8 cod;

  const int times = 10;
  TLocalisamfile f(LF_COMUNI);
	start_progind(times, TR("Lettura casuale senza cache"));
  for (int i = 1; i <= times; i++)
  {
    for (long j = 0; j < 10000; j++)
    {
      const int rn = rand();
      cod.format("%c%03d", 'A'+(rn%2), rn % 1000);
      f.put(COM_COM, cod);
      f.read();
      r++;
    }
		if (!update_progind(i)) 
    {
      log.log(1, TR("Interrotto dall'utente"));
      ok = false;
      break;
    }
  }

  const clock_t t = stop_progind();
  TString msg;
	msg.format("Lettura casuale di %ld records in %ld msec - %lg records per sec", 
             r, t, 1000.0*r/t);             
	log.log(0, msg);
	return ok;
}

bool TTest_application::test6(TLog_report& log)
{
  bool ok = true;
  TString msg;
  TRecnotype n = 100;

  if (n > 0)
  {
    TTable tab("CZZ");
 	  msg.format("Creazione di %ld records", n);
 	  start_progind(n, msg);
    int i;
    for (i = 1; i <= n; i++)
    {
      tab.put("CODTAB", i);
      tab.write();
			if (!update_progind(i)) 
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
    const clock_t t = stop_progind();
    msg.format("Scritti %ld records in %ld msec - %lg records per sec", i, t, 1000.0*i/t);             
	  log.log(0, msg);
  }
  
  if (n > 0)
  {
    TRelation rel("CZZ");
    TCursor cur(&rel);
    n = cur.items();
    cur.freeze();

   	msg.format("Cancellazione %ld records", n);
		start_progind(n, msg);
    for (cur = 0; cur.pos() < n; ++cur)
    {
      cur.file().remove();
			if (!update_progind(cur.pos()+1)) 
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
	  const clock_t t = stop_progind();
	  msg.format("Cancellati %ld records in %ld msec - %lg records per sec", n, t, 1000.0*n/t);             
	  log.log(0, msg);
  }
	return ok;
}

bool TTest_application::test7(TLog_report& log)
{
  bool ok = true;
  TString msg;
  TRecnotype n = 10000;

  if (n > 0)
  {
    TSystemisamfile tab(LF_TAB);
    tab.open(_excllock);
 	  msg.format("Creazione di %ld records", n);
 	  start_progind(n, msg);
    int i;
    for (i = 1; i <= n; i++)
    {
      tab.curr().put("COD", "CZZ");
      tab.curr().put("CODTAB", i);
      tab.write();
			if (!update_progind(i)) 
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
    const clock_t t = stop_progind();
    msg.format("Scritti %ld records in %ld msec - %lg records per sec", i, t, 1000.0*i/t);             
	  log.log(0, msg);
    tab.close();
  }
  
  if (n > 0)
  {
    TSystemisamfile tab(LF_TAB);
    tab.open(_excllock);

    TRelation rel("CZZ");
    TCursor cur(&rel);
    n = cur.items();
    cur.freeze();

   	msg.format("Cancellazione %ld records", n);
		start_progind(n, msg);
    for (cur = 0; cur.pos() < n; ++cur)
    {
      tab.put("COD", cur.curr().get("COD"));
      tab.put("CODTAB", cur.curr().get("CODTAB"));
      tab.remove();
			if (!update_progind(cur.pos()+1)) 
      {
        log.log(1, TR("Interrotto dall'utente"));
        ok = false;
        break;
      }
    }
	  const clock_t t = stop_progind();
	  msg.format("Cancellati %ld records in %ld msec - %lg records per sec", n, t, 1000.0*n/t);             
	  log.log(0, msg);
    tab.close();
  }
	return ok;
}

static bool str_deserialize(istream& i, TString& str)
{
  char* buff = str.get_buffer(50);
  bool in_string = false;
  while (!i.eof())
  {
    char c = '\0'; i.get(c);
    if ((c > '\0' && c < '0') || (c > '9' && c < '@') || (c>'Z' && c < 'a'))
    {
      if (in_string)
        break;
    }
    else
    {
      *buff = c;
      buff++;
      in_string = true;
    }
  }
  *buff = '\0';
  return in_string && str.full();
}

bool TTest_application::test8(TLog_report& log)
{
  static TArray parole(1024*1024);

  TString msg;

  const bool first_time = parole.empty();
  if (first_time) 
  {
    TString80 str;

    start_progind(1, TR("Lettura big.txt con TArray"));
    ifstream big("recdesc/big.txt");
    if (big.good())
    {
      while (str_deserialize(big, str))
      {
        TString* s = str.len() <= 16 ? new TString16(str) : new TString(str);
        parole.add(s);
        update_progind(1); 
      }
      const clock_t t = stop_progind();
      const unsigned int n = parole.items();
      msg.format("TArray %ld parole %6ld msec - %5lg wps", n, t, 1000.0*n/t);
      log.log(0, msg);
    }
  }
  
  const int n = parole.items();
  bool ok = n > 0;

  if (ok)
  {
    THash_table ash(883);
    start_progind(1, TR("Lettura big.txt con THash_table"));
    for (int i = 1; i < n; i++)
    {
      const TString& p = (const TString&)parole[i];
      ash.add(p, p, false);
      if ((i % 10000 == 0) && !update_progind(1))
      {
        ok = false;
        break;
      }
    }
	  const clock_t t = stop_progind();
	  msg.format("THash_table  %ld parole %6ld msec - %5lg wps", ash.items(), t, 1000.0*n/t);
    log.log(0, msg);
  }
  if (ok)
  {
    TAssoc_array ass;
    start_progind(1, TR("Lettura big.txt con TAssoc_array"));
    for (int i = 1; i < n; i++)
    {
      const TString& p = (const TString&)parole[i];
      ass.add(p, p, false);
      if ((i % 10000 == 0) && !update_progind(1))
      {
        ok = false;
        break;
      }
    }
	  const clock_t t = stop_progind();
	  msg.format("TAssoc_array %ld parole %6ld msec - %5lg wps", ass.items(), t, 1000.0*n/t);
    log.log(0, msg);
  }

	if (!ok) 
    log.log(1, TR("Interrotto dall'utente"));

  return ok;
}

bool TTest_application::test(int n, TLog_report& log)
{
  bool ok = false;
  switch (n)
  {
  case  1: ok = test1(log); break;
  case  2: ok = test2(log); break;
  case  3: ok = test3(log); break;
  case  4: ok = test4(log); break;
  case  5: ok = test5(log); break;
  case  6: ok = test6(log); break;
  case  7: ok = test7(log); break;
  case  8: ok = test8(log); break;
  default: ok = false;   break;
  }
  return ok;
}


void TTest_application::main_loop()
{
  int te = -1;
  int ti = 3;
  
  for (int a = argc()-1; a > 1; a--) 
  {
    const TFixed_string t(argv(a));
    if (t.starts_with("test="))
      te = atoi(t.mid(5)); else
    if (t.starts_with("times="))
      ti = atoi(t.mid(6));
  }

  const bool interactive = te < 0;

  while (true)
  {
    if (interactive)
    {
      TProfiler_mask m;
      m.set(101, te);
      m.set(102, ti);
      if (m.run() == K_QUIT)
        break;
      te = m.get_int(101);
	    ti = m.get_int(102);
      if (ti <= 0) 
        ti = 1;
    }

    TLog_report log(TR("Report"));

	  bool ok = true;
	  for (int t = 1; ok && t <= 8; t++)
	  {
      if (te <= 0 || te == t)
      {
        log.log(0, "");
        for (int i = 0; ok && i < ti; i++)
		      ok = test(t, log);
	    }
	  }

    if (interactive)
      log.preview();
    else
      break;
  }
}

///////////////////////////////////////////////////////////

int ba1200(int argc, char** argv)
{
  TTest_application a;
  a.run(argc, argv, TR("Performance profiler"));
  return 0;
}