/*
    example/example12.cpp -- overriding virtual functions from Python

    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#include "example.h"
#include <pybind11/functional.h>

/* This is an example class that we'll want to be able to extend from Python */
class Example12  {
public:
    Example12(int state) : state(state) {
        cout << "Constructing Example12.." << endl;
    }

    ~Example12() {
        cout << "Destructing Example12.." << endl;
    }

    virtual int run(int value) {
        std::cout << "Original implementation of Example12::run(state=" << state
                  << ", value=" << value << ")" << std::endl;
        return state + value;
    }

    virtual bool run_bool() = 0;
    virtual void pure_virtual() = 0;
private:
    int state;
};

/* This is a wrapper class that must be generated */
class PyExample12 : public Example12 {
public:
    using Example12::Example12; /* Inherit constructors */

    virtual int run(int value) {
        /* Generate wrapping code that enables native function overloading */
        PYBIND11_OVERLOAD(
            int,        /* Return type */
            Example12,  /* Parent class */
            run,        /* Name of function */
            value       /* Argument(s) */
        );
    }

    virtual bool run_bool() {
        PYBIND11_OVERLOAD_PURE(
            bool,         /* Return type */
            Example12,    /* Parent class */
            run_bool,     /* Name of function */
                          /* This function has no arguments. The trailing comma
                             in the previous line is needed for some compilers */
        );
        throw std::runtime_error("this will never be reached");
    }

    virtual void pure_virtual() {
        PYBIND11_OVERLOAD_PURE(
            void,         /* Return type */
            Example12,    /* Parent class */
            pure_virtual, /* Name of function */
                          /* This function has no arguments. The trailing comma
                             in the previous line is needed for some compilers */
        );
    }
};

int runExample12(Example12 *ex, int value) {
    return ex->run(value);
}

bool runExample12Bool(Example12* ex) {
    return ex->run_bool();
}

void runExample12Virtual(Example12 *ex) {
    ex->pure_virtual();
}

void init_ex12(py::module &m) {
    /* Important: use the wrapper type as a template
       argument to class_<>, but use the original name
       to denote the type */
    py::class_<PyExample12>(m, "Example12")
        /* Declare that 'PyExample12' is really an alias for the original type 'Example12' */
        .alias<Example12>()
        .def(py::init<int>())
        /* Reference original class in function definitions */
        .def("run", &Example12::run)
        .def("run_bool", &Example12::run_bool)
        .def("pure_virtual", &Example12::pure_virtual);

    m.def("runExample12", &runExample12);
    m.def("runExample12Bool", &runExample12Bool);
    m.def("runExample12Virtual", &runExample12Virtual);
}
