#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <stdexcept>
#include <string>

struct Observation {
    int closed{0};
    bool close_ok{true};
};

class Fd {
    int value_;
    Observation& observation_;
public:
    Fd(int value, Observation& observation)
        : value_{value}, observation_{observation} {
        if (value_ < 0) throw std::runtime_error("socket");
        std::cout << "open=listener\n";
    }
    Fd(const Fd&) = delete;
    Fd& operator=(const Fd&) = delete;
    ~Fd() {
        const int result{::close(value_)};
        ++observation_.closed;
        observation_.close_ok = observation_.close_ok && result == 0;
        std::cout << "close=listener result=" << result << '\n';
    }
    int get() const { return value_; }
};

int main(int argc, char* argv[]) {
    const std::string mode{argc == 2 ? argv[1] : "normal"};
    if (argc > 2 || (mode != "normal" && mode != "unwind")) return 2;
    Observation observation;
    bool caught{false};
    try {
        Fd listener{::socket(AF_INET, SOCK_STREAM, 0), observation};
        sockaddr_in address{};
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        address.sin_port = htons(0);
        if (::bind(listener.get(), reinterpret_cast<sockaddr*>(&address),
                   sizeof address) != 0) throw std::runtime_error("bind");
        socklen_t length{sizeof address};
        if (::getsockname(listener.get(), reinterpret_cast<sockaddr*>(&address),
                          &length) != 0) throw std::runtime_error("getsockname");
        const auto port{ntohs(address.sin_port)};
        const bool loopback{ntohl(address.sin_addr.s_addr) == INADDR_LOOPBACK};
        const bool length_ok{length == sizeof address};
        std::cout << "port=" << port << " loopback=" << loopback
                  << " length-ok=" << length_ok << '\n';
        if (port == 0 || !loopback || !length_ok) throw std::runtime_error("address");
        if (mode == "unwind") throw std::runtime_error("lesson-unwind");
        std::cout << "body=normal\n";
    } catch (const std::exception& error) {
        if (mode != "unwind" || std::string{error.what()} != "lesson-unwind") {
            std::cerr << "unexpected=" << error.what() << '\n';
            return 1;
        }
        caught = true;
        std::cout << "caught=lesson-unwind\n";
    }
    std::cout << "closed=" << observation.closed << " close-ok="
              << observation.close_ok << " caught=" << caught << '\n';
    return observation.closed == 1 && observation.close_ok &&
           caught == (mode == "unwind") ? 0 : 1;
}
