#pragma once
#include <string>
#include <string_view>
#include <optional>
#include <array>
#include <cstdint>
#include <concepts>

namespace Aftr
{
   //These methods were originally written to parse GenICam GigE compliant messages where the
   //primitives are bools, int64s, doubles, and std::strings. So this converts 48 bit mac
   //address and IP Address into handy formats for displaying, converting, and resending.
   //For example usage, see AftrUtil_net_test.cpp at:
   //"...\aburn\engine\src\aftr\gtest\AftrUtil_net_test.cpp"

   //---------------- BEGIN MAC Address methods
   //Output will look like A8-5E-45-E1-8F-16 (each octet will be two hex digits like 04-5E-45-E1-00-01
   std::string                toStringMACAddr( std::int64_t mac48bit ); //only considers lower 48 bits
   std::string                toStringMACAddr( std::array< std::byte, 6 > const& mac48bit );

   //Valid sample inputs:
   //A8-5E-45-E1-8F-16 (upper or lower case)
   //A8:5E:45:E1:8F:16
   //A85E45E18F16
   //0xA85E45E18F16
   std::optional< std::array< std::byte, 6 > > fromStringMACAddr_toBytes( std::string const& macAddr );
   std::optional<std::int64_t>                 fromStringMACAddr_toI64( std::string const& macAddr );

   std::array< std::byte, 6 >                  toBytes_fromI64_MACAddr( std::int64_t bits );
   //Places the data in the lower 48 bits of the 64 bit integer
   std::int64_t                                toI64_MACAddr_fromBytes( std::array<std::byte, 6> const& bytes );
   //---------------- END MAC Address methods

   //---------------- BEGIN IPv4 Address methods
   //IP Address functions
   std::string                toStringIPv4Addr( std::int64_t ip32bits ); //only considers lower 32 bits if int64
   std::string                toStringIPv4Addr( std::array< std::byte, 4 > const& ip32bits );

   std::optional< std::array< std::byte, 4 > >  fromStringIPv4Addr_toBytes( std::string const& IPv4Addr );
   std::optional< std::int64_t>                 fromStringIPv4Addr_toI64( std::string const& IPv4Addr );

   std::array< std::byte, 4 >                  toBytes_fromI64_IPv4Addr( std::int64_t octets );
   std::int64_t                                toI64_IPv4Addr_fromBytes( std::array<std::byte, 4> const& bytes );
   //---------------- END IPv4 Address methods

   struct IP_PORT_sv
   {
      std::string_view ip;   //"127.0.0.1"
      std::string_view port; //"12683"
   };
   ///Parses "127.0.0.1:12683 to a string view where ip="127.0.0.1" and port="12683". *NO* allocation
   /// is performed - returns string_views refer into the in passed const ref string
   std::optional< IP_PORT_sv > parse_IP_PORT( std::string const& ip_port_colon_separated );

   //Endian Conversions for host to network and network to host. Based on boost's implemenation, but 
   //extended to support 64-bit values and beyond.
   //#include <boost/endian/conversion.hpp>
   //To CONVERT endian-ness from NETWORK to your CPU, do this:
   // Networks always send data in BIG ENDIAN. Native endian depends on your local CPU.
   //int64_t val = boost::endian::big_to_native( int64_t fromNetwork_int64_t );

   //To CONVERT endian-ness from your CPU to NETWORK, do this:
   // Networks always send data in BIG ENDIAN. Native endian depends on your local CPU.
   //int64_t val_big_endian_forNetwork = boost::endian::native_to_big( int64_t local_cpu_64_bit_int );
   //These method avoid strict type aliasing and are standards compliant (compared to type punning implementations)

   template< std::floating_point F > //for float or double
   F native_to_big( F native_endian_val ); /// converts val from native (local CPU) to big endian (for sending out on the network)

   template< std::floating_point F > //for float or double
   F big_to_native( F big_endian_val ); /// converts val from big endian (from a network) to native (local CPU) (such as little endian on Intel x86 / AMD x64)

   template< std::integral T > //for char, short, int, long, etc (all integral types)
   T native_to_big( T native_endian_val ); /// converts val from native (local CPU) to big endian (for sending out on the network)

   template< std::integral T > //for char, short, int, long, etc (all integral types)
   T big_to_native( T big_endian_val ); /// converts val from big endian (from a network) to native (local CPU) (such as little endian on Intel x86 / AMD x64)

} //namespace Aftr