// Copyright 2016 The Android Open Source Project
//
// 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.

#include "aemu/base/Log.h"
#include "aemu/base/sockets/ScopedSocket.h"
#include "aemu/base/sockets/SocketUtils.h"

#include "aemu/base/threads/Thread.h"

#include <string>
#include <string_view>

#include <stddef.h>

#ifdef _WIN32
#undef ERROR
#endif

namespace android {
namespace base {

// Simple server thread that receives data and stores it in a buffer.
// Usage is the following:
//
// 1) Create instance, passing a port to listen to the constructor.
//    The default constructor will bind to a random port, which you
//    can retrieve with the port() method. Check that the port could be
//    be bound with the valid() method.
//
// 2) Start the thread, it will store all the data it receives into
//    an internal buffer. The thread only stops when the connection
//    is closed from the remote end.
//
// 3) Join the thread, the result is the size of the buffer in bytes, or
//    -1 if a connection could not be accepted.
//
// 4) Use view() to retrieve a view of the content.
//
class TestInputBufferSocketServerThread : public android::base::Thread {
public:
    // Create new thread instance, try to bound to specific TCP |port|,
    // a value of 0 let the system choose a free IPv4 port, which can
    // later be retrieved with port().
    TestInputBufferSocketServerThread(int port = 0)
        : Thread(), mSocket(android::base::socketTcp4LoopbackServer(port)) {}

    // Returns true if port could be bound.
    bool valid() const { return mSocket.valid(); }

    // Return bound server port.
    int port() const { return android::base::socketGetPort(mSocket.get()); }

    // Return buffer content as a std::string_view.
    std::string_view view() const { return mString; }

    // Main function simply receives everything and stores it in a string.
    virtual intptr_t main() override {
        // Wait for a single connection.
        int fd = android::base::socketAcceptAny(mSocket.get());
        if (fd < 0) {
            LOG(ERROR) << "Could not accept one connection!";
            return -1;
        }

        // Now read data from the connection until it closes.
        size_t size = 0;
        for (;;) {
            size_t capacity = mString.size();
            if (size >= capacity) {
                mString.resize(1024 + capacity * 2);
                capacity = mString.size();
            }
            size_t avail = capacity - size;
            ssize_t len = android::base::socketRecv(fd, &mString[size], avail);
            if (len <= 0) {
                break;
            }
            size += len;
        }
        android::base::socketClose(fd);

        mString.resize(size);
        return static_cast<intptr_t>(size);
    }

private:
    ScopedSocket mSocket;
    std::string mString;
};

}  // namespace base
}  // namespace android
