/*
 * 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_H
#define BU_CLIENT_H

#include <stdint.h>

#include "bu/config.h"
#include "bu/string.h"
#include "bu/clientbuf.h"
#include "bu/mutex.h"
#include "bu/readwritemutex.h"
#include "bu/serverinterface.h"

#ifndef PROFILE_BU_SERVER
// #define PROFILE_BU_SERVER   1
#endif

#ifdef PROFILE_BU_SERVER
#include "bu/profiler.h"
#endif

namespace Bu
{
    class Protocol;
    class Stream;
    class ClientLinkFactory;

    /**
     *@ingroup Serving
     */
    class Client : public Bu::Stream
    {
    friend class Server;
    public:
        Client( int iId, const Bu::ServerInterface &rServer );
        virtual ~Client();

        int getId() const;

        void processInput();
        void outputReady();

        Bu::size write( const Bu::String &sData );
        Bu::size write( const void *pData, Bu::size nBytes );
        Bu::size write( int8_t nData );
        Bu::size write( int16_t nData );
        Bu::size write( int32_t nData );
        Bu::size write( int64_t nData );
        Bu::size write( uint8_t nData );
        Bu::size write( uint16_t nData );
        Bu::size write( uint32_t nData );
        Bu::size write( uint64_t nData );
        Bu::size read( void *pData, Bu::size nBytes );
        Bu::size peek( void *pData, int nBytes, int nOffset=0 );
        Bu::size getInputSize();
        Bu::size getOutputSize();

        void setProtocol( Protocol *pProto );
        Bu::Protocol *getProtocol();
        void clearProtocol();

        bool isOpen();
        void close();
        void tick();

        void disconnect();
        bool wantsDisconnect();

        class ClientLink *getLink();

        void onMessage( const Bu::String &sMsg );

        bool hasOutput() { return cbBuffer.server().getSize() > 0; }
        bool hasInput() { return cbBuffer.client().getSize() > 0; }

        template<typename filter>
        void pushFilter()
        {
        }
        
        template<typename filter, typename p1t>
        void pushFilter( p1t p1 )
        {
        }
        
        template<typename filter, typename p1t, typename p2t>
        void pushFilter( p1t p1, p2t p2 )
        {
        }

        /*
         * These are required to qualify as a stream, I dunno how many will
         * be implemented.
         */
        virtual Bu::size tell();
        virtual void seek( Bu::size offset );
        virtual void setPos( Bu::size pos );
        virtual void setPosEnd( Bu::size pos );
        virtual bool isEos();
        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( Bu::size iSize );
        virtual size getSize() const;
        virtual size getBlockSize() const;
        virtual Bu::String getLocation() const;

    private:
        Bu::Protocol *pProto;
        Bu::ClientBuf cbBuffer;
        bool bWantsDisconnect;
        mutable Bu::Mutex mProto;
        mutable Bu::Mutex mDisconnect;
        int iId;
        Bu::ServerInterface xServer;
    };
}

#endif
