# Allow user to move the current selection.
# Author: Andrew Trevorrow (andrew@trevorrow.com), Jan 2011.

import golly as g

# set edges of bounded grid for later use
if g.getwidth() > 0:
    gridl = -int(g.getwidth()/2)
    gridr = gridl + g.getwidth() - 1
if g.getheight() > 0:
    gridt = -int(g.getheight()/2)
    gridb = gridt + g.getheight() - 1

helpmsg = " (hit 'h' for help)"

# --------------------------------------------------------------------

def showhelp1():
    g.note(
 """Hit the escape key to abort the script.

 Note that alt-clicking in the selection allows you to COPY it
 to another location (the original selection is not deleted).""")

# --------------------------------------------------------------------

def showhelp2():
    g.note(
 """While moving the selection the following keys can be used:

 x -- flip selection left-right
 y -- flip selection top-bottom
 > -- rotate selection clockwise
 < -- rotate selection anticlockwise
 h -- show this help
 escape -- abort and restore the selection""")

# ------------------------------------------------------------------------------

def cellinrect(x, y, r):
    # return True if given cell position is inside given rectangle
    if x < r[0] or x >= r[0] + r[2]: return False
    if y < r[1] or y >= r[1] + r[3]: return False
    return True

# ------------------------------------------------------------------------------

def rectingrid(r):
    # return True if all of given rectangle is inside grid
    if g.getwidth() > 0 and (r[0] < gridl or r[0] + r[2] - 1 > gridr): return False
    if g.getheight() > 0 and (r[1] < gridt or r[1] + r[3] - 1 > gridb): return False
    return True

# ------------------------------------------------------------------------------

def lookforkeys(event, deltax, deltay):
    global oldcells, selrect, selpatt

    # look for keys used to flip/rotate selection
    if event == "key x none" or event == "key y none":
        # flip floating selection left-right or top-bottom
        if len(oldcells) > 0:
            g.clear(0)
            g.putcells(selpatt, deltax, deltay)
        if " x " in event:
            g.flip(0)
        else:
            g.flip(1)
        selpatt = g.transform(g.getcells(selrect), -deltax, -deltay)
        if len(oldcells) > 0:
            g.clear(0)
            g.putcells(oldcells)
            g.putcells(selpatt, deltax, deltay)
        g.update()
        return

    if event == "key > none" or event == "key < none":
        # rotate floating selection clockwise or anticlockwise;
        # because we use g.rotate below we have to use the exact same
        # calculation (see Selection::Rotate in wxselect.cpp) for rotrect:
        midx = selrect[0] + int((selrect[2]-1)/2)
        midy = selrect[1] + int((selrect[3]-1)/2)
        newleft = midx + selrect[1] - midy
        newtop = midy + selrect[0] - midx
        rotrect = [ newleft, newtop, selrect[3], selrect[2] ]
        if not rectingrid(rotrect):
            g.warn("Rotation is not allowed if selection would be outside grid.")
            return
        g.clear(0)
        if len(oldcells) > 0: g.putcells(oldcells)
        oldcells = g.join(oldcells, g.getcells(rotrect))
        g.clear(0)
        g.select(rotrect)
        g.clear(0)
        g.select(selrect)
        g.putcells(selpatt, deltax, deltay)
        if " > " in event:
            g.rotate(0)
        else:
            g.rotate(1)
        selrect = g.getselrect()
        if selrect != rotrect: g.warn("Bug: selrect != rotrect")
        selpatt = g.transform(g.getcells(selrect), -deltax, -deltay)
        if len(oldcells) > 0:
            g.clear(0)
            g.putcells(oldcells)
            g.putcells(selpatt, deltax, deltay)
        g.update()
        return

    if event == "key h none":
        showhelp2()
        return

    g.doevent(event)

# ------------------------------------------------------------------------------

def moveselection():
    global oldcells, selrect, selpatt

    # wait for 1st click in selection
    while True:
        event = g.getevent()
        if event.startswith("click"):
            # event is a string like "click 10 20 left none"
            evt, xstr, ystr, butt, mods = event.split()
            x = int(xstr)
            y = int(ystr)
            if cellinrect(x, y, selrect):
                oldmouse = xstr + ' ' + ystr
                firstx = x
                firsty = y
                xoffset = firstx - selrect[0]
                yoffset = firsty - selrect[1]
                if mods == "alt":
                    # don't delete pattern in selection
                    oldcells = g.getcells(selrect)
                break
        elif event == "key h none":
            showhelp1()
        else:
            g.doevent(event)

    # wait for 2nd click while moving selection
    g.show("Move mouse and click again..." + helpmsg)
    gotclick = False
    while not gotclick:
        event = g.getevent()
        if event.startswith("click"):
            evt, x, y, butt, mods = event.split()
            mousepos = x+' '+y
            gotclick = True
        else:
            if len(event) > 0:
                lookforkeys(event, x - firstx, y - firsty)
                # update xoffset,yoffset in case selection was rotated
                xoffset = x - selrect[0]
                yoffset = y - selrect[1]
            mousepos = g.getxy()
        if len(mousepos) > 0 and mousepos != oldmouse:
            # mouse has moved, so move selection rect and pattern
            g.clear(0)
            if len(oldcells) > 0: g.putcells(oldcells)
            xstr, ystr = mousepos.split()
            x = int(xstr)
            y = int(ystr)
            selrect[0] = x - xoffset
            selrect[1] = y - yoffset

            if g.getwidth() > 0:
                # ensure selrect doesn't move beyond left/right edge of grid
                if selrect[0] < gridl:
                    selrect[0] = gridl
                    x = selrect[0] + xoffset
                elif selrect[0] + selrect[2] - 1 > gridr:
                    selrect[0] = gridr + 1 - selrect[2]
                    x = selrect[0] + xoffset
            if g.getheight() > 0:
                # ensure selrect doesn't move beyond top/bottom edge of grid
                if selrect[1] < gridt:
                    selrect[1] = gridt
                    y = selrect[1] + yoffset
                elif selrect[1] + selrect[3] - 1 > gridb:
                    selrect[1] = gridb + 1 - selrect[3]
                    y = selrect[1] + yoffset

            g.select(selrect)
            oldcells = g.getcells(selrect)
            g.putcells(selpatt, x - firstx, y - firsty)
            oldmouse = mousepos
            g.update()

# ------------------------------------------------------------------------------

selrect = g.getselrect()
if len(selrect) == 0: g.exit("There is no selection.")
selpatt = g.getcells(selrect)

# remember initial selection in case user aborts script
firstrect = g.getselrect()
firstpatt = g.getcells(selrect)

g.show("Click anywhere in selection, move mouse and click again..." + helpmsg)
oldcursor = g.getcursor()
g.setcursor("Move")
oldcells = []

try:
    aborted = True
    moveselection()
    aborted = False
finally:
    g.setcursor(oldcursor)
    if aborted:
        g.clear(0)
        if len(oldcells) > 0: g.putcells(oldcells)
        g.putcells(firstpatt)
        g.select(firstrect)
    else:
        g.show(" ")
