import curses

from visidata import vd, VisiData, BaseSheet, Sheet, AttrDict, dispwidth


# registry of mouse events.  cleared before every draw cycle.
vd.mousereg = []  # list of AttrDict(y=, x=, h=, w=, buttonfuncs=dict)

# sheet mouse position for current mouse event
BaseSheet.init('mouseX', int)
BaseSheet.init('mouseY', int)
# sheet info for a click that may become a drag
BaseSheet.init('drag1')


@VisiData.after
def initCurses(vd):
    if not getattr(curses, 'mousemask', None):
      return
    curses.MOUSE_ALL = 0xffffffff
    curses.mouseEvents = {}

    if not vd.enableMouse(bool(vd.options.mouse_interval)):
        return

    curses.def_prog_mode()
    curses.mouseinterval(vd.options.mouse_interval)

    for k in dir(curses):
        if k.startswith('BUTTON') or k in ('REPORT_MOUSE_POSITION', '2097152'):
            curses.mouseEvents[getattr(curses, k)] = k


@VisiData.api
def enableMouse(vd, b:bool) -> bool:  #2913 #2851
    'Call curses.mousemask(all if b else 0) only if available.  Return True if mouse enabled.'
    if not hasattr(curses, 'mousemask'):
        return False
    mm, _ = curses.mousemask(getattr(curses, 'MOUSE_ALL', 0xffffffff) if b else 0)
    return bool(mm)


@VisiData.after
def clearCaches(vd):
    vd.mousereg = []


@VisiData.api
def onMouse(vd, scr, x, y, w, h, **kwargs):
    px, py = vd.getrootxy(scr)
    e = AttrDict(x=x+px, y=y+py, w=w, h=h, buttonfuncs=kwargs)
    vd.mousereg.append(e)


@VisiData.api
def getMouse(vd, _x, _y, button):
    for reg in vd.mousereg[::-1]:
        if reg.x <= _x < reg.x+reg.w and reg.y <= _y < reg.y+reg.h and button in reg.buttonfuncs:
            return reg.buttonfuncs[button]


@VisiData.api
def parseMouse(vd, **kwargs):
    'Return list of mouse interactions (clicktype, y, x, name, scr) for curses screens given in kwargs as name:scr.'

    devid, x, y, z, bstate = curses.getmouse()

    clicktype = ''
    if bstate & curses.BUTTON_CTRL:
        clicktype += "Ctrl+"
        bstate &= ~curses.BUTTON_CTRL
    if bstate & curses.BUTTON_ALT:
        clicktype += "Alt+"
        bstate &= ~curses.BUTTON_ALT
    if bstate & curses.BUTTON_SHIFT:
        clicktype += "Shift+"
        bstate &= ~curses.BUTTON_SHIFT

    keystroke = clicktype + curses.mouseEvents.get(bstate, str(bstate))
    ret = AttrDict(keystroke=keystroke, y=y, x=x, found=[])
    for winname, winscr in kwargs.items():
        if not winscr:
            continue
        px, py = vd.getrootxy(winscr)
        mh, mw = winscr.getmaxyx()
        if py <= y < py+mh and px <= x < px+mw:
            ret.found.append(winname)
#            vd.debug(f'{keystroke} at ({x-px}, {y-py}) in window {winname} {winscr}')

    return ret


