#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#define USE_NUM_GMP
#include "secp256k1.h"
#include "secp256k1_recovery.h"


secp256k1_context *secp256k1_precomp_context_sign;
secp256k1_context *secp256k1_precomp_context_verify;
static int context_exist = 0;



static PyObject *secp256k1_secp256k1_ec_pubkey_tweak_add(PyObject *self, PyObject *args) {
    Py_buffer pubkey;
    Py_buffer tweak;
    int flag;
    if (!PyArg_ParseTuple(args,"y*y*i", &pubkey, &tweak, &flag)) { return NULL; }
    secp256k1_pubkey data;
    int t = secp256k1_ec_pubkey_parse(secp256k1_precomp_context_sign, &data, pubkey.buf, pubkey.len);
    PyBuffer_Release(&pubkey);
    if (t==0) { return Py_BuildValue("b", -1); }

    t = secp256k1_ec_pubkey_tweak_add(secp256k1_context_no_precomp, &data, tweak.buf);
    PyBuffer_Release(&tweak);
    if (t==0) { return Py_BuildValue("b", -2); }

    size_t outl = 33;
    if (flag == 1) {
      outl = 33;
      flag = SECP256K1_EC_COMPRESSED;
    } else {
      outl = 65;
      flag = SECP256K1_EC_UNCOMPRESSED;
    }
    unsigned char pubkeyo[outl];
    t = secp256k1_ec_pubkey_serialize(secp256k1_context_no_precomp, pubkeyo, &outl, &data, flag);
    if (t != 1) { return Py_BuildValue("b", 0); }
    PyObject *return_value = Py_BuildValue("y#", pubkeyo, outl);
    Py_DECREF(pubkeyo);
    return return_value;
}

static PyObject *secp256k1_secp256k1_ecdsa_add_points(PyObject *self, PyObject *args) {
    Py_buffer a;
    Py_buffer b;
    int flag;
    if (!PyArg_ParseTuple(args,"y*y*i", &a, &b, &flag)) { return NULL; }
    secp256k1_pubkey data[2];
    int t = secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp, data, a.buf, a.len);
    PyBuffer_Release(&a);
    if (t==0) { return Py_BuildValue("b", -1); }

    t = secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp, data + 1, b.buf, b.len);
    PyBuffer_Release(&b);
    if (t==0) { return Py_BuildValue("b", -1); }
    secp256k1_pubkey out;
    const secp256k1_pubkey* d[2];
    d[0] = &data[0];
    d[1] = &data[1];

    t = secp256k1_ec_pubkey_combine(secp256k1_precomp_context_sign, &out,
                                    d, 2);

    size_t outl;
    if (flag == 1) {
      outl = 33;
      flag = SECP256K1_EC_COMPRESSED;
    } else {
      outl = 65;
      flag = SECP256K1_EC_UNCOMPRESSED;
    }
    unsigned char pubkeyo[outl];
    t = secp256k1_ec_pubkey_serialize(secp256k1_context_no_precomp, pubkeyo, &outl, &out, flag);
    if (t != 1) { return Py_BuildValue("b", 0); }
    PyObject *return_value = Py_BuildValue("y#", pubkeyo, outl);
    Py_DECREF(pubkeyo);
    return return_value;
}

static PyObject *secp256k1_secp256k1_ecdsa_signature_serialize_der(PyObject *self, PyObject *args) {
    Py_buffer sig;
    if (!PyArg_ParseTuple(args,"y*", &sig)) { return NULL; }
    secp256k1_ecdsa_signature signature;
    int t = secp256k1_ecdsa_signature_parse_compact(secp256k1_context_no_precomp,
                                                              &signature,
                                                              sig.buf);
    PyBuffer_Release(&sig);
    if (t==0) { return Py_BuildValue("b", 0); }
    unsigned char outputSer[72];
    size_t outputLen = 72;
    t = secp256k1_ecdsa_signature_serialize_der(secp256k1_context_no_precomp,
                                            outputSer,
                                            &outputLen,
                                            &signature);
    if (t==0) { return Py_BuildValue("b", 0); }
    PyObject *return_value = Py_BuildValue("y#", &outputSer, outputLen);
    Py_DECREF(outputSer);
    return return_value;
}

static PyObject *secp256k1_secp256k1_nonce_rfc6979(PyObject *self, PyObject *args) {
  unsigned char nonce[32];
  Py_buffer msg32;
  Py_buffer key32;
  unsigned int counter;
  if (!PyArg_ParseTuple(args,"y*y*b", &msg32, &key32, &counter)) {  return NULL; }

  int r = secp256k1_nonce_function_rfc6979(nonce, msg32.buf, key32.buf, NULL, NULL, counter);
  PyBuffer_Release(&msg32);
  PyBuffer_Release(&key32);
  if (r == 0 ) { return Py_BuildValue("b", 0); }
  PyObject *return_value = Py_BuildValue("y#", &nonce, 32);
  Py_DECREF(nonce);
  return return_value;
}

