#ifndef ENVIRONMENT_QUEUE_SORT_QUEUE_DYNAMIC_H
#define ENVIRONMENT_QUEUE_SORT_QUEUE_DYNAMIC_H

#include <stdint.h>
#include <iostream>
#include "Parameters.h"
#include "Queue.h"

namespace environment {
namespace queue {

#define PERCENT_USE_MAX 90
#define PERCENT_USE_MIN 33
  
    /*
     * Queue, sort by delay with a dynamic size
     */
    
    template <class T>
    class Sort_Queue_Dynamic : public Queue<T>
    {
      // *****[ variables ]*****
    private   : uint32_t _factor   ;
    private   : uint32_t _size_base;

      // *****[ constructor ]*****
    public : Sort_Queue_Dynamic (std::string name, Parameters * param) : Queue <T> (name, param->_size)
      {
	_size_base = param->_size;
	_factor    = 1;
      };
      
      // *****[ destructor ]*****
    public : ~Sort_Queue_Dynamic ()
      {
      };
      
      // *****[ must_decrease ]*****
      // Test if the queue must be decrease
    private : bool must_decrease (void)
      {
	return ( (_factor > 1 ) and
		 (((100*Queue <T>::_nb_slot)/Queue <T>::_size) <= PERCENT_USE_MIN));
      }

      // *****[ must_increase ]*****
      // Test if the queue must be increase
    private : bool must_increase (void)
      {
	return (((100*Queue <T>::_nb_slot)/Queue <T>::_size) >= PERCENT_USE_MAX);
      }

      // *****[ increase ]*****
      // increase the queue
    private : void increase (void)
      {
	_factor ++;
	change_size (_factor*_size_base);
      }
      
      // *****[ decrease ]*****
      // decrease the queue
    private : void decrease (void)
      {
	_factor --;
	change_size (_factor*_size_base);
      }

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

	// copy data
	for (uint32_t it = 0; it < Queue <T>::_nb_slot; it ++)
	  {
	    new_slot [it] = Queue <T>::_slot [it];
	  }
	// Change information

	delete [] Queue <T>::_slot;

	Queue <T>::_slot = new_slot;
	Queue <T>::_size = new_size;
      }
      
      // *****[ reset ]*****
      // Reset the queue in the empty state
    public : void reset (void)
      {
	Queue <T>::_nb_slot   = 0;
      };
      
      // *****[ transition ]*****
      // Decrease the delay at all cycle
    public : void transition ()
      {
	for (uint32_t it = 0; it < Queue <T>::_nb_slot; it ++)
	  {
	    if (Queue <T>::_slot[it]._delay != 0)
	      Queue <T>::_slot[it] ._delay --;
	  }
      }
      
      // *****[ read ]*****
      // return the n-eme slot.
      // (first = 0)
    public : slot_t<T> read (uint32_t num=0)
      {
	if (num >= Queue <T>::_nb_slot)
	  {
	    std::cerr << "<Sort_Queue_Dynamic.read> {ERROR} can't read because : num (" << num << ") >= _nb_slot (" << Queue <T>::_nb_slot << ")" << std::endl;
	    exit(1);
	  }

	return Queue <T>::_slot[num];
      };
      
      // *****[ pop ]*****
      // read the queue, and update the pointer
    public : T pop  (uint32_t num=0)
      {
	if (num >= Queue <T>::_nb_slot)
	  {
	    std::cerr << "<Sort_Queue_Dynamic.pop> {ERROR} can't read because : num (" << num << ") >= _nb_slot (" << Queue <T>::_nb_slot << ")" << std::endl;
	    exit(1);
	  }

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

	T val = Queue <T>::_slot [num]._data;
	
	// Reorganize the queue

 	for (uint32_t it = num; it+1 < Queue <T>::_nb_slot; it ++)
	  {
	    uint32_t ptr      = (it);
	    uint32_t ptr_next = (it+1);

// 	    std::cout << "ptr      : " << ptr << std::endl
// 		      << "ptr_next : " << ptr_next << std::endl
// 		      << "nb_slot  : " << Queue <T>::_nb_slot << std::endl;
	    
	    Queue <T>::_slot [ptr] = Queue <T>::_slot [ptr_next];
	  }

	Queue <T>::_nb_slot --;
	
	return val;
      }
      
      // *****[ push ]*****
    public : bool push (T val)
      {
	return push (0, val);
      }
      
      // Push a new value (they must have a slot free)
      // Is sort by delay
    public : bool push (uint32_t delay, T val)
      {
// 	std::cout << val << std::endl;

	// Test if must increase the queue (never full)
	if (must_increase() == true)
	  increase();

	uint32_t ptr = Queue <T>::_nb_slot;
	
	// Scan the fill to find the good position (keep the sort)
	for (uint32_t it = 0; it < Queue <T>::_nb_slot; it ++)
	  {
	    uint32_t ptr_scan = ptr-1; // ptr > 0

	    if (Queue <T>::_slot[ptr_scan]._delay <= delay)
	      break; //find
	    
	    // reformor the queue
	    Queue <T>::_slot [ptr] = Queue <T>::_slot [ptr_scan];
	    ptr                  = ptr_scan;
	  }

	Queue <T>::_slot [ptr] = slot_t <T> (delay,val);
	Queue <T>::_nb_slot ++;
	
	return true;
      }
      // *****[ print ]*****
    public    : friend std::ostream& operator<< (std::ostream& output, const Sort_Queue_Dynamic & x)
      {
	output << "<" << x._name << ">" << std::endl;
	output << " * nb_slot     : " << x._nb_slot   << std::endl;
	output << " * size        : " << x._size      << std::endl;
	output << " * factor      : " << x._factor    << std::endl;
	
	for (uint32_t it = 0; it < x._nb_slot; it ++)
	  {
	    output << x._slot [it] << std::endl;
	  }
	
	return output;
      };
      

    };
  
};
};
#endif
