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, tests):
    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 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)
    ]
    _checkLimits(uut, tests)
    if not fail:
        assert uut.getCurrentLimits()[0] == aLimit
        assert uut.getCurrentLimits()[1] == bLimit

def checkDefaultLimits(uut):
    tests = [
        TCase(0.0, None),
        TCase(0.1, 0.0),
        TCase(0.5, 0.0)
    ]
    _checkLimits(uut, tests)

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

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

    checkLimits(uut, abLimits.a, abLimits.b)
    assert uut.getStoredLimits(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)
        assert uut.getLoadedIndex() == i
        checkLimits(uut, l.a, l.b)

    assert uut.getStoredLimits(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)
        assert uut.getLoadedIndex() == 0
        
        checkLimits(uut, abLimits[i].a, abLimits[i].b)
        assert uut.getStoredLimits(s) == [abLimits[i]]

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

    uut = ABController()
    uut.setCurrentSong(song)
    uut.storeLimits(abLimits.a, abLimits.b)
    uut.loadLimits(0)
    assert uut.getLoadedIndex() == 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)
    assert uut.getLoadedIndex() == 0

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

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

    checkDefaultLimits(uut)
    
def test_storeLimitsToSongWithoutCurrentSong():
    song = "/path/to/song"
    uut = ABController()
    uut.storeLimits(abLimits.a, abLimits.b, song)
    uut.loadLimits(0)
    assert uut.getLoadedIndex() == None

    checkDefaultLimits(uut)

    uut.setCurrentSong(song)

    checkDefaultLimits(uut)

    uut.loadLimits(0)
    assert uut.getLoadedIndex() == 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)
    assert uut.getLoadedIndex() == None

    checkDefaultLimits(uut)

    uut.loadLimits(0)
    assert uut.getLoadedIndex() == 0

    checkLimits(uut, abLimits.a, abLimits.b)
    
def test_getStoredLimitsOfInexistentSong():
    song = "/path/to/song"
    uut = ABController()
    assert uut.getStoredLimits(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.getStoredLimits(s[0]) == [s[1]]

    uut.clear()

    for i, s in enumerate(songsWithLimits):
        assert uut.getStoredLimits(s[0]) == None
    
def test_setTemporaryLimits():
    abLimits = [
        AB(0.2, 0.4),
        AB(0.3, 0.5),
        AB(0.0, 1.2)
    ]
    uut = ABController()

    for l in abLimits:
        uut.setLimits(l.a, l.b)
        assert uut.getLoadedIndex() == None
        checkLimits(uut, l.a, l.b)

def test_setTemporaryLimitsWithCurrentSong():
    songLimits = AB(0.2, 0.4)
    abLimits = [
        AB(0.2, 0.4),
        AB(0.3, 0.5),
        AB(0.0, 1.2)
    ]
    song = "/path/to/song"
    uut = ABController()
    uut.setCurrentSong(song)
    uut.storeLimits(songLimits.a, songLimits.b)
    uut.loadLimits(0)
    assert uut.getLoadedIndex() == 0

    for l in abLimits:
        uut.setLimits(l.a, l.b)
        checkLimits(uut, l.a, l.b)

def test_defaultBehaviour():
    uut = ABController()
    checkDefaultLimits(uut)

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

    uut = ABController()
    uut.setCurrentSong(song)
    for l in abLimits:
        uut.storeLimits(l.a, l.b)
    
    checkDefaultLimits(uut)

    uut.nextStoredAbLimits()
    checkLimits(uut, abLimits[0].a, abLimits[0].b)

    uut.nextStoredAbLimits()
    checkLimits(uut, abLimits[1].a, abLimits[1].b)

    uut.nextStoredAbLimits()
    checkLimits(uut, abLimits[1].a, abLimits[1].b)

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

    uut = ABController()
    uut.setCurrentSong(song)
    for l in abLimits:
        uut.storeLimits(l.a, l.b)
    
    checkDefaultLimits(uut)

    uut.previousStoredAbLimits()
    checkLimits(uut, abLimits[0].a, abLimits[0].b)

    uut.previousStoredAbLimits()
    checkLimits(uut, abLimits[0].a, abLimits[0].b)

    uut.loadLimits(1)
    checkLimits(uut, abLimits[1].a, abLimits[1].b)

    uut.previousStoredAbLimits()
    checkLimits(uut, abLimits[0].a, abLimits[0].b)