static PyObject *secp256k1_secp256k1_ecdsa_recover(PyObject *self, PyObject *args) {
    Py_buffer message;
    Py_buffer sig;
    int rec_id;
    int compressed;
    if (!PyArg_ParseTuple(args,"y*y*ii", &sig, &message, &rec_id, &compressed)) { return NULL; }

    secp256k1_ecdsa_signature signature;
    int r = secp256k1_ecdsa_signature_parse_der(secp256k1_context_no_precomp,
                                                &signature,
                                                sig.buf, sig.len);
    PyBuffer_Release(&sig);
    if (r != 1) { return Py_BuildValue("b", -1);}


    unsigned char compact_sig[64] ;
    r = secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_no_precomp,
                                                              compact_sig,
                                                              &signature);

    if (r != 1) { return Py_BuildValue("b", -1);}

    secp256k1_ecdsa_recoverable_signature signature_recoverable;
    r = secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_no_precomp,
                                                            &signature_recoverable,
                                                            compact_sig,
                                                            rec_id);
    if (r != 1) { return Py_BuildValue("b", -2);}

    secp256k1_pubkey pubkey;
    r = secp256k1_ecdsa_recover(secp256k1_precomp_context_verify,
                                &pubkey,
                                &signature_recoverable, message.buf);
    PyBuffer_Release(&message);
    if (r != 1) { return Py_BuildValue("b", 0);}

    size_t outl;
    if (compressed == 1) {
      outl = 33;
      compressed = SECP256K1_EC_COMPRESSED;
    } else {
      outl = 65;
      compressed = SECP256K1_EC_UNCOMPRESSED;
    }
    unsigned char pubkeyo[outl];
    r = secp256k1_ec_pubkey_serialize(secp256k1_context_no_precomp, pubkeyo, &outl, &pubkey, compressed);
    if (r != 1) {
      return Py_BuildValue("b", -3);
    }
    PyObject *return_value = Py_BuildValue("y#", pubkeyo, outl);
    Py_DECREF(pubkeyo);
    return return_value;
}

static PyObject *secp256k1_secp256k1_ecdsa_verify(PyObject *self, PyObject *args) {
    Py_buffer message;
    Py_buffer pub;
    Py_buffer sig;
    if (!PyArg_ParseTuple(args,"y*y*y*", &sig, &pub, &message)) { return NULL; }

    secp256k1_ecdsa_signature signature;
    secp256k1_pubkey pubkey;
    int r = secp256k1_ecdsa_signature_parse_der(secp256k1_context_no_precomp,
                                                &signature,
                                                sig.buf, sig.len);
    PyBuffer_Release(&sig);
    if (r != 1) { return Py_BuildValue("b", -1);}

    r = secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp,
                                  &pubkey,
                                  pub.buf, pub.len);
    PyBuffer_Release(&pub);

    if (r != 1) { return Py_BuildValue("b", -2);}

    r = secp256k1_ecdsa_verify(secp256k1_precomp_context_verify,
                               &signature,
                               message.buf,
                               &pubkey);
    PyBuffer_Release(&message);
    if (r != 1) { return Py_BuildValue("b", 0);}
    return Py_BuildValue("b", 1);
}

static PyObject *secp256k1_secp256k1_context_create(PyObject *self, PyObject *args) {
  if (context_exist == 0 ) {
      secp256k1_context *s = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
      secp256k1_context *v = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
      secp256k1_precomp_context_sign = s;
      secp256k1_precomp_context_verify = v;
      context_exist = 1;
      return Py_BuildValue("b", 1);
  }
  return Py_BuildValue("b", 0);
}

static PyObject *secp256k1_secp256k1_context_randomize(PyObject *self, PyObject *args) {
    Py_buffer buffer;
    if (!PyArg_ParseTuple(args,"y*", &buffer)) {  return NULL; }
    int r = 0;
    r += secp256k1_context_randomize(secp256k1_precomp_context_sign, buffer.buf);
    r += secp256k1_context_randomize(secp256k1_precomp_context_sign, buffer.buf);
    PyBuffer_Release(&buffer);
    if (r == 2) { return Py_BuildValue("b", 1); }
    else { return Py_BuildValue("b", 0); }

}

static PyObject *secp256k1_secp256k1_ec_pubkey_create(PyObject *self, PyObject *args) {
    int flag;
    Py_buffer buffer;
    if (!PyArg_ParseTuple(args,"y*i", &buffer, &flag)) { return NULL; }
    secp256k1_pubkey pubkey;

    int r = 0;
    r = secp256k1_ec_pubkey_create(secp256k1_precomp_context_sign, &pubkey, buffer.buf);
    PyBuffer_Release(&buffer);
    if (r != 1) {
      return Py_BuildValue("b", 0);
    }
    size_t outl;
    if (flag == 1) {
      outl = 33;
      flag = SECP256K1_EC_COMPRESSED;
    } else {
      outl = 65;
      flag = SECP256K1_EC_UNCOMPRESSED;
    }
    unsigned char pubkeyo[outl];

    r = secp256k1_ec_pubkey_serialize(secp256k1_context_no_precomp, pubkeyo, &outl, &pubkey, flag);
    if (r != 1) {
      return Py_BuildValue("b", 0);
    }
    PyObject *return_value =  Py_BuildValue("y#", pubkeyo, outl);
    Py_DECREF(pubkeyo);
    return return_value;
}

