#include "output.h"
#include "binaryrw.h"
#include <math.h>
#include <utility>
#include <algorithm>

Output::Output() : mute_flags(0)
{
}


Output::~Output()
{
}


void Output::init_start(int nodes)
{
  
  patches = nodes;
  
  lifetime_bins_size = 0;
  
}


void Output::create_new_files()
{
  
     
  for(int i = 0; i < OUT_FILE_COUNT; i++)
  {
    file[i].open(names[i].c_str());
    file[i].close();
    
  }
  
  
  ofstream parWriter;
  parWriter.open(names.back().c_str());
  parWriter.close();
  
}
  

void Output::create_file_names(string path)
{
  
  string c[] = {"SAR", "steps", "distribution", "starter", "lifetime"};
  for(int i = 0; i < OUT_FILE_COUNT; i++)
  {
    string name = path + string("/hm_") + c[i] + string(".out");
    names.push_back(name);
    
    
  }
  
  string name = path + string("/hm_par.out");
  names.push_back(name);
  
}


void Output::open_files()
{
  for(int i = 0; i < OUT_FILE_COUNT; i++)
  {
        
    // Testen, ob die Ausgabe unterdrückt wurde:
    if(muted(resfile_type(i)) || file[i].is_open())
      continue;
    file[i].open(names[i].c_str(), ios::out | ios::app);
  }
  
}


void Output::close_files()
{
  for(int i = 0; i < OUT_FILE_COUNT; i++)
  {
    // Testen, ob die Ausgabe unterdrückt wurde:
    if(muted(resfile_type(i)) || !file[i].is_open())
      continue;
    file[i].close();

  }
  
}


void Output::open_file(resfile_type f)
{
  if(muted(f) || file[f].is_open())
      return;
  
  file[f].open(names[f].c_str(), ios::out | ios::app);
  
}

void Output::close_file(resfile_type f)
{
  if(muted(f) || !file[f].is_open())
      return;
    
  file[f].close();
  
}


void Output::mute(resfile_type f)
{
  mute_flags |= (0x01 << (unsigned int)f);
}

bool Output::muted(resfile_type f)
{
  // Testen, ob die Ausgabe unterdrückt wurde:
  return mute_flags & (0x01 << (unsigned int)f);
}

void Output::unmute(resfile_type f)
{
  if(mute_flags & (0x01 << (unsigned int)f))
    mute_flags -= (0x01 << (unsigned int)f);
}


void Output::update_bins(sll lifetime)
{
  if(muted(OUT_LIFETIME))
    return;
  
  if(lifetime == 0)
  {
    cout << "update_bins(sll lifetime) mit lifetime = 0" << endl;
    return;
  }
    
  int curr = ceil(log10(lifetime)*50);				//50 bestimmt die BinSize
  
  if(curr < lifetime_bins_size)
    lifetime_bins[curr]++;
  
  else
  {
    for(int i = 0; i < curr - lifetime_bins_size; i++)
      lifetime_bins.push_back(0);
    
    lifetime_bins.push_back(1);
    lifetime_bins_size = curr+1;
    
  }
  
}


// Output lifetime
void Output::print_line(resfile_type f)
{
  // Testen, ob die Ausgabe unterdrückt wurde:
  if(mute_flags & (0x01 << (unsigned int)f))
    return;
  
  bool opend = file[f].is_open();
  
  if(!opend)
    file[f].open(names[f].c_str(), ios::out | ios::app);
   
  for(int i = 0; i < lifetime_bins_size-1; i++)
    file[f] << lifetime_bins[i] << "\t";
  
  if(lifetime_bins_size > 0)
    file[f] << lifetime_bins[lifetime_bins_size-1];
      
  file[f] << endl;

  if(!opend)
    file[f].close();
  
  return;  
}


//Output Steps, Starter
void Output::print_line(resfile_type f, sll info)
{
  // Testen, ob die Ausgabe unterdrückt wurde:
  if(mute_flags & (0x01 << (unsigned int)f))
    return;
  
  bool opend = file[f].is_open();
  
  if(!opend)
    file[f].open(names[f].c_str(), ios::out | ios::app);
  
  file[f] << info << endl;
  
  if(!opend)
    file[f].close();
}


