#pragma once
#include <iostream> 
#include <sstream>
#include <cstdarg>
#include <windows.h>

//usage: 
//   DEBUGOUT("HI %s", username);
//   DEBUGOUT << "HI " << username;
//results:
//   C:/users/dev/code/mpd/file_ptr.h(9): HI smith
//   C:/users/dev/code/mpd/file_ptr.h(10): HI smith
//double click a line to be taken directly to that line of code

__declspec(dllimport) void __stdcall OutputDebugStringA(_In_opt_ const char* lpOutputString);
struct DebugOutBuffer {
    DebugOutBuffer(const char* file, int line, bool enabled) 
    {if (enabled) ss<<file<<'('<<line<<"): "; else ss.setstate(std::ios_base::badbit); }
    ~DebugOutBuffer()
    {if (ss) {ss<<'\n'; OutputDebugStringW(ss.str().c_str());}}
    
    operator std::wostream&() {return ss;}
    template<class T> std::wostream& operator<<(T&& t) {return ss << t;}

    DebugOutBuffer& operator()(_In_z_ const char* text) 
    {if (ss) ss<<text; return *this;}
    template<class T, class enabled=typename std::enable_if<!std::is_same<T,va_list>::value>::type>
    DebugOutBuffer& operator()(_In_z_ _Printf_format_string_ const char* format, T, ...) 
    { 
        if (!ss) return *this;
        va_list arglist;
        va_start(arglist, format);
        operator()(format, arglist);
        va_end(arglist);
        return *this;
    }
    DebugOutBuffer& operator()(_In_z_ _Printf_format_string_ const char* format, va_list args) 
    { 
        if (!ss) return *this;
        char buffer[1024];
        va_list arglist;
        va_start(arglist, format);
        vsnprintf(buffer, 1024, format, args);
        buffer[1023] = '\0';
        ss<<buffer;
        return *this;
    }
    DebugOutBuffer& operator()(_In_z_ const wchar_t* text) 
    {if (ss) ss<<text; return *this;}
    template<class T, class enabled=typename std::enable_if<!std::is_same<T,va_list>::value>::type>
    DebugOutBuffer& operator()(_In_z_ _Printf_format_string_ const wchar_t* format, T, ...) 
    { 
        if (!ss) return *this;
        va_list arglist;
        va_start(arglist, format);
        operator()(format, arglist);
        va_end(arglist);
        return *this;
    }
    DebugOutBuffer& operator()(_In_z_ _Printf_format_string_ const wchar_t* format, va_list args) 
    { 
        if (!ss) return *this;
        wchar_t buffer[1024];
        va_list arglist;
        va_start(arglist, format);
        _vsnwprintf(buffer, 1024, format, args);
        buffer[1023] = '\0';
        ss<<buffer;
        return *this;
    }
private:
    std::wostringstream ss;
};
#define DEBUGOUT DebugOutBuffer(__FILE__, __LINE__, true) 