#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <gio/gio.h>
#include <gudev/gudev.h>
#include <glib.h>
#include <glib/gstdio.h>

#include <xf86drm.h>
#include <nouveau.h>
#include <nouveau_drm.h>
#include <nouveau/nvif/ioctl.h>
#include <nvif/cl0080.h>
#include <nvif/class.h>

static int
init_device (int                    fd,
             struct nouveau_object *device)
{
	struct {
		struct nvif_ioctl_v0 ioctl;
		struct nvif_ioctl_new_v0 new;
		struct nv_device_v0 dev;
	} init_args = {
		.ioctl = {
			.object = 0,
			.owner = NVIF_IOCTL_V0_OWNER_ANY,
			.route = 0x00,
			.type = NVIF_IOCTL_V0_NEW,
			.version = 0,
		},
		.new = {
			.handle = 0,
			.object = (uintptr_t)device,
			.oclass = NV_DEVICE,
			.route = NVIF_IOCTL_V0_ROUTE_NVIF,
			.token = (uintptr_t)device,
			.version = 0,
		},
		.dev = {
			.device = ~0ULL,
		},
	};

	return drmCommandWrite (fd, DRM_NOUVEAU_NVIF, &init_args, sizeof(init_args));
}

static int
device_info (int                       fd,
             struct nouveau_object    *device,
             struct nv_device_info_v0 *info)
{
	struct {
		struct nvif_ioctl_v0 ioctl;
		struct nvif_ioctl_mthd_v0 mthd;
		struct nv_device_info_v0 info;
	} args = {
		.ioctl = {
			.object = (uintptr_t)device,
			.owner = NVIF_IOCTL_V0_OWNER_ANY,
			.route = 0x00,
			.type = NVIF_IOCTL_V0_MTHD,
			.version = 0,
		},
		.mthd = {
			.method = NV_DEVICE_V0_INFO,
			.version = 0,
		},
		.info = {
			.version = 0,
		},
	};

	int ret = drmCommandWriteRead (fd, DRM_NOUVEAU_NVIF, &args, sizeof(args));
	memcpy (info, &args.info, sizeof(*info));

	return ret;
}

int main (int argc, char **argv)
{
	const gchar *devname = NULL;
	g_autofd int fd = -1;
	struct nouveau_object device;
	struct nv_device_info_v0 info;
	g_autoptr(GOptionContext) option_context = NULL;
	g_autoptr(GError) error = NULL;

	setlocale (LC_ALL, "");
	option_context = g_option_context_new ("");

	if (!g_option_context_parse (option_context, &argc, &argv, &error)) {
		g_printerr ("Failed to parse arguments: %s\n", error->message);
		return EXIT_FAILURE;
	}

	if (argc < 2) {
		g_printerr ("%s\n", g_option_context_get_help (option_context, TRUE, NULL));
		return EXIT_FAILURE;
	}
	devname = argv[1];

	fd = open (devname, O_RDWR);
	if (fd < 0)
		return EXIT_FAILURE;

	if (init_device (fd, &device))
		return EXIT_FAILURE;

	if (device_info (fd, &device, &info))
		return EXIT_FAILURE;

	switch (info.platform) {
		case NV_DEVICE_INFO_V0_IGP:
		case NV_DEVICE_INFO_V0_SOC:
			return EXIT_FAILURE;

		case NV_DEVICE_INFO_V0_PCI:
		case NV_DEVICE_INFO_V0_AGP:
		case NV_DEVICE_INFO_V0_PCIE:
		default:
			return EXIT_SUCCESS;
	}
	return EXIT_FAILURE;
}
