#ifndef SORT_FILE_DYNAMIC_H
#define SORT_FILE_DYNAMIC_H

#include <stdint.h>
#include <iostream>
#include "file.h"
#include "slot.h"
#include "param.h"

using namespace std;

namespace hierarchy_memory
{
  namespace sort_file
  {
    /*
     * File, sort by delay with a dynamic size
     */
    
    template <class T>
    class Sort_File_Dynamic : public File<T>
    {
      // *****[ variables ]*****
    protected : uint32_t       ptr_read ; // pointer of the next slot to read
    protected : uint32_t       ptr_write; // pointer of the next slot to write
    private   : uint32_t       factor   ;
    private   : const uint32_t size_base;
      // *****[ constructor ]*****
    public : Sort_File_Dynamic (param_t param) : File <T> (param.name,param.size), size_base (param.size)
      {
	factor = 1;
      };
      
      // *****[ destructor ]*****
    public : ~Sort_File_Dynamic ()
      {
      };
      
      // *****[ must_decrease ]*****
      // Test if the file must be decrease
    private : bool must_decrease (void)
      {
	return ( (factor                         > 1             ) &&
		 (((100*File <T>::nb_slot)/File <T>::size) <= 33));
      }

      // *****[ must_increase ]*****
      // Test if the file must be increase
    private : bool must_increase (void)
      {
	return (((100*File <T>::nb_slot)/File <T>::size) >= 90);
      }

      // *****[ increase ]*****
      // increase the file
    private : void increase (void)
      {
	factor ++;
	change_size (factor*size_base);
      }
      
      // *****[ decrease ]*****
      // decrease the file
    private : void decrease (void)
      {
	factor --;
	change_size (factor*size_base);
      }

      // *****[ change_size ]*****
      // decrease the file
    private : void change_size (uint32_t new_size)
      {
	uint32_t    old_size = File <T>::size;
	slot_t<T> * new_slot = new slot_t<T> [new_size];

	// copy data
	for (uint32_t it = 0; it < File <T>::nb_slot; it ++)
	  {
	    uint32_t ptr = (ptr_read+it)%old_size;
	    
	    new_slot [it] = File <T>::slot [ptr];
	  }
	// Change information
	delete File <T>::slot;
	File <T>::slot = new_slot;
	File <T>::size = new_size;
	ptr_write      = File <T>::nb_slot;
	ptr_read       = 0;
      }
      
      // *****[ reset ]*****
      // Reset the file in the empty state
    public : void reset (void)
      {
	ptr_read  = 0;
	ptr_write = 0;
	File <T>::nb_slot   = 0;
	//nb_slot   = 0;
      };
      
      // *****[ transition ]*****
      // Decrease the delay at all cycle
    public : void transition ()
      {
	for (uint32_t it = 0; it < File <T>::nb_slot; it ++)
	  {
	    uint32_t ptr = (ptr_read + it)%File <T>::size;
	    
	    if (File <T>::slot[ptr].delay != 0)
	      File <T>::slot[ptr] .delay --;
	  }
      }
      
      // *****[ read ]*****
      // return the n-eme slot.
      // (first = 0)
    public : slot_t<T> read (uint32_t num)
      {
	if (num >= File <T>::nb_slot)
	  {
	    cerr << "<Sort_File_Dynamic.read> {ERROR} can't read because : num (" << num << ") >= nb_slot (" << File <T>::nb_slot << ")" << endl;
	    exit(1);
	  }

	return File <T>::slot[(ptr_read + num)%File <T>::size];
      };
      
      // *****[ pop ]*****
      // read the file, and update the pointer
    public : T pop  ()
      {
	return pop (0);
      }

      // *****[ pop ]*****
      // read the file, and update the pointer
    public : T pop  (uint32_t num)
      {
	if (num >= File <T>::nb_slot)
	  {
	    cerr << "<Sort_File_Dynamic.pop> {ERROR} can't read because : num (" << num << ") >= nb_slot (" << File <T>::nb_slot << ")" << endl;
	    exit(1);
	  }

	// If full -> quit
	if (must_decrease() == true)
	  decrease();


	T val = File <T>::slot [(ptr_read+num)%File <T>::size].data;
	
	// Reorganize the file

 	for (uint32_t it = 0; it < num; it ++)
	  {
	    uint32_t ptr      = (ptr_read + num - it   )%File <T>::size;
	    uint32_t ptr_next = (ptr-1)%File <T>::size;

	    File <T>::slot [ptr] = File <T>::slot [ptr_next];
	  }

	File <T>::nb_slot --;
	ptr_read = (ptr_read+1)%File <T>::size;
	
	return val;
      }
      
      // *****[ push ]*****
      // Push a new value (they must have a slot free)
      // Is sort by delay
    public : bool push (uint32_t delay, T val)
      {
	// If full -> quit
	if (must_increase() == true)
	  increase();

	uint32_t ptr = ptr_write;
	
	// Scan the fill to find the good position (keep the sort)
	for (uint32_t it = 0; it < File <T>::nb_slot; it ++)
	  {
	    uint32_t ptr_scan = (ptr != 0)?ptr:(File <T>::size);
	    ptr_scan --;

	    if (File <T>::slot[ptr_scan].delay <= delay)
	      break; //find
	    
	    // reformor the file
	    File <T>::slot [ptr] = File <T>::slot [ptr_scan];
	    ptr                  = ptr_scan;
	  }

	File <T>::slot [ptr] = slot_t <T> (delay,val);
	ptr_write            = (ptr_write+1)%File <T>::size; // update the pointer
	File <T>::nb_slot ++;
	
	return true;
      }
      // *****[ print ]*****
    public    : friend ostream& operator<< (ostream& output_stream, const Sort_File_Dynamic & x)
      {
	output_stream << "<" << x.name << ">" << endl;
	output_stream << " * ptr_read    : " << x.ptr_read  << endl;
	output_stream << " * ptr_write   : " << x.ptr_write << endl;
	output_stream << " * nb_slot     : " << x.nb_slot   << endl;
	output_stream << " * size        : " << x.size      << endl;
	output_stream << " * factor      : " << x.factor    << endl;
	
	for (uint32_t it = 0; it < x.nb_slot; it ++)
	  {
	    uint32_t ptr = (x.ptr_read+it)%x.size;
	    output_stream << x.slot [ptr] << endl;
	  }
	
	return output_stream;
      };
      

    };
  }; //sort_file
}; //hierarchy_memory
#endif //!SORT_FILE_DYNAMIC_H
  