//Output SAR  //Neue Variante mit hash table
void Output::print_line(resfile_type f, Species**** webs, int websize, sll liv)
{
  // Testen, ob die Ausgabe unterdrückt wurde:
  if(mute_flags & (0x01 << (unsigned int)f))
    return;
  
  bool opend = file[f].is_open();
  
  if(!opend)
    file[f].open(names[f].c_str(), ios::out | ios::app);

    
  // (dx, dy) is a vector - direction in which we move right now
  int dx = 1;
  int dy = 0;
  // length of current segment
  int segment_length = 1;
  int segment_passed = 0;

  // current position (x,y) and how much of current segment we passed
  int x = floor((sqrt(patches)-1)/2);
  int y = floor((sqrt(patches)-1)/2);
  
  
  
  
  sll species = 0;
  
  //vector<sll> hashtable[524288];                                               // 16777216 = 2^24
  //vector<sll> hashtable[16777216];                                             // 16777216 = 2^24
  
  sll hash_table_size = (sll) pow(2.0 , ceil( log2( min(patches * websize * 0.2, (double) liv) ) ) );
  
  vector<sll>* hashtable = new vector<sll>[hash_table_size];
  
  
  for(int k = 0; k < patches; k++)                                             
  {
    
    for(int i = 0; i < websize; i++)
    {
      sll curr = webs[x][y][i]->first_occurrence;
      int key = curr & (hash_table_size-1);                                               // x mod 16777216 = x & (16777216-1) = x & 16777215
      bool nomatch = true;
      
      //cout << key << endl;
      
      for(int j = 0; j < hashtable[key].size(); j++)
      {
        if(hashtable[key][j] == curr)
        {
          nomatch = false;
          break;
        }
      }
      
      if(nomatch)
      {
        hashtable[key].push_back(curr);
        species++;
        
      }
      
    }
        
    if((segment_passed == segment_length-1)&&(dy == 0))                        //Bedingungen für eine Quadratzahl
    {
      if(k == 0)
        file[f] << species;                                                    //Ersten Wert der SAR ausgeben
      else
        file[f] << "\t" << species;                                            //Nächsten Wert der SAR ausgeben
    }
      
    // make a step, add 'direction' vector (dx, dy) to current position (x,y)
    x += dx;
    y += dy;
    segment_passed++;
      
    if(segment_passed == segment_length)
    {
      // done with current segment
      segment_passed = 0;

      // 'rotate' directions
      int buffer = dx;
      dx = -dy;
      dy = buffer;
      
      // increase segment length if necessary
      if(dy == 0)
        segment_length++;
        
    }   
      
    
  }
  
  file[f] << endl;
  
  delete[] hashtable;
  
  if(!opend)
    file[f].close();
}


//Output distribution
void Output::print_line(resfile_type f, int maxS, int* species_count)
{
  
  // Testen, ob die Ausgabe unterdrückt wurde:
  if(mute_flags & (0x01 << (unsigned int)f))
    return;
  
  bool opend = file[f].is_open();
  
  if(!opend)
    file[f].open(names[f].c_str(), ios::out | ios::app);
  
  
  file[f] << species_count[0];
  
  for(int i = 1; i < maxS; i++)
  {
    if(species_count[i] > 0)
      file[f] << "\t" << species_count[i];
  }
  
  file[f] << endl;
  
  if(!opend)
    file[f].close();
}


  
void Output::save_output(string save_dir)
{
  string name = save_dir + string("/output.hm");
  
  ofstream writerB;
  writerB.open(name.c_str(), ios::out | ios::binary);
  
  bwrite(writerB, patches);					//Übergabe der Länge der nachfolgenden Vektoren
   
  bwrite(writerB, lifetime_bins_size);				//Übergabe der Länge des nachfolgenden Vektors
  
  for(int i = 0; i < lifetime_bins_size; i++)
    bwrite(writerB, lifetime_bins[i]);				//Datentyp: sll

    
  writerB.close();
    
  
}


bool Output::load_output(string save_dir, int nodes)
{
    
  patches = nodes;
    
  string name = save_dir + string("/output.hm");
  
  ifstream readerB (name.c_str(), ios::in | ios::binary);
  
  if(readerB.is_open())
  {
    int length;
    bread(readerB, length);
    
    if(patches != length)
    {
      cout << "Fehler beim lesen der Datei 'output.hm'!" << endl;
      cout << "Anzahl Knoten stimmt nicht!" << endl;
      return false;
    }

    bread(readerB, lifetime_bins_size);
    
    for(int i = 0; i < lifetime_bins_size; i++)
    {
      sll info_bins;
      bread(readerB, info_bins);
      lifetime_bins.push_back(info_bins);
    }
    
    
    readerB.close();
    
  }
  else
  {    
    cout << "Fehler beim lesen der Datei 'output.hm'!" << endl;
    return false;
  }    
  
  return true;    
  
}










