#include "PyColor.h"

PyGetSetDef PyColor::getsetters[] = {
    {"r", (getter)PyColor::get_member, (setter)PyColor::set_member, "Red component",   (void*)0},
    {"g", (getter)PyColor::get_member, (setter)PyColor::set_member, "Green component", (void*)1},
    {"b", (getter)PyColor::get_member, (setter)PyColor::set_member, "Blue component",  (void*)2},
    {"a", (getter)PyColor::get_member, (setter)PyColor::set_member, "Alpha component", (void*)3},
    {NULL}
};

PyColor::PyColor(_PyColorData d)
{
    data = d;
    if (data.index == PyColor::SELF_OWNED) data.index = PyColor::BORROWED; // I think this is a bad idea, but the alternatives come with their own mess
}

PyColor::PyColor(sf::Color* target, std::weak_ptr<UIDrawable> parent, int index)
{
    data.index = index;
    data.parent = parent;
    data.target = target;
    data.setter = NULL;
    data.getter = NULL;
}

PyColor::PyColor(sf::Color target)
{
    data.index = PyColor::SELF_OWNED;
    data.parent = std::weak_ptr<UIDrawable>();
    data.target = new sf::Color;
    data.setter = NULL;
    data.getter = NULL;
}

PyColor::PyColor(sf::Color (*getter)(), void (*setter)(sf::Color), std::weak_ptr<UIDrawable> parent, int index)
{
    data.index = index;
    data.parent = parent;
    data.target = NULL;
    data.setter = setter;
    data.getter = getter;

}

PyColor::~PyColor()
{
    if (data.index == PyColor::SELF_OWNED)
        delete data.target;
}

PyObject* PyColor::pyObject()
{
    PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyColorType, 0);
    PyColorObject* self = (PyColorObject*)obj;
    self->data = data;
    if (data.index == PyColor::SELF_OWNED) self->data.index = PyColor::BORROWED;
    return obj;
}

PyColor PyColor::fromPy(PyObject* obj)
{
    PyColorObject* self = (PyColorObject*)obj;
    return PyColor(self->data);
}

PyColor PyColor::fromPy(PyColorObject* self)
{
    return PyColor(self->data);
}

void PyColor::set(sf::Color color)
{
    auto ptr = data.parent.lock();
    if (ptr || data.index == PyColor::SELF_OWNED)
    {
        if (data.setter)
            data.setter(color);
        else
            *data.target = color;
    }
}

sf::Color PyColor::get()
{
    auto ptr = data.parent.lock();
    if (ptr || data.index == PyColor::SELF_OWNED)
    {
        if (data.getter)
            return data.getter();
        else
            return *(data.target);
    }
    return sf::Color(0, 0, 0, 0);
}

bool PyColor::alive()
{
    if (data.index == PyColor::SELF_OWNED) return true;
    return !data.parent.lock();
}

std::string PyColor::field()
{
    switch (data.index)
    {
        case PyColor::SELF_OWNED:
            return "None";
            break;
        case 0:
            return "fill";
            break;
        case 1:
            return "outline";
            break;
        case 2:
            return "background";
            break;
        default:
            return "unknown";
            break;
    }
}

std::string PyColor::mode()
{
    if (data.index == PyColor::SELF_OWNED) return "self-owned value";
    else if (data.index == PyColor::BORROWED) return "self-owned value (borrowed)";
    else if (data.getter || data.setter)
    {
        if (alive()) return "callback passthrough";
        else return "callback passthrough (dangling; parent dead)";
    }
    else if (alive()) return "linked pointer";
    else return "linked pointer (dangling; parent dead)";
}

Py_hash_t PyColor::hash(PyObject* obj)
{
    auto self = (PyColorObject*)obj;
    Py_hash_t value = 0;
    auto ptr = self->data.parent.lock();
    if (ptr || self->data.index == PyColor::SELF_OWNED)
    {
        value += self->data.target->r;
        value << 8; value += self->data.target->g; 
        value << 8; value += self->data.target->b; 
        value << 8; value += self->data.target->a;
    }
    if (ptr)
    {
        //value << (sizeof(*UIDrawable) * 8);
        value += reinterpret_cast<Py_hash_t>(&ptr);
    } 

    return value;
}

PyObject* PyColor::repr(PyObject* obj)
{
    PyColorObject* self = (PyColorObject*)obj;
    std::ostringstream ss;
    PyColor color = PyColor(self->data);
    sf::Color c = color.get();
    ss << "<Color (" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ") " << color.mode() << ">";

    std::string repr_str = ss.str();
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}


int PyColor::init(PyColorObject* self, PyObject* args, PyObject* kwds)
{
    static const char* keywords[] = { "r", "g", "b", "a", nullptr };
    return 0;
}

PyObject* PyColor::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
    return (PyObject*)type->tp_alloc(type, 0);
}

PyObject* PyColor::get_member(PyObject* obj, void* closure)
{
    return Py_None;
}

int PyColor::set_member(PyObject* obj, PyObject* value, void* closure)
{
    return 0;
}
