require "reset/prelude"
require "reset/patches"
require "h2o/h2o_evloop"

class H2oHello < H2o
  @config = LibH2o::H2oGlobalconfT.new
  @ctx = LibH2o::H2oContextT.new
  @accept_ctx = LibH2o::H2oAcceptCtxT.new
  @loop : LibH2o::H2oLoopT*

  macro hello
    Handler.new do |handler, req|
      generator = uninitialized LibH2o::H2oGeneratorT[2]
      body = h2o_iovec_init("Hello, World!")
      req.value.res.status = 200
      req.value.res.reason = "OK"
      req.value.res.content_length = body.len

      # require h2o.c extension
      h2o_add_header(req, H2O_TOKEN_CONTENT_TYPE, "text/plain")
      h2o_start_response(req, generator)
      h2o_send(req, pointerof(body), 1, LibH2o::H2oSendState::H2OSendStateFinal)
      0
    end
  end

  def on_accept(listener : LibH2o::H2oSocketT*, err : LibC::Char*) : Void
    return if err
    return unless s = h2o_evloop_socket_accept(listener)
    h2o_accept(pointerof(@accept_ctx), s)
  end

  def create_listener : Int32
    addr = uninitialized LibC::SockaddrIn

    pointerof(addr).clear
    addr.sin_family = LibC::AF_INET
    addr.sin_addr.s_addr = 0 # 0x100007f # b32(0x7f000001)
    addr.sin_port = 0xd21e   # b16(7890)

    option = 1
    if (fd = socket(LibC::AF_INET, LibC::SOCK_STREAM | LibC::O_NONBLOCK | LibC::O_CLOEXEC, 0)) == -1 ||
       setsockopt(fd, LibC::SOL_SOCKET, LibC::SO_REUSEADDR, pointerof(option), 4) != 0 ||
       setsockopt(fd, LibC::SOL_SOCKET, LibC::SO_REUSEPORT, pointerof(option), 4) != 0 ||
       setsockopt(fd, LibC::IPPROTO_TCP, LibC::TCP_QUICKACK, pointerof(option), 4) != 0 ||
       ((option = H2O_DEFAULT_HANDSHAKE_TIMEOUT_IN_SECS) &&
       setsockopt(fd, LibC::IPPROTO_TCP, LibC::TCP_DEFER_ACCEPT, pointerof(option), 4) != 0) ||
       ((option = DEFAULT_TCP_FASTOPEN_QUEUE_LEN) &&
       setsockopt(fd, LibC::IPPROTO_TCP, LibC::TCP_FASTOPEN, pointerof(option), 4) != 0) ||
       bind(fd, pointerof(addr).as(LibC::Sockaddr*), sizeof(LibC::SockaddrIn)) != 0 ||
       listen(fd, LibC::SOMAXCONN) != 0
      return -1
    end

    sock = h2o_evloop_socket_create(@ctx.loop, fd, H2O_SOCKET_FLAG_DONT_READ)
    h2o_socket_read_start(sock, LibH2o::H2oSocketCb.new { |listener, err|
      {{@type}}.instance.on_accept(listener, err)
    })
    0
  end

  def register_handler(hostconf : LibH2o::H2oHostconfT*, path : String, on_req : Handler) : Void
    pathconf = h2o_config_register_path(hostconf, path, 0)
    handler = h2o_create_handler(pathconf, sizeof(LibH2o::H2oHandlerT))
    handler.value.on_req = on_req
  end

  def initialize
    @loop = h2o_evloop_create()
  end

  def run : Void
    h2o_config_init(pointerof(@config))
    hostconf = h2o_config_register_host(pointerof(@config), h2o_iovec_init("default"), 65535)
    register_handler(hostconf, "/hello", hello)

    h2o_context_init(pointerof(@ctx), @loop, pointerof(@config))

    @accept_ctx.ctx = pointerof(@ctx)
    @accept_ctx.hosts = @config.hosts

    if create_listener != 0
      return 1
    end

    while h2o_evloop_run(@ctx.loop, Int32::MAX) == 0; end
  end
end

H2oHello.run


