from abcontroller import ABController
from collections import namedtuple

TCase = namedtuple("TCase", ["currentPosition", "requestedPosition"])
AB = namedtuple("AB", ["a", "b"])
abLimits = AB(0.2, 0.4)

def checkLimits(uut, aLimit, bLimit, fail=False):
    tests = [
        TCase(aLimit - 0.1, None),
        TCase(aLimit, None),
        TCase(bLimit - 0.1, None),
        TCase(bLimit, None),
        TCase(bLimit + 0.1, aLimit if not fail else None)
    ]

    requestedPosition = None
    def callback(newPosition):
        nonlocal requestedPosition
        requestedPosition = newPosition
    
    originalCallback = uut._setPositionCallback
    uut._setPositionCallback = callback

    for t in tests:
        uut.positionChanged(t.currentPosition)
        assert requestedPosition == t.requestedPosition

    uut._setPositionCallback = originalCallback

def test_oneSetOfLimits():
    song = "/path/to/song"

    uut = ABController()
    uut.setCurrentSong(song)
    uut.storeLimits(abLimits.a, abLimits.b)
    uut.loadLimits(0)

    checkLimits(uut, abLimits.a, abLimits.b)
    assert uut.getLimits(song) == [abLimits]

def test_multipleSetsOfLimits():
    song = "/path/to/song"
    abLimits = [
        AB(0.2, 0.4),
        AB(0.3, 0.5),
        AB(0.0, 1.2)
    ]

    uut = ABController()
    uut.setCurrentSong(song)
    for l in abLimits:
        uut.storeLimits(l.a, l.b)
    
    for i, l in enumerate(abLimits):
        uut.loadLimits(i)
        checkLimits(uut, l.a, l.b)

    assert uut.getLimits(song) == abLimits

def test_multipleSongs():
    songs = [
        "/path/to/song",
        "/path/to/another/song"
    ]
    abLimits = [
        AB(0.2, 0.4),
        AB(0.3, 0.5)
    ]
    uut = ABController()
    for i, s in enumerate(songs):
        uut.storeLimits(abLimits[i].a, abLimits[i].b, s)

    for i, s in enumerate(songs):
        uut.setCurrentSong(s)
        uut.loadLimits(0)
        
        checkLimits(uut, abLimits[i].a, abLimits[i].b)
        assert uut.getLimits(s) == [abLimits[i]]

def test_disableAbRepeat():
    song = "/path/to/song"

    uut = ABController()
    uut.setCurrentSong(song)
    uut.storeLimits(abLimits.a, abLimits.b)
    uut.loadLimits(0)

    uut.setEnable(False)
    checkLimits(uut, abLimits.a, abLimits.b, fail=True)

    uut.setEnable(True)
    checkLimits(uut, abLimits.a, abLimits.b)

def test_storeLimitsToSpecificSong():
    song = "/path/to/song"

    uut = ABController()
    uut.storeLimits(abLimits.a, abLimits.b, song)
    uut.setCurrentSong(song)
    uut.loadLimits(0)

    checkLimits(uut, abLimits.a, abLimits.b)

def test_storeLimitsWithoutCurrentSong():
    uut = ABController()
    uut.storeLimits(abLimits.a, abLimits.b)
    uut.loadLimits(0)

    checkLimits(uut, abLimits.a, abLimits.b, fail=True)
    
def test_storeLimitsToSongWithoutCurrentSong():
    song = "/path/to/song"
    uut = ABController()
    uut.storeLimits(abLimits.a, abLimits.b, song)
    uut.loadLimits(0)

    checkLimits(uut, abLimits.a, abLimits.b, fail=True)

    uut.setCurrentSong(song)

    checkLimits(uut, abLimits.a, abLimits.b, fail=True)

    uut.loadLimits(0)
    
    checkLimits(uut, abLimits.a, abLimits.b)
    
def test_storeLimitsToCurrentSongButDoNotSetCurrentLimits():
    song = "/path/to/song"
    uut = ABController()
    uut.setCurrentSong(song)
    uut.storeLimits(abLimits.a, abLimits.b)

    checkLimits(uut, abLimits.a, abLimits.b, fail=True)

    uut.loadLimits(0)

    checkLimits(uut, abLimits.a, abLimits.b)
    
def test_getLimitsOfInexistentSong():
    song = "/path/to/song"
    uut = ABController()
    assert uut.getLimits(song) == None

def test_clearAbController():
    songsWithLimits = [
        ("/path/to/song", AB(0.2, 0.4)),
        ("/path/to/another/song", AB(0.3, 0.5))
    ]

    uut = ABController()
    for s in songsWithLimits:
        uut.storeLimits(s[1].a, s[1].b, s[0])

    for i, s in enumerate(songsWithLimits):
        assert uut.getLimits(s[0]) == [s[1]]

    uut.clear()

    for i, s in enumerate(songsWithLimits):
        assert uut.getLimits(s[0]) == None
    
def test_setTemporaryLimits():
    pass

def test_defaultBehaviour():
    uut = ABController()