// Copyright 2022 Google LLC
//
// 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 <fmt/format.h>

#include <sdeventplus/event.hpp>
#include <sdeventplus/source/io.hpp>
#include <stdplus/fd/create.hpp>
#include <stdplus/fd/ops.hpp>

using namespace std::string_view_literals;

// A privileged port that is reserved for querying BMC DHCP completion.
// This is well known by the clients querying the status.
constexpr uint16_t kListenPort = 23;
enum : uint8_t
{
    DONE = 0,
    POWERCYCLE = 1,
};

stdplus::ManagedFd createListener()
{
    using namespace stdplus::fd;
    auto sock = socket(SocketDomain::INet6, SocketType::Stream,
                       SocketProto::TCP);
    setFileFlags(sock, getFileFlags(sock).set(stdplus::fd::FileFlag::NonBlock));
    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(kListenPort);
    bind(sock, addr);
    listen(sock, 10);
    return sock;
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        fmt::print(stderr, "Invalid parameter count\n");
        return 1;
    }

    std::vector<uint8_t> data;

    if (argv[1] == "POWERCYCLE"sv)
    {
        data.push_back(POWERCYCLE);
    }
    else if (argv[1] == "DONE"sv)
    {
        data.push_back(DONE);
    }
    else
    {
        fmt::print(stderr, "Invalid parameter\n");
        return 1;
    }

    try
    {
        auto listener = createListener();
        auto event = sdeventplus::Event::get_default();
        sdeventplus::source::IO do_accept(
            event, listener.get(), EPOLLIN | EPOLLET,
            [&](sdeventplus::source::IO&, int, uint32_t) {
            while (auto fd = stdplus::fd::accept(listener))
            {
                stdplus::fd::sendExact(*fd, data, stdplus::fd::SendFlags(0));
            }
            });
        return event.loop();
    }
    catch (const std::exception& e)
    {
        fmt::print(stderr, "Failed: {}\n", e.what());
        return 1;
    }
}