#pragma once
#include <iosfwd>
#include <string>
#include <cstdint>
#include <chrono>
#include <expected>
#include <concepts>
#include "NetMessengerStreamBuffer.h"

namespace Aftr
{

   //See SensorDatum.h and SensorDatum.cpp for a simple "howto" usage example. This Concept class 
   //specifies the concepts and helpful functions to make the SensorDatum derivatives easy-to-use.

   struct ParseError; //lightweight error returned when parsing fails, it contains an int and a string.
   //The most useful type is probably the string, the int may not always be used but
   //it is there (defaults to -1).

   //Easy utility function that report a human readable error message.
   std::optional< Aftr::ParseError > validateInputStream( std::istream& is, std::string const& location );// { return is.rdstate() == 0; }
   std::optional< Aftr::ParseError > validateInputStream( Aftr::NetMessengerStreamBuffer const& is, std::string const& location );    // { return is.rdstate() == 0; }
   std::ostream& operator<<( std::ostream& os, Aftr::ParseError const& err );

   //Defines a concept for any type that, at compile time, fulfils the interface of a
   //SensorDatum. This could be done w/ inheritance, but dispatching on a virtual interface
   //when parsing millions of these datums isn't necessary, especially when every use case of
   //these SensorDatum types are specialized and the exact type is a template parameter known
   //at compile time.
   //A user ought to copy / paste the SensorDatum from below, change its name to SensorDatum_SuperSpecialSensor
   //and adjust the body of all member functions accordingly.
   template< typename T >
   concept SensorDatumConcept = requires(T t, T const& tconst, //t and tconst are the same type, just one is const
      std::istream& is,
      NetMessengerStreamBuffer& isn,
      std::ostream& os,
      std::chrono::microseconds offset)
   {
      {  T::fromStreamOneDatum( is )    } -> std::convertible_to< std::expected<T, ParseError > >; //static member function
      {  T::fromStreamOneDatum( isn )   } -> std::convertible_to< std::expected<T, ParseError > >; //static member function
      //To stream should terminate each datum with a \n newline
      {  tconst.toStreamOneDatum( os )  } -> std::convertible_to< bool >; //member function must be const!
      {  T::getHeaderDescribingData()   } -> std::convertible_to< std::string >; //static member function
      {  tconst.getTime()               } -> std::convertible_to< std::chrono::utc_clock::time_point >; //const member func
   };

   struct ParseError
   {
      int code = -1;
      std::string str;
      friend std::ostream& operator<< ( std::ostream&, ParseError const& );
   };


} //namespace Aftr

