//===-- rosa/agent/State.hpp ----------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/State.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *state* *functionality*.
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_AGENT_STATE_HPP
#define ROSA_AGENT_STATE_HPP

#include "rosa/agent/Functionality.h"
#include "rosa/agent/History.hpp"

namespace rosa {
namespace agent {

/// \tparam DIN type of data of input, \tparam DDAB type of data in which DABs are saved,
/// \tparam DOUT type of data of abstracted value
template <typename DIN, typename DDAB, typename DOUT> class State : public Functionality {
	
private:

  unsigned int StateId;

  DynamicHistory<DIN, HistoryPolicy::FIFO> SampleHistory;
  DynamicHistory<DIN, HistoryPolicy::SRWF> DAB;				
  DynamicHistory<DDAB, HistoryPolicy::LIFO> DABHistory;
  
  std::shared_ptr<PartialFunction<DIN, DOUT>> PartialFunctionSampleMatches;
  std::shared_ptr<PartialFunction<DIN, DOUT>> PartialFunctionSampleMismatches;
  std::shared_ptr<PartialFunction<DIN, DOUT>> PartialFunctionNumOfSamplesMatches;
  std::shared_ptr<PartialFunction<DIN, DOUT>> PartialFunctionNumOfSamplesMismatches;
  
  bool StateIsValid;
  bool StateIsValidAfterReentrance;
  
public:
  
  State(unsigned int StateId, unsigned int sampleHistorySize, unsigned int DABSize, 
        unsigned int DABHistorySize, PartialFunction* PartialFunctionSampleMatches,
		PartialFunction* PartialFunctionSampleMismatches,
		PartialFunction* PartialFunctionNumOfSamplesMatches,
		PartialFunction* PartialFunctionNumOfSamplesMismatches) noexcept : 
		SampleHistory(sampleHistorySize),
        DAB(DABSize),
        DABHistory(DABHistorySize) {
		  this->StateId = StateId;
		  this->PartialFunctionSampleMatches = PartialFunctionSampleMatches;
		  this->PartialFunctionSampleMismatches = PartialFunctionSampleMismatches;
		  this->PartialFunctionNumOfSamplesMatches = PartialFunctionNumOfSamplesMatches;
		  this->PartialFunctionNumOfSamplesMismatches = PartialFunctionNumOfSamplesMismatches;
		  StateIsValid = false;
          StateIsValidAfterReentrance = false;
		}

  //TODO: static assert -> check:
  // 1) if DIN == arithmetic,  
  // 2) if DDAB == float, double, ...?
  // 3) output could be arithemtic or char/string, right?
  
  ~State(void) = default;
  
  void leaveState(void) {
	//QUESTION: is it important to check first if DAB is empty?
	if(!DAB.empty()) {
	  DAB.clear();	
	}
	StateIsValidAfterReentrance = false;
  }

  
  bool insertSample(D Sample) {
	
	bool workedForAll;
	
	workedForAll = SampleHistory.addEntry(Sample);
	
	//QUESTION: is it important to have this flag (workedForAll). What should I do if it does not work at some point?
	if (workedForAll) {
	  workedForAll &= DAB.addEntry(Sample);
	  if (workedForAll) {
	    if (numberOfEntries >= DAB) {
	      DOUT AvgOfDAB = template average<DOUT>();
		  
		  workedForAll &= DABHistory.addEntry(AvgOfDAB);
			
		  if(workedForAll) {
			DAB.clear();
		  } //QUESTION: - what should be done if it has not worked?
        }
		
		if(workedForAll) {
		  //TODO: calculate whether state is valid	
		}
	  }		
	}
	
	return workedForAll;
  }
  
  DOUT confSampleMatchesState (DIN sample) {
	  
	  
  }
  DOUT confSampleMismatchesState () {
	  
	  
  }
  
  unsigned int stateId(void) { return StateId; }
  
  
  
};
		
} // End namespace agent
} // End namespace rosa

#endif // ROSA_AGENT_STATE_HPP