//#####################################################################
// Copyright 2023, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#include <Core/Read_Write/READ_WRITE.h>
#include <Core/Log/DEBUG_UTILITIES.h>
#include <charconv>
#include <iomanip>
namespace PhysBAM
{
bool Test(UNTYPED_ISTREAM<char>& input,char a)
{
    input.raw.stream>>std::ws;
    return input.raw.stream.peek()==a;
}
bool Try_Get(UNTYPED_ISTREAM<char>& input,char a)
{
    if(!Test(input,a)) return false;
    input.raw.stream.get();
    return true;
}
void Get(UNTYPED_ISTREAM<char>& input,char a)
{
    char b;
    input.raw.stream>>std::ws>>b;
    PHYSBAM_ASSERT(a==b);
}
void Beg(UNTYPED_OSTREAM<char>& output,char a)
{
    if(output.raw.need_space) output.raw.stream<<' ';
    output.raw.stream<<a;
    output.raw.need_space=false;
}
void Sep(UNTYPED_OSTREAM<char>& output,char a,bool space)
{
    output.raw.stream<<a;
    output.raw.need_space=space;
}
bool Test(TYPED_ISTREAM& input,char a)
{
    if(input.type==STREAM_TYPE::type_text)
    {
        UNTYPED_ISTREAM<char> s(input.raw);
        return Test(s,a);
    }
    return true;
}
bool Try_Get(TYPED_ISTREAM& input,char a)
{
    if(input.type==STREAM_TYPE::type_text)
    {
        UNTYPED_ISTREAM<char> s(input.raw);
        return Try_Get(s,a);
    }
    return true;
}
void Get(TYPED_ISTREAM& input,char a)
{
    if(input.type==STREAM_TYPE::type_text)
    {
        UNTYPED_ISTREAM<char> s(input.raw);
        Get(s,a);
    }
}
void Beg(TYPED_OSTREAM& output,char a)
{
    if(output.type==STREAM_TYPE::type_text)
    {
        UNTYPED_OSTREAM<char> s(output.raw);
        Beg(s,a);
    }
}
void Sep(TYPED_OSTREAM& output,char a,bool space)
{
    if(output.type==STREAM_TYPE::type_text)
    {
        UNTYPED_OSTREAM<char> s(output.raw);
        Sep(s,a,space);
    }
}
//#####################################################################
// Function Read_Binary
//#####################################################################
template<class RW,class T> std::enable_if_t<std::is_arithmetic<T>::value&&!std::is_const<T>::value>
Read_Binary(UNTYPED_ISTREAM<RW>& input,T& d)
{
    if constexpr(!is_same<RW,char>::value)
    {
        if constexpr((is_same<T,float>::value || is_same<T,double>::value) && !is_same<T,RW>::value)
        {
            RW tmp;
            input.raw.stream.read(reinterpret_cast<char*>(&tmp),sizeof(tmp));
            d=tmp;
        }
        else input.raw.stream.read(reinterpret_cast<char*>(&d),sizeof(d));
    }
    else
    {
        if constexpr(is_same<T,char>::value)
        {
            input.raw.stream>>std::ws;
            if(input.raw.stream.peek()=='\'')
            {
                char a,b;
                input.raw.stream>>a>>d>>b;
                PHYSBAM_ASSERT(b=='\'');
            }
            else
            {
                int i;
                input.raw.stream>>i;
                d=i;
            }
        }
        else input.raw.stream>>d;
    }
}
//#####################################################################
// Function Write_Binary
//#####################################################################
template<class RW,class T> std::enable_if_t<std::is_arithmetic<T>::value>
Write_Binary(UNTYPED_OSTREAM<RW>& output,const T& d)
{
    if constexpr(!is_same<RW,char>::value)
    {
        if constexpr((is_same<T,float>::value || is_same<T,double>::value) && !is_same<T,RW>::value)
        {
            RW tmp=d;
            output.raw.stream.write(reinterpret_cast<const char*>(&tmp),sizeof(tmp));
        }
        else output.raw.stream.write(reinterpret_cast<const char*>(&d),sizeof(d));
    }
    else
    {
        if(output.raw.need_space) output.raw.stream<<' ';
        if constexpr(is_same<T,char>::value)
        {
            if(isprint(d)) output.raw.stream<<'\''<<d<<'\'';
            else output.raw.stream<<(int)d;
        }
        else if constexpr(is_integral<T>::value)
            output.raw.stream<<d;
        else
        {
            char buff[100];
            snprintf(buff,100,is_same<T,float>::value?"%.9g":"%.17g",d);
            output.raw.stream<<buff;
        }
        output.raw.need_space=true;
    }
}
//#####################################################################
// Function Read_Binary
//#####################################################################
template<class RW> void
Read_Binary(UNTYPED_ISTREAM<RW>& input,std::string& d)
{
    if constexpr(is_same<RW,char>::value)
        input.raw.stream>>std::quoted(d);
    else
    {
        int n;
        Read_Binary(input,n);
        char* buffer=new char[n];
        input.raw.stream.read(buffer,n);
        d.assign(buffer,buffer+n);
        delete[] buffer;
    }
}
//#####################################################################
// Function Write_Binary
//#####################################################################
template<class RW> void
Write_Binary(UNTYPED_OSTREAM<RW>& output,const std::string& d)
{
    if constexpr(is_same<RW,char>::value)
    {
        if(output.raw.need_space) output.raw.stream<<' ';
        output.raw.stream<<std::quoted(d);
        output.raw.need_space=true;
    }
    else
    {
        Write_Binary(output,(int)d.size());
        output.raw.stream.write(d.c_str(),d.size());
    }
}
template void Read_Binary(UNTYPED_ISTREAM<char>&,bool&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,char&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,double&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,float&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,int&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,long&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,short&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,std::string&);
template void Read_Binary(UNTYPED_ISTREAM<char>&,unsigned short&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,bool&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,char&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,double&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,float&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,int&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,long&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,short&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,std::string&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,unsigned char&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,unsigned int&);
template void Read_Binary(UNTYPED_ISTREAM<double>&,unsigned short&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,bool&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,char&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,double&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,float&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,int&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,long&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,short&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,std::string&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,unsigned char&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,unsigned int&);
template void Read_Binary(UNTYPED_ISTREAM<float>&,unsigned short&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,bool const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,char const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,double const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,float const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,int const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,long const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,short const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,std::string const&);
template void Write_Binary(UNTYPED_OSTREAM<char>&,unsigned short const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,bool const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,char const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,double const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,float const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,int const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,long const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,short const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,std::string const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,unsigned char const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,unsigned int const&);
template void Write_Binary(UNTYPED_OSTREAM<double>&,unsigned short const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,bool const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,char const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,double const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,float const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,int const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,long const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,short const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,std::string const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,unsigned char const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,unsigned int const&);
template void Write_Binary(UNTYPED_OSTREAM<float>&,unsigned short const&);
}
