/*
    Copyright (C) 1995-2025, The AROS Development Team. All rights reserved.

    Desc: X11 hidd handling mouse events.
*/

#include "x11_debug.h"

#define __OOP_NOATTRBASES__

#include <proto/utility.h>
#include <hidd/input.h>
#include <hidd/mouse.h>

#include "x11_types.h"
#include LC_LIBDEFS_FILE

/****************************************************************************************/

static OOP_AttrBase HiddInputAB;
static OOP_AttrBase HiddMouseAB;

static struct OOP_ABDescr attrbases[] =
{
    { IID_Hidd_Input, &HiddInputAB  },
    { IID_Hidd_Mouse, &HiddMouseAB  },
    { NULL          , NULL          }
};

/****************************************************************************************/

static ULONG xbutton2hidd(XButtonEvent *xb)
{
    ULONG button = vHidd_Mouse_NoButton;
    
    switch (xb->button)
    {
        case Button1:
            button = vHidd_Mouse_Button1;
            break;
        
        case Button2:
            button = vHidd_Mouse_Button3;
            break;
            
        case Button3:
            button = vHidd_Mouse_Button2;
            break;
        
    }
    
    return button;
}

/****************************************************************************************/

OOP_Object * X11Mouse__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
{
    BOOL has_mouse_hidd = FALSE;

    EnterFunc(bug("[X11:Mouse] New()\n"));
    ObtainSemaphoreShared( &XSD(cl)->sema);
    
    if (XSD(cl)->mousehidd)
        has_mouse_hidd = TRUE;
        
    ReleaseSemaphore( &XSD(cl)->sema);
    
    if (has_mouse_hidd) /* Cannot open twice */
        return NULL; /* Should have some error code here */

    o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
    if (o) {
        struct x11mouse_data *data = OOP_INST_DATA(cl, o);
        struct TagItem       *tag, *tstate;
        
        tstate = msg->attrList;
        while ((tag = NextTagItem(&tstate))) {
            ULONG idx;
            
            if (IS_HIDDINPUT_ATTR(tag->ti_Tag, idx)) {
                switch (idx) {
                    case aoHidd_Input_IrqHandler:
                        data->mouse_callback = (APTR)tag->ti_Data;
                        break;
                }
            }
            
        } /* while (tags to process) */

        OOP_GetAttr(o, aHidd_Input_IrqHandlerData, (IPTR *)&data->callbackdata);
        D(bug("[X11:Mouse] %s: callback data = %p\n", __func__, (APTR)data->callbackdata));

        /* Install the mouse hidd */
        ObtainSemaphore( &XSD(cl)->sema);
        XSD(cl)->mousehidd = o;
        ReleaseSemaphore( &XSD(cl)->sema);
    }
    
    return o;
}

/****************************************************************************************/

VOID X11Mouse__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
{
    EnterFunc(bug("[X11:Mouse] Dispose()\n"));

    ObtainSemaphore( &XSD(cl)->sema);
    XSD(cl)->mousehidd = NULL;
    ReleaseSemaphore( &XSD(cl)->sema);
    OOP_DoSuperMethod(cl, o, msg);
}

/****************************************************************************************/

VOID X11Mouse__Hidd_Mouse_X11__HandleEvent(OOP_Class *cl, OOP_Object *o, struct pHidd_Mouse_X11_HandleEvent *msg)
{

    struct x11mouse_data        *data = OOP_INST_DATA(cl, o);
    struct pHidd_Mouse_Event     e;

    DB2(bug("[X11:Mouse] HandleEvent()\n"));
    XButtonEvent *xb = &(msg->event->xbutton);
    
    e.x = xb->x;
    e.y = xb->y;
   
    if (msg->event->type == ButtonRelease)
    {
        switch(xb->button)
        {
            case Button1:
            case Button2:
            case Button3:
                e.button = xbutton2hidd(xb);
                e.type   = vHidd_Mouse_Release;
                data->mouse_callback(data->callbackdata, &e);
                break;
        }
    }
    else if (msg->event->type == ButtonPress)
    {
        switch(xb->button)
        {
            case Button1:
            case Button2:
            case Button3:
                e.button = xbutton2hidd(xb);
                e.type   = vHidd_Mouse_Press;
                data->mouse_callback(data->callbackdata, &e);
                break;
                
            case Button4:
                e.type   = vHidd_Mouse_WheelMotion;
                e.button = vHidd_Mouse_NoButton;
                e.x      = 0;
                e.y      = -1;
                data->mouse_callback(data->callbackdata, &e);
                break;
                
            case Button5:
                e.type   = vHidd_Mouse_WheelMotion;
                e.button = vHidd_Mouse_NoButton;
                e.x      = 0;
                e.y      = 1;
                data->mouse_callback(data->callbackdata, &e);
                break;
                
        }
    }
    else if (msg->event->type == MotionNotify)
    {
        e.button = vHidd_Mouse_NoButton;
        e.type = vHidd_Mouse_Motion;
        
        data->mouse_callback(data->callbackdata, &e);
    }
    
}

/****************************************************************************************/

#undef XSD
#define XSD(cl) (&LIBBASE->xsd)

/****************************************************************************************/

static int X11Mouse_Init(LIBBASETYPEPTR LIBBASE)
{
    return OOP_ObtainAttrBases(attrbases);
}

/****************************************************************************************/

static int X11Mouse_Expunge(LIBBASETYPEPTR LIBBASE)
{
    OOP_ReleaseAttrBases(attrbases);
    return TRUE;
}

/****************************************************************************************/

ADD2INITLIB(X11Mouse_Init, 0)
ADD2EXPUNGELIB(X11Mouse_Expunge, 0)