@VisiData.api
def handleMouse(vd, sheet):
    r = None
    try:
        vd.keystrokes = ''
        pct = vd.windowConfig['pct']
        topPaneActive = ((vd.activePane == 2 and pct < 0)  or (vd.activePane == 1 and pct > 0))
        bottomPaneActive = ((vd.activePane == 1 and pct < 0)  or (vd.activePane == 2 and pct > 0))
        r = vd.parseMouse(top=vd.winTop, bot=vd.winBottom, menu=vd.scrMenu)
        if (bottomPaneActive and 'top' in r.found) or (topPaneActive and 'bot' in r.found):
            vd.activePane = 1 if vd.activePane == 2 else 2
            sheet = vd.activeSheet

        f = vd.getMouse(r.x, r.y, r.keystroke)
        winx, winy = vd.getrootxy(sheet._scr)
        sheet.mouseX, sheet.mouseY = r.x-winx, r.y-winy
        if f:
            if isinstance(f, str):
                if f.startswith('onclick'):
                    if '://' in f:
                        vd.launchBrowser(f[8:])
                    else:
                        sheet.execCommand(f[8:])
                else:
                    for cmd in f.split():
                        sheet.execCommand(cmd)
            else:
                f(r.y, r.x, r.keystroke)

            vd.keystrokes = vd.prettykeys(r.keystroke)
            return ''  #  handled
    except curses.error:
        pass

    return r.keystroke if r else ''


@Sheet.api
def visibleColInfoAtX(sheet, x):
    '''return (vcolidx, is_separator) for the x-coordinate'''
    for vcolidx, (colx, w) in sheet._visibleColLayout.items():
        if vcolidx == sheet.nVisibleCols-1:
            sep = vd.options.disp_rowend_sep
        elif (sheet.keyCols and sheet.availCols[vcolidx] is sheet.keyCols[-1]):
            sep = vd.options.disp_keycol_sep
        else:
            sep = vd.options.disp_column_sep
        sepw = dispwidth(sep, literal=True)
        if colx <= x <= colx+w+sepw-1:
            if colx <= x <= colx+w-1:  #in the cell
                return (vcolidx, False)
            else:   #in the end-of-column separator
                return (vcolidx, True)
    return (None, False)


@Sheet.api
def visibleRowAtY(sheet, y):
    for rowidx, (rowy, h) in sheet._rowLayout.items():
        if rowy <= y <= rowy+h-1:
            return rowidx


Sheet.addCommand('BUTTON1_PRESSED', 'go-mouse-1', 'go_mouse(drag_button=1)', 'set cursor to row and column where mouse was clicked, or start a drag event')
Sheet.bindkey('BUTTON1_CLICKED', 'go-mouse-1')
Sheet.addCommand('BUTTON1_RELEASED', 'check-drag', 'drag_button1()', 'resize column if col separator has been dragged')
Sheet.addCommand('BUTTON3_PRESSED', 'go-mouse', 'go_mouse()', 'set cursor to row and column where mouse was clicked')
@Sheet.api
def go_mouse(sheet, drag_button=None):
    if sheet.mouseY == sheet.windowHeight-1:
        return
    ridx = sheet.visibleRowAtY(sheet.mouseY)
    if ridx is not None:
        sheet.cursorRowIndex = ridx
    cidx, is_column = sheet.visibleColInfoAtX(sheet.mouseX)
    if cidx is not None:
        sheet.cursorVisibleColIndex = cidx
        if drag_button == 1 and is_column:
            # save info for a possible drag event
            sheet.drag1 = (sheet.availCols[cidx], sheet.mouseX)
        else:
            sheet.drag1 = None

@Sheet.api
def drag_button1(sheet):
    if not sheet.drag1:
        return
    delta_w = sheet.mouseX - sheet.drag1[1]
    col = sheet.drag1[0]
    sheet.drag1 = None
    if col.width and col.width > 0:
        new_w = col.width + delta_w
        if new_w > 0:
            col.setWidth(new_w)

Sheet.addCommand(None, 'scroll-mouse', 'sheet.topRowIndex=cursorRowIndex-mouseY+1', 'scroll to mouse cursor location')

Sheet.addCommand('ScrollUp', 'scroll-up', 'cursorDown(options.scroll_incr); sheet.topRowIndex += options.scroll_incr', 'scroll up by one row increment')
Sheet.addCommand('ScrollDown', 'scroll-down', 'cursorDown(-options.scroll_incr); sheet.topRowIndex -= options.scroll_incr', 'scroll down by one row increment')