static PyObject *secp256k1_secp256k1_ecdsa_sign(PyObject *self, PyObject *args) {
    Py_buffer msg;
    Py_buffer private_key;
    int der_encoding;
    if (!PyArg_ParseTuple(args,"y*y*b", &msg, &private_key, &der_encoding)) {
      return NULL;
    }
    secp256k1_ecdsa_recoverable_signature signature;
    int r=1;
    r = secp256k1_ecdsa_sign_recoverable(secp256k1_precomp_context_sign,
                                         &signature,
                                         msg.buf,
                                         private_key.buf,
                                         NULL, NULL);
    PyBuffer_Release(&private_key);
    PyBuffer_Release(&msg);
    if (r != 1) {
      return Py_BuildValue("b", 0);
    }
    if (der_encoding == 0) {
        PyObject *return_value =  Py_BuildValue("y#", &signature, 65);
//        Py_DECREF(signature);
        return return_value;
    } else {
        unsigned char outputSer[72];
        size_t outputLen = 72;
        secp256k1_ecdsa_signature_serialize_der(secp256k1_context_no_precomp,
                                                outputSer,
                                                &outputLen,
                                                (const secp256k1_ecdsa_signature *)&signature);
        PyObject *return_value = Py_BuildValue("y#", &outputSer, outputLen);
        Py_DECREF(outputSer);
        return return_value;
    }
}


static PyMethodDef module_methods[] = {
    {"secp256k1_context_randomize", secp256k1_secp256k1_context_randomize, METH_VARARGS, "Randomize context"},
    {"secp256k1_context_create", secp256k1_secp256k1_context_create, METH_VARARGS, "Returns context"},
    {"secp256k1_ec_pubkey_create", secp256k1_secp256k1_ec_pubkey_create, METH_VARARGS, "Returns public key"},
    {"secp256k1_ecdsa_sign", secp256k1_secp256k1_ecdsa_sign, METH_VARARGS, "Sign message"},
    {"secp256k1_ecdsa_verify", secp256k1_secp256k1_ecdsa_verify, METH_VARARGS, "Verify signature"},
    {"secp256k1_ecdsa_recover", secp256k1_secp256k1_ecdsa_recover, METH_VARARGS, "Recover public key from signature"},
    {"secp256k1_nonce_rfc6979", secp256k1_secp256k1_nonce_rfc6979, METH_VARARGS, "Create rfc6979 nonce"},
    {"secp256k1_ecdsa_signature_serialize_der", secp256k1_secp256k1_ecdsa_signature_serialize_der, METH_VARARGS, "Serialize to DER"},
    {"secp256k1_ecdsa_add_points", secp256k1_secp256k1_ecdsa_add_points, METH_VARARGS, "2 points addition"},
    {"secp256k1_ec_pubkey_tweak_add", secp256k1_secp256k1_ec_pubkey_tweak_add, METH_VARARGS, "tweak addition "},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef _module_secp256k1 = {
    PyModuleDef_HEAD_INIT,
    "_secp256k1",
    "...",
    -1,
    module_methods
};



PyMODINIT_FUNC PyInit__secp256k1(void) {
    PyObject *m;
    m = PyModule_Create(&_module_secp256k1);
    PyModule_AddObject(m, "SECP256K1_CONTEXT_NO_PRECOMP", PyCapsule_New(&secp256k1_context_no_precomp,
                                                                        "secp256k1_context", NULL));
    PyModule_AddIntMacro(m, SECP256K1_CONTEXT_VERIFY);
    PyModule_AddIntMacro(m, SECP256K1_CONTEXT_SIGN);
    PyModule_AddIntMacro(m, SECP256K1_CONTEXT_NONE);
    PyModule_AddIntMacro(m, SECP256K1_EC_COMPRESSED);
    PyModule_AddIntMacro(m, SECP256K1_EC_UNCOMPRESSED);
    PyModule_AddIntMacro(m, SECP256K1_TAG_PUBKEY_EVEN);
    PyModule_AddIntMacro(m, SECP256K1_TAG_PUBKEY_ODD);
    PyModule_AddIntMacro(m, SECP256K1_TAG_PUBKEY_UNCOMPRESSED);
    PyModule_AddIntMacro(m, SECP256K1_TAG_PUBKEY_HYBRID_EVEN);
    PyModule_AddIntMacro(m, SECP256K1_TAG_PUBKEY_HYBRID_ODD);

    return m;
}

