// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "drgnpy.h"
#include "../array.h"
#include "../language.h"

static PyObject *Language_repr(Language *self)
{
	return PyUnicode_FromFormat("Language.%s", self->attr_name);
}

static PyObject *Language_get_name(Language *self, void *arg)
{
	return PyUnicode_FromString(self->language->name);
}

static PyGetSetDef Language_getset[] = {
	{"name", (getter)Language_get_name, NULL, drgn_Language_name_DOC, NULL},
	{},
};

PyTypeObject Language_type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	.tp_name = "_drgn.Language",
	.tp_basicsize = sizeof(Language),
	.tp_repr = (reprfunc)Language_repr,
	// Doesn't reference any objects, no GC needed.
	.tp_flags = Py_TPFLAGS_DEFAULT,
	.tp_doc = drgn_Language_DOC,
	.tp_getset = Language_getset,
};

static PyObject *languages_py[DRGN_NUM_LANGUAGES];

PyObject *Language_wrap(const struct drgn_language *language)
{
	PyObject *obj = languages_py[language->number];
	Py_INCREF(obj);
	return obj;
}

int language_converter(PyObject *o, void *p)
{
	const struct drgn_language **ret = p;

	if (o == Py_None) {
		*ret = NULL;
		return 1;
	} else if (PyObject_TypeCheck(o, &Language_type)) {
		*ret = ((Language *)o)->language;
		return 1;
	} else {
		PyErr_Format(PyExc_TypeError,
			     "expected Language, not %s",
			     Py_TYPE(o)->tp_name);
		return 0;
	}
}

int add_languages(void)
{
	static const char * const attr_names[] = {
		[DRGN_LANGUAGE_C] = "C",
		[DRGN_LANGUAGE_CPP] = "CPP",
	};
	static_assert(array_size(attr_names) == DRGN_NUM_LANGUAGES,
		      "missing language in attr_names");
	for (size_t i = 0; i < DRGN_NUM_LANGUAGES; i++) {
		Language *language_obj = call_tp_alloc(Language);
		if (!language_obj)
			return -1;
		language_obj->attr_name = attr_names[i];
		language_obj->language = drgn_languages[i];
		languages_py[i] = (PyObject *)language_obj;
		int ret = PyDict_SetItemString(Language_type.tp_dict,
					       attr_names[i],
					       (PyObject *)language_obj);
		if (ret)
			return ret;
	}
	return 0;
}
