// Copyright (c) 2013-2017, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// An example to show how one might use asynchronous responses.

#include "seasocks/PrintfLogger.h"
#include "seasocks/Server.h"
#include "seasocks/Response.h"
#include "seasocks/ResponseWriter.h"
#include "seasocks/ResponseCode.h"
#include "seasocks/util/RootPageHandler.h"
#include "seasocks/util/PathHandler.h"

#include <cassert>
#include <cstring>
#include <iostream>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <unistd.h>

using namespace seasocks;
using namespace std;

// The AsyncResponse does some long-lived "work" (in this case a big sleep...)
// before responding to the ResponseWriter, in chunks. It uses a new thread to
// perform this "work". As responses can be canceled before the work is
// complete, we must ensure the ResponseWriter used to communicate the response
// is kept alive long enough by holding its shared_ptr in the "work" thread.
// Seasocks will tell the response it has been cancelled (if the connection
// associated with the request is closed); but the ResponseWriter is safe in the
// presence of a closed connection so for simplicity this example does nothing
// in the cancel() method. It is assumed the lifetime of the Server object is
// long enough for all requests to complete before it is destroyed.
struct AsyncResponse : Response {
    Server &_server;
    explicit AsyncResponse(Server &server) : _server(server) {}

    // From Response:
    virtual void handle(shared_ptr<ResponseWriter> writer) override {
        auto &server = _server;
        thread t([&server, writer] () mutable {
            usleep(1000000); // A long database query...
            string response = "some kind of response...beginning<br>";
            server.execute([response, writer]{
                writer->begin(ResponseCode::Ok, TransferEncoding::Chunked);
                writer->header("Content-type", "application/html");
                writer->payload(response.data(), response.length());
            });
            response = "more data...<br>";
            for (auto i = 0; i < 5; ++i) {
                usleep(1000000); // more data
                server.execute([response, writer]{
                    writer->payload(response.data(), response.length());
                });
            }
            response = "Done!";
            usleep(100000); // final data
            server.execute([response, writer]{
                writer->payload(response.data(), response.length());
                writer->finish(true);
            });
        });
        t.detach();
    }
    virtual void cancel() override {
        // If we could cancel the thread, we would do so here. There's no need
        // to invalidate the _writer; any writes to it after this will be
        // silently dropped.
    }
};

struct DataHandler : CrackedUriPageHandler {
    virtual std::shared_ptr<Response> handle(
            const CrackedUri &/*uri*/, const Request &request) override {
        return make_shared<AsyncResponse>(request.server());
    }
};

int main(int /*argc*/, const char* /*argv*/[]) {
    auto logger = std::make_shared<PrintfLogger>(Logger::Level::DEBUG);

    Server server(logger);
    auto root = make_shared<RootPageHandler>();
    auto pathHandler = make_shared<PathHandler>("data", make_shared<DataHandler>());
    root->add(pathHandler);
    server.addPageHandler(root);

    server.serve("src/async_test_web", 9090);
    return 0;
}
