// Copyright (C) 2003  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_SOCKStREAMBUF_Hh_
#define DLIB_SOCKStREAMBUF_Hh_

#include <iosfwd>
#include <streambuf>
#include "../sockets.h"
#include "sockstreambuf_abstract.h"
#include "sockstreambuf_unbuffered.h"

namespace dlib
{

// ---------------------------------------------------------------------------------------- 

    class sockstreambuf : public std::streambuf
    {
        /*!
            INITIAL VALUE
                - con == a connection
                - in_buffer == an array of in_buffer_size bytes
                - out_buffer == an array of out_buffer_size bytes

            CONVENTION
                - in_buffer == the input buffer used by this streambuf
                - out_buffer == the output buffer used by this streambuf
                - max_putback == the maximum number of chars to have in the put back buffer.
        !*/

    public:

        // These typedefs are here for backwards compatibility with previous versions of
        // dlib.
        typedef sockstreambuf_unbuffered kernel_1a;
        typedef sockstreambuf kernel_2a;

        sockstreambuf (
            connection* con_
        ) :
            con(*con_),
            out_buffer(0),
            in_buffer(0),
            autoflush(false)
        {
            init();
        }

        sockstreambuf (
            const std::unique_ptr<connection>& con_
        ) :
            con(*con_),
            out_buffer(0),
            in_buffer(0),
            autoflush(false)
        {
            init();
        }

        virtual ~sockstreambuf (
        )
        {
            sync();
            delete [] out_buffer;
            delete [] in_buffer;
        }

        connection* get_connection (
        ) { return &con; }

        void flush_output_on_read()
        {
            autoflush = true;
        }

        bool flushes_output_on_read() const
        {
            return autoflush;
        }

        void do_not_flush_output_on_read()
        {
            autoflush = false;
        }

    protected:

        void init (
        )
        {
            try
            {
                out_buffer = new char[out_buffer_size];
                in_buffer = new char[in_buffer_size];
            }
            catch (...)
            {
                if (out_buffer) delete [] out_buffer;
                throw;
            }
            setp(out_buffer, out_buffer + (out_buffer_size-1));
            setg(in_buffer+max_putback, 
                 in_buffer+max_putback, 
                 in_buffer+max_putback);
        }

        int flush_out_buffer (
        )
        {
            int num = static_cast<int>(pptr()-pbase());
            if (con.write(out_buffer,num) != num)
            {
                // the write was not successful so return EOF 
                return EOF;
            } 
            pbump(-num);
            return num;
        }

        // output functions
        int_type overflow (
            int_type c
        );

        int sync (
        )
        {
            if (flush_out_buffer() == EOF)
            {
                // an error occurred
                return -1;
            }
            return 0;
        }

        std::streamsize xsputn (
            const char* s,
            std::streamsize num
        );

        // input functions
        int_type underflow( 
        );

        std::streamsize xsgetn (
            char_type* s, 
            std::streamsize n
        );

    private:

        // member data
        connection&  con;
        static const std::streamsize max_putback = 4;
        static const std::streamsize out_buffer_size = 10000;
        static const std::streamsize in_buffer_size = 10000;
        char* out_buffer;
        char* in_buffer;
        bool autoflush;
    
    };

// ---------------------------------------------------------------------------------------- 

}

#ifdef NO_MAKEFILE
#include "sockstreambuf.cpp"
#endif

#endif // DLIB_SOCKStREAMBUF_Hh_