// Copyright (c) 2014 Baidu, Inc.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Authors: Ge,Jun (gejun@baidu.com)

#ifndef BRPC_SOCKET_MESSAGE_H
#define BRPC_SOCKET_MESSAGE_H

#include "butil/status.h"                          // butil::Status


namespace brpc {

// Generate the IOBuf to write dynamically, for implementing complex protocols.
// Used in RTMP and HTTP2 right now.
class SocketMessage {
public:
    virtual ~SocketMessage() {}
    
    // Called once and only once *sequentially* to generate the buffer to
    // write. This object should destroy itself at the end of this method.
    // AppendAndDestroySelf() to the same Socket are called one by one in the
    // same sequence as their generated data are written into the file
    // descriptor. AppendAndDestroySelf() are called _after_ completion of
    // connecting including AppConnect.
    // Params:
    //   out  - The buffer to be generated, being empty initially, could
    //          remain empty after being called.
    //   sock - the socket to write. NULL when the message is abandoned,
    //          namely the socket is broken or AppendAndDestroySelf() is
    //          called by SocketMessagePtr<T>
    // If the status returned is an error, WriteOptions.id_wait (if absent)
    // will be signalled with the error. Other messages are not affected.
    virtual butil::Status AppendAndDestroySelf(butil::IOBuf* out, Socket* sock) = 0;

    // Estimated size of the buffer generated by AppendAndDestroySelf()
    virtual size_t EstimatedByteSize() { return 0; }
};

namespace details {
struct SocketMessageDeleter {
    void operator()(SocketMessage* msg) const {
        butil::IOBuf dummy_buf;
        // We don't care about the return value since the message is abandoned
        (void)msg->AppendAndDestroySelf(&dummy_buf, NULL);
    }
};
}

// A RAII pointer to make sure SocketMessage.AppendAndDestroySelf() is always
// called even if the message is rejected by Socket.Write()
// Property: Any SocketMessagePtr<T> can be casted to SocketMessagePtr<> which
// is accepted by Socket.Write()
template <typename T = void> struct SocketMessagePtr;

template <> struct SocketMessagePtr<void>
    : public std::unique_ptr<SocketMessage, details::SocketMessageDeleter> {
    SocketMessagePtr() {}
    SocketMessagePtr(SocketMessage* p)
        : std::unique_ptr<SocketMessage, details::SocketMessageDeleter>(p) {}
};

template <typename T>
struct SocketMessagePtr : public SocketMessagePtr<> {
    SocketMessagePtr() {}
    SocketMessagePtr(T* p) : SocketMessagePtr<>(p) {}
    T& operator*() { return static_cast<T&>(SocketMessagePtr<>::operator*()); }
    T* operator->() { return static_cast<T*>(SocketMessagePtr<>::operator->()); }
    T* release() { return static_cast<T*>(SocketMessagePtr<>::release()); }
};

} // namespace brpc


#endif  // BRPC_SOCKET_MESSAGE_H
