/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#ifndef BU_CLIENT_BUF_H
#define BU_CLIENT_BUF_H

#include "bu/stream.h"
#include "bu/streamstack.h"
#include "bu/queuebuf.h"
#include "bu/mutex.h"

namespace Bu
{
    /**
     * ClientBuf represents two buffered I/O channels, input and output. Each
     * is stored in its own Bu::QueueBuf. One side is client oriented and can
     * have filters applied ala Bu::StreamStack. The other is server oriented
     * and is accesesed raw. Both must be accessed via accessor objects.
     *
     * Direct access to the buffers isn't allowed, instead you must select a
     * perspective. client() provides an accessor where reading consumes data
     * coming from the socket and writing provides data to write to the socket.
     * server() works in reverse, writing is writing data from the socket that
     * we have just read, reading is reading data that should be written to the
     * socket.
     *
     * client() provides a filtered access view of the data, while server() is
     * unfiltered.
     */
    class ClientBuf
    {
    public:
        class ClientAccess : public Bu::Stream
        {
        friend class Bu::ClientBuf;
        private:
            ClientAccess( Bu::ClientBuf &rBuf );
        
        public:
            virtual ~ClientAccess();
        
            virtual void close();
            virtual size read( void *pBuf, size iBytes );
            virtual size peek( void *pData, int iBytes, int iOffset=0 );
            virtual size write( const void *pBuf, size iBytes );
            using Stream::write;
            virtual size tell();
            virtual void seek( size offset );
            virtual void setPos( size pos );
            virtual void setPosEnd( size pos );
            virtual bool isEos();
            virtual bool isOpen();
            virtual void flush();
            virtual bool canRead();
            virtual bool canWrite();
            virtual bool isReadable();
            virtual bool isWritable();
            virtual bool isSeekable();
            virtual bool isBlocking();
            virtual void setBlocking( bool bBlocking=true );
            virtual void setSize( size iSize );
            virtual size getSize() const;
            virtual size getBlockSize() const;
            virtual Bu::String getLocation() const;

        private:
            Bu::ClientBuf &rBuf;
            Bu::QueueBuf qbPeek;
            mutable Bu::Mutex mAccess;
        };

        class ServerAccess : public Bu::Stream
        {
        friend class Bu::ClientBuf;
        private:
            ServerAccess( Bu::ClientBuf &rBuf );
        
        public:
            virtual ~ServerAccess();
            
            virtual void close();
            virtual size read( void *pBuf, size iBytes );
            virtual size peek( void *pData, int iBytes, int iOffset=0 );
            virtual size write( const void *pBuf, size iBytes );
            using Stream::write;
            virtual size tell();
            virtual void seek( size offset );
            virtual void setPos( size pos );
            virtual void setPosEnd( size pos );
            virtual bool isEos();
            virtual bool isOpen();
            virtual void flush();
            virtual bool canRead();
            virtual bool canWrite();
            virtual bool isReadable();
            virtual bool isWritable();
            virtual bool isSeekable();
            virtual bool isBlocking();
            virtual void setBlocking( bool bBlocking=true );
            virtual void setSize( size iSize );
            virtual size getSize() const;
            virtual size getBlockSize() const;
            virtual Bu::String getLocation() const;

        private:
            Bu::ClientBuf &rBuf;
        };
        
    private:
        class ClientAccessRaw : public Bu::Stream
        {
        friend class Bu::ClientBuf;
        private:
            ClientAccessRaw( Bu::ClientBuf &rBuf );
        
        public:
            virtual ~ClientAccessRaw();
        
            virtual void close();
            virtual size read( void *pBuf, size iBytes );
            virtual size write( const void *pBuf, size iBytes );
            using Stream::write;
            virtual size tell();
            virtual void seek( size offset );
            virtual void setPos( size pos );
            virtual void setPosEnd( size pos );
            virtual bool isEos();
            virtual bool isOpen();
            virtual void flush();
            virtual bool canRead();
            virtual bool canWrite();
            virtual bool isReadable();
            virtual bool isWritable();
            virtual bool isSeekable();
            virtual bool isBlocking();
            virtual void setBlocking( bool bBlocking=true );
            virtual void setSize( size iSize );
            virtual size getSize() const;
            virtual size getBlockSize() const;
            virtual Bu::String getLocation() const;

        private:
            Bu::ClientBuf &rBuf;
        };

    public:
        ClientBuf();
        virtual ~ClientBuf();

        /**
         * Access the client access side. Use directly, do NOT copy or store.
         */
        ClientAccess &client();

        /**
         * Access the server access side. Use directly, do NOT copy or store.
         */
        ServerAccess &server();

    private:
        ClientAccessRaw accClientRaw;
        ServerAccess accServer;
        //Bu::StreamStack accClientFiltered;
        ClientAccess accClient;
        Bu::QueueBuf qbOutput;
        Bu::QueueBuf qbInput;
        Bu::Mutex mOutput;
        Bu::Mutex mInput;

    friend class Bu::ClientBuf::ClientAccess;
    friend class Bu::ClientBuf::ClientAccessRaw;
    friend class Bu::ClientBuf::ServerAccess;
    };
}

#endif
