#ifndef PGS_PY_H
#define PGS_PY_H
#define PY_SSIZE_T_CLEAN 1
#include "pgs_api.h"
#include <Python.h>
#include <stdbool.h>

PyObject *_pModule = NULL;
bool python_initialized = false;

PyObject *py_construct() {

  PyStatus status;
  PyConfig config;
  if (!python_initialized) {
    if (PyImport_AppendInittab("pgs", PyInit_mymodule) == -1) {
      fprintf(stderr, "Failed to add mymodule to the interpreter's table\n");
      return NULL;
    }
    PyConfig_InitPythonConfig(&config);

    /* optional but recommended */
    status = PyConfig_SetBytesString(&config, &config.program_name, "pgs");
    if (PyStatus_Exception(status)) {
      goto exception;
    }

    status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status)) {
      goto exception;
    }
    PyConfig_Clear(&config);
    // Py_Initialize();
    if (!Py_IsInitialized()) {
      fprintf(stderr, "Python initialization failed!\n");
      return NULL;
    }
    PyGILState_STATE gstate = PyGILState_Ensure();

    PyRun_SimpleString("import pgs");

    // Py_InitModule("pgscript", NULL);
    python_initialized = true;
    PyObject *sysPath = PySys_GetObject("path");
    PyList_Append(sysPath, PyUnicode_FromString("."));
    // Py_DECREF(sysPath);
    PyObject *pName = PyUnicode_DecodeFSDefault("pgscript");
    _pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    PyGILState_Release(gstate);
    python_initialized = true;
  }
  return _pModule;
exception:
  PyConfig_Clear(&config);
  Py_ExitStatusException(status);
  return NULL;
}

void py_destruct() {
  if (!python_initialized)
    return;
  Py_DECREF(_pModule);
  Py_Finalize();
  python_initialized = false;
}

char py_on_headers(int downstream, char *headers) {
  PyObject *pModule = py_construct();
  char *new_headers = NULL;
  if (pModule != NULL) {
    PyObject *pFunc = PyObject_GetAttrString(pModule, "on_headers");
    if (PyCallable_Check(pFunc)) {
      PyObject *pArgs = PyTuple_Pack(2, PyLong_FromLong(downstream),
                                     PyUnicode_FromString(headers));
      PyGILState_STATE gstate = PyGILState_Ensure();
      new_headers = PyBytes_AsString(PyObject_CallObject(pFunc, pArgs));
      PyGILState_Release(gstate);
      Py_DECREF(pArgs);
    }
  }
  return new_headers ? new_headers : headers;
}

char * py_http_intercept_headers(int sock, char * headers){
  PyObject *pModule = py_construct();
  char *new_headers = NULL;
  if (pModule != NULL) {
    PyObject *pFunc = PyObject_GetAttrString(pModule, "http_intercept_headers");
    if (PyCallable_Check(pFunc)) {
      PyObject *pArgs = PyTuple_Pack(2, PyLong_FromLong(sock),
                                     PyUnicode_FromString(headers));
      PyGILState_STATE gstate = PyGILState_Ensure();
      new_headers = PyBytes_AsString(PyObject_CallObject(pFunc, pArgs));
      PyGILState_Release(gstate);
      Py_DECREF(pArgs);
    }
  }
  return new_headers ? new_headers : headers;
}

int py_on_connect(int downstream) {
  PyObject *pModule = py_construct();
  int upstream_fd = -1;
  if (pModule != NULL) {
    PyObject *pFunc = PyObject_GetAttrString(pModule, "on_connect");
    if (PyCallable_Check(pFunc)) {
      PyObject *pArgs = PyTuple_Pack(1, PyLong_FromLong(downstream));

      PyGILState_STATE gstate = PyGILState_Ensure();

      PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
      PyGILState_Release(gstate);

      Py_DECREF(pArgs);
      if (pValue != NULL) {
        upstream_fd = PyLong_AsLong(pValue);
        Py_DECREF(pValue);
      } else {
        PyErr_Print();
        fprintf(stderr, "Call failed\n");
      }
    } else {
      PyErr_Print();
      fprintf(stderr, "Cannot find function 'route'\n");
    }

    Py_XDECREF(pFunc);
  } else {
    PyErr_Print();
    fprintf(stderr, "Failed to load 'script'\n");
  }

  return upstream_fd;
}

#endif