//#####################################################################
// Copyright 2013.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class LOG_PRINTF
//##################################################################### 
#ifndef __LOG_PRINTF__
#define __LOG_PRINTF__
#include <Core/Log/DEBUG_UTILITIES.h>
#include <Core/Log/LOG.h>
#include <Core/Utilities/TYPE_UTILITIES.h>
#include <climits>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <stdexcept>
#include <type_traits>
namespace PhysBAM{
namespace LOG{
namespace LOG_PRINTF
{
struct OUT_STATE
{
    int width,precision,opts;
    std::ios::fmtflags flags;
    char fill;
    bool need_restore;
    std::ostream::pos_type pos;
    std::ostream& out;
    const char *format;
    
    OUT_STATE(std::ostream& out,const char *format);
    void Restore();
    int Number_Output();
};

void find_format(OUT_STATE& state);
void print_rest(OUT_STATE& state);

void print_argument(OUT_STATE& state,int data);
void print_argument(OUT_STATE& state,unsigned int data);
void print_argument(OUT_STATE& state,short data);
void print_argument(OUT_STATE& state,unsigned short data);
void print_argument(OUT_STATE& state,char data);
void print_argument(OUT_STATE& state,unsigned char data);
void print_argument(OUT_STATE& state,long data);
void print_argument(OUT_STATE& state,unsigned long data);
void print_argument(OUT_STATE& state,long long data);
void print_argument(OUT_STATE& state,unsigned long long data);
void print_argument(OUT_STATE& state,bool data);
void print_argument(OUT_STATE& state,const char* data);
void print_argument(OUT_STATE& state,int* data);

template<class T>
void print_argument(OUT_STATE& state,const T& data)
{
    PHYSBAM_ASSERT(!(state.opts&15));
    state.out<<data;
    state.Restore();
}
}

template<typename... Args>
int fprintf(std::ostream& out,const char *format,Args&&... args)
{
    LOG_PRINTF::OUT_STATE state(out,format);

    bool tmp[]={(LOG_PRINTF::find_format(state),LOG_PRINTF::print_argument(state,args),false)...};
    (void)tmp;
    LOG_PRINTF::print_rest(state);
    
    out<<std::flush;
    return state.Number_Output();
}

template<typename... Args>
int printf(const char *format,Args&&... args)
{
    return fprintf(LOG::cout,format,args...);
}

template<typename... Args>
std::string sprintf(const char *format,Args&&... args)
{
    std::ostringstream stream;
    fprintf(stream,format,args...);
    return stream.str();
}

template<typename... Args>
int fprintf(std::ostream& out,const std::string& format,Args&&... args)
{
    return fprintf(out,format.c_str(),args...);
}

template<typename... Args>
int printf(const std::string& format,Args&&... args)
{
    return printf(format.c_str(),args...);
}

template<typename... Args>
std::string sprintf(const std::string& format,Args&&... args)
{
    return sprintf(format.c_str(),args...);
}

extern int fputc(int c, std::ostream& out);
extern int fputs(const char *s, std::ostream& out);
extern int puts(const char *s);
inline int putc(int c, std::ostream& out){return fputc(c,out);}
inline int putchar(int c){return putc(c,LOG::cout);}
}
}
#endif
