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

#include "sockstreambuf.h"
#include "../assert.h"

#include <cstring>

namespace dlib
{

// ---------------------------------------------------------------------------------------- 
    // output functions
// ---------------------------------------------------------------------------------------- 

    sockstreambuf::int_type sockstreambuf::
    overflow (
        int_type c
    )
    {
        if (c != EOF)
        {
            *pptr() = c;
            pbump(1);
        }
        if (flush_out_buffer() == EOF)
        {
            // an error occurred
            return EOF;
        }
        return c;
    }

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

    std::streamsize sockstreambuf::
    xsputn (
        const char* s,
        std::streamsize num
    )
    {
        // Add a sanity check here 
        DLIB_ASSERT(num >= 0,
            "\tstd::streamsize sockstreambuf::xsputn"
            << "\n\tThe number of bytes to write can't be negative"
            << "\n\tnum:  " << num 
            << "\n\tthis: " << this
            );

        std::streamsize space_left = static_cast<std::streamsize>(epptr()-pptr());
        if (num <= space_left)
        {
            std::memcpy(pptr(),s,static_cast<size_t>(num));
            pbump(static_cast<int>(num));
            return num;
        }
        else
        {
            std::memcpy(pptr(),s,static_cast<size_t>(space_left));
            s += space_left;
            pbump(space_left);
            std::streamsize num_left = num - space_left;

            if (flush_out_buffer() == EOF)
            {
                // the write was not successful so return that 0 bytes were written
                return 0;
            }

            if (num_left < out_buffer_size)
            {
                std::memcpy(pptr(),s,static_cast<size_t>(num_left));
                pbump(num_left);
                return num;
            }
            else
            {
                if (con.write(s,num_left) != num_left)
                {
                    // the write was not successful so return that 0 bytes were written
                    return 0;
                } 
                return num;
            }
        }
    }

// ---------------------------------------------------------------------------------------- 
    // input functions
// ---------------------------------------------------------------------------------------- 

    sockstreambuf::int_type sockstreambuf::
    underflow( 
    )
    {
        if (gptr() < egptr())
        {
            return static_cast<unsigned char>(*gptr());
        }

        int num_put_back = static_cast<int>(gptr() - eback());
        if (num_put_back > max_putback)
        {
            num_put_back = max_putback;
        }

        // copy the putback characters into the putback end of the in_buffer
        std::memmove(in_buffer+(max_putback-num_put_back), gptr()-num_put_back, num_put_back);

        if (flushes_output_on_read())
        {
            if (flush_out_buffer() == EOF)
            {
                // an error occurred
                return EOF;
            }
        }

        int num = con.read(in_buffer+max_putback, in_buffer_size-max_putback);
        if (num <= 0)
        {
            // an error occurred or the connection is over which is EOF
            return EOF;
        }

        // reset in_buffer pointers
        setg (in_buffer+(max_putback-num_put_back),
              in_buffer+max_putback,
              in_buffer+max_putback+num);

        return static_cast<unsigned char>(*gptr());
    }

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

    std::streamsize sockstreambuf::
    xsgetn (
        char_type* s, 
        std::streamsize n
    )
    { 
        std::streamsize temp = n;
        while (n > 0)
        {
            int num = static_cast<int>(egptr() - gptr());
            if (num >= n)
            {
                // copy data from our buffer 
                std::memcpy(s, gptr(), static_cast<size_t>(n));
                gbump(static_cast<int>(n));
                return temp;
            }

            // read more data into our buffer  
            if (num == 0)
            {
                if (underflow() == EOF)
                    break;
                continue;
            }

            // copy all the data from our buffer 
            std::memcpy(s, gptr(), num);
            n -= num;
            gbump(num);
            s += num;
        }
        return temp-n;       
    }

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

}
#endif // DLIB_SOCKStREAMBUF_CPp_