#include "xf86dri.h"

#include <xcb/xcb.h>
#include <xcb/xcbext.h>

#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "ui/gfx/x/xproto_internal.h"

namespace x11 {

XF86Dri::XF86Dri(x11::Connection* connection,
    const x11::QueryExtensionReply& info)
    : connection_(connection), info_(info) {}

Future<XF86Dri::QueryVersionReply>
XF86Dri::QueryVersion(
    const XF86Dri::QueryVersionRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 0;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::QueryVersionReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::QueryVersionReply>
ReadReply<XF86Dri::QueryVersionReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::QueryVersionReply>();

  auto& sequence = (*reply).sequence;
  auto& dri_major_version = (*reply).dri_major_version;
  auto& dri_minor_version = (*reply).dri_minor_version;
  auto& dri_minor_patch = (*reply).dri_minor_patch;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // dri_major_version
  Read(&dri_major_version, &buf);

  // dri_minor_version
  Read(&dri_minor_version, &buf);

  // dri_minor_patch
  Read(&dri_minor_patch, &buf);

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<XF86Dri::QueryDirectRenderingCapableReply>
XF86Dri::QueryDirectRenderingCapable(
    const XF86Dri::QueryDirectRenderingCapableRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 1;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::QueryDirectRenderingCapableReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::QueryDirectRenderingCapableReply>
ReadReply<XF86Dri::QueryDirectRenderingCapableReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::QueryDirectRenderingCapableReply>();

  auto& sequence = (*reply).sequence;
  auto& is_capable = (*reply).is_capable;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // is_capable
  Read(&is_capable, &buf);

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<XF86Dri::OpenConnectionReply>
XF86Dri::OpenConnection(
    const XF86Dri::OpenConnectionRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 2;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::OpenConnectionReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::OpenConnectionReply>
ReadReply<XF86Dri::OpenConnectionReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::OpenConnectionReply>();

  auto& sequence = (*reply).sequence;
  auto& sarea_handle_low = (*reply).sarea_handle_low;
  auto& sarea_handle_high = (*reply).sarea_handle_high;
  uint32_t bus_id_len{};
  auto& bus_id = (*reply).bus_id;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // sarea_handle_low
  Read(&sarea_handle_low, &buf);

  // sarea_handle_high
  Read(&sarea_handle_high, &buf);

  // bus_id_len
  Read(&bus_id_len, &buf);

  // pad1
  Pad(&buf, 12);

  // bus_id
  bus_id.resize(bus_id_len);
  for (auto& bus_id_elem : bus_id) {
    // bus_id_elem
    Read(&bus_id_elem, &buf);

  }

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<void>
XF86Dri::CloseConnection(
    const XF86Dri::CloseConnectionRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 3;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  Align(&buf, 4);

  return x11::SendRequest<void>(connection_, &buf, false);
}

Future<XF86Dri::GetClientDriverNameReply>
XF86Dri::GetClientDriverName(
    const XF86Dri::GetClientDriverNameRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 4;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::GetClientDriverNameReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::GetClientDriverNameReply>
ReadReply<XF86Dri::GetClientDriverNameReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::GetClientDriverNameReply>();

  auto& sequence = (*reply).sequence;
  auto& client_driver_major_version = (*reply).client_driver_major_version;
  auto& client_driver_minor_version = (*reply).client_driver_minor_version;
  auto& client_driver_patch_version = (*reply).client_driver_patch_version;
  uint32_t client_driver_name_len{};
  auto& client_driver_name = (*reply).client_driver_name;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // client_driver_major_version
  Read(&client_driver_major_version, &buf);

  // client_driver_minor_version
  Read(&client_driver_minor_version, &buf);

  // client_driver_patch_version
  Read(&client_driver_patch_version, &buf);

  // client_driver_name_len
  Read(&client_driver_name_len, &buf);

  // pad1
  Pad(&buf, 8);

  // client_driver_name
  client_driver_name.resize(client_driver_name_len);
  for (auto& client_driver_name_elem : client_driver_name) {
    // client_driver_name_elem
    Read(&client_driver_name_elem, &buf);

  }

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<XF86Dri::CreateContextReply>
XF86Dri::CreateContext(
    const XF86Dri::CreateContextRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;
  auto& visual = request.visual;
  auto& context = request.context;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 5;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  // visual
  buf.Write(&visual);

  // context
  buf.Write(&context);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::CreateContextReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::CreateContextReply>
ReadReply<XF86Dri::CreateContextReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::CreateContextReply>();

  auto& sequence = (*reply).sequence;
  auto& hw_context = (*reply).hw_context;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // hw_context
  Read(&hw_context, &buf);

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<void>
XF86Dri::DestroyContext(
    const XF86Dri::DestroyContextRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;
  auto& context = request.context;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 6;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  // context
  buf.Write(&context);

  Align(&buf, 4);

  return x11::SendRequest<void>(connection_, &buf, false);
}

Future<XF86Dri::CreateDrawableReply>
XF86Dri::CreateDrawable(
    const XF86Dri::CreateDrawableRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;
  auto& drawable = request.drawable;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 7;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  // drawable
  buf.Write(&drawable);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::CreateDrawableReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::CreateDrawableReply>
ReadReply<XF86Dri::CreateDrawableReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::CreateDrawableReply>();

  auto& sequence = (*reply).sequence;
  auto& hw_drawable_handle = (*reply).hw_drawable_handle;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // hw_drawable_handle
  Read(&hw_drawable_handle, &buf);

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<void>
XF86Dri::DestroyDrawable(
    const XF86Dri::DestroyDrawableRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;
  auto& drawable = request.drawable;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 8;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  // drawable
  buf.Write(&drawable);

  Align(&buf, 4);

  return x11::SendRequest<void>(connection_, &buf, false);
}

Future<XF86Dri::GetDrawableInfoReply>
XF86Dri::GetDrawableInfo(
    const XF86Dri::GetDrawableInfoRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;
  auto& drawable = request.drawable;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 9;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  // drawable
  buf.Write(&drawable);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::GetDrawableInfoReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::GetDrawableInfoReply>
ReadReply<XF86Dri::GetDrawableInfoReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::GetDrawableInfoReply>();

  auto& sequence = (*reply).sequence;
  auto& drawable_table_index = (*reply).drawable_table_index;
  auto& drawable_table_stamp = (*reply).drawable_table_stamp;
  auto& drawable_origin_X = (*reply).drawable_origin_X;
  auto& drawable_origin_Y = (*reply).drawable_origin_Y;
  auto& drawable_size_W = (*reply).drawable_size_W;
  auto& drawable_size_H = (*reply).drawable_size_H;
  uint32_t num_clip_rects{};
  auto& back_x = (*reply).back_x;
  auto& back_y = (*reply).back_y;
  uint32_t num_back_clip_rects{};
  auto& clip_rects = (*reply).clip_rects;
  size_t clip_rects_len = clip_rects.size();
  auto& back_clip_rects = (*reply).back_clip_rects;
  size_t back_clip_rects_len = back_clip_rects.size();

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // drawable_table_index
  Read(&drawable_table_index, &buf);

  // drawable_table_stamp
  Read(&drawable_table_stamp, &buf);

  // drawable_origin_X
  Read(&drawable_origin_X, &buf);

  // drawable_origin_Y
  Read(&drawable_origin_Y, &buf);

  // drawable_size_W
  Read(&drawable_size_W, &buf);

  // drawable_size_H
  Read(&drawable_size_H, &buf);

  // num_clip_rects
  Read(&num_clip_rects, &buf);

  // back_x
  Read(&back_x, &buf);

  // back_y
  Read(&back_y, &buf);

  // num_back_clip_rects
  Read(&num_back_clip_rects, &buf);

  // clip_rects
  clip_rects.resize(num_clip_rects);
  for (auto& clip_rects_elem : clip_rects) {
    // clip_rects_elem
    {
      auto& x1 = clip_rects_elem.x1;
      auto& y1 = clip_rects_elem.y1;
      auto& x2 = clip_rects_elem.x2;
      auto& x3 = clip_rects_elem.x3;

      // x1
      Read(&x1, &buf);

      // y1
      Read(&y1, &buf);

      // x2
      Read(&x2, &buf);

      // x3
      Read(&x3, &buf);

    }

  }

  // back_clip_rects
  back_clip_rects.resize(num_back_clip_rects);
  for (auto& back_clip_rects_elem : back_clip_rects) {
    // back_clip_rects_elem
    {
      auto& x1 = back_clip_rects_elem.x1;
      auto& y1 = back_clip_rects_elem.y1;
      auto& x2 = back_clip_rects_elem.x2;
      auto& x3 = back_clip_rects_elem.x3;

      // x1
      Read(&x1, &buf);

      // y1
      Read(&y1, &buf);

      // x2
      Read(&x2, &buf);

      // x3
      Read(&x3, &buf);

    }

  }

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<XF86Dri::GetDeviceInfoReply>
XF86Dri::GetDeviceInfo(
    const XF86Dri::GetDeviceInfoRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 10;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::GetDeviceInfoReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::GetDeviceInfoReply>
ReadReply<XF86Dri::GetDeviceInfoReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::GetDeviceInfoReply>();

  auto& sequence = (*reply).sequence;
  auto& framebuffer_handle_low = (*reply).framebuffer_handle_low;
  auto& framebuffer_handle_high = (*reply).framebuffer_handle_high;
  auto& framebuffer_origin_offset = (*reply).framebuffer_origin_offset;
  auto& framebuffer_size = (*reply).framebuffer_size;
  auto& framebuffer_stride = (*reply).framebuffer_stride;
  uint32_t device_private_size{};
  auto& device_private = (*reply).device_private;
  size_t device_private_len = device_private.size();

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // framebuffer_handle_low
  Read(&framebuffer_handle_low, &buf);

  // framebuffer_handle_high
  Read(&framebuffer_handle_high, &buf);

  // framebuffer_origin_offset
  Read(&framebuffer_origin_offset, &buf);

  // framebuffer_size
  Read(&framebuffer_size, &buf);

  // framebuffer_stride
  Read(&framebuffer_stride, &buf);

  // device_private_size
  Read(&device_private_size, &buf);

  // device_private
  device_private.resize(device_private_size);
  for (auto& device_private_elem : device_private) {
    // device_private_elem
    Read(&device_private_elem, &buf);

  }

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

Future<XF86Dri::AuthConnectionReply>
XF86Dri::AuthConnection(
    const XF86Dri::AuthConnectionRequest& request) {
  if (!connection_->Ready() || !present())
    return {};

  WriteBuffer buf;

  auto& screen = request.screen;
  auto& magic = request.magic;

  // major_opcode
  uint8_t major_opcode = info_.major_opcode;
  buf.Write(&major_opcode);

  // minor_opcode
  uint8_t minor_opcode = 11;
  buf.Write(&minor_opcode);

  // length
  // Caller fills in length for writes.
  Pad(&buf, sizeof(uint16_t));

  // screen
  buf.Write(&screen);

  // magic
  buf.Write(&magic);

  Align(&buf, 4);

  return x11::SendRequest<XF86Dri::AuthConnectionReply>(connection_, &buf, false);
}

namespace detail {
template<> COMPONENT_EXPORT(X11)
std::unique_ptr<XF86Dri::AuthConnectionReply>
ReadReply<XF86Dri::AuthConnectionReply>(ReadBuffer* buffer) {
  auto& buf = *buffer;
  auto reply = std::make_unique<XF86Dri::AuthConnectionReply>();

  auto& sequence = (*reply).sequence;
  auto& authenticated = (*reply).authenticated;

  // response_type
  uint8_t response_type;
  Read(&response_type, &buf);

  // pad0
  Pad(&buf, 1);

  // sequence
  Read(&sequence, &buf);

  // length
  uint32_t length;
  Read(&length, &buf);

  // authenticated
  Read(&authenticated, &buf);

  Align(&buf, 4);
  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);

  return reply;
}
}  // namespace detail

}  // namespace x11
