# -*- Mode: Python; test-case-name: whipper.test.test_image_toc -*-
# vi:si:et:sw=4:sts=4:ts=4

import os
import copy
import shutil
import tempfile

from whipper.image import toc

from whipper.test import common


class CureTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__), 'cure.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 13)

    def testGetTrackLength(self):
        t = self.toc.table.tracks[0]
        # first track has known length because the .toc is a single file
        # its length is all of track 1 from .toc, plus the INDEX 00 length
        # of track 2
        self.assertEqual(self.toc.getTrackLength(t),
                         (((6 * 60) + 16) * 75 + 45) + ((1 * 75) + 4))
        # last track has unknown length
        t = self.toc.table.tracks[-1]
        self.assertEqual(self.toc.getTrackLength(t), -1)

    def testIndexes(self):
        # track 2, index 0 is at 06:16:45 or 28245
        # track 2, index 1 is at 06:17:49 or 28324
        # FIXME: cdrdao seems to get length of FILE 1 frame too many,
        # and START value one frame less
        t = self.toc.table.tracks[1]
        self.assertEqual(t.getIndex(0).relative, 28245)
        self.assertEqual(t.getIndex(1).relative, 28324)

    def _getIndex(self, t, i):
        track = self.toc.table.tracks[t - 1]
        return track.getIndex(i)

    def _assertAbsolute(self, t, i, value):
        index = self._getIndex(t, i)
        self.assertEqual(index.absolute, value)

    def _assertPath(self, t, i, value):
        index = self._getIndex(t, i)
        self.assertEqual(index.path, value)

    def _assertRelative(self, t, i, value):
        index = self._getIndex(t, i)
        self.assertEqual(index.relative, value)

    def testSetFile(self):
        self._assertAbsolute(1, 1, 0)
        self._assertAbsolute(2, 0, 28245)
        self._assertAbsolute(2, 1, 28324)
        self._assertPath(1, 1, "data.wav")

        self.toc.table.clearFiles()

        self._assertAbsolute(1, 1, 0)
        self._assertAbsolute(2, 0, 28245)
        self._assertAbsolute(2, 1, 28324)
        self._assertAbsolute(3, 1, 46110)
        self._assertAbsolute(4, 1, 66767)
        self._assertPath(1, 1, None)
        self._assertRelative(1, 1, None)

        # adding the first track file with length 28324 to the table should
        # relativize from absolute 0 to absolute 28323, right before track 2,
        # index 1
        self.toc.table.setFile(1, 1, 'track01.wav', 28324)
        self._assertPath(1, 1, 'track01.wav')
        self._assertRelative(1, 1, 0)
        self._assertPath(2, 0, 'track01.wav')
        self._assertRelative(2, 0, 28245)

        self._assertPath(2, 1, None)
        self._assertRelative(2, 1, None)

    def testConvertCue(self):
        cue = self.toc.table.cue()
        ref = self.readCue('cure.cue')
        common.diffStrings(ref, cue)

        # we verify it because it has failed in readdisc in the past
        self.assertEqual(self.toc.table.accuraterip_path(),
                         '3/c/4/dBAR-013-0019d4c3-00fe8924-b90c650d.bin')

    def testGetRealPath(self):
        self.assertRaises(KeyError, self.toc.getRealPath, 'track01.wav')
        (fd, path) = tempfile.mkstemp(suffix='.whipper.test.wav')
        self.assertEqual(self.toc.getRealPath(path), path)

        winpath = path.replace('/', '\\')
        self.assertEqual(self.toc.getRealPath(winpath), path)
        os.close(fd)
        os.unlink(path)

# Bloc Party - Silent Alarm has a Hidden Track One Audio


class BlocTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__), 'bloc.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 13)

    def testGetTrackLength(self):
        t = self.toc.table.tracks[0]
        # first track has known length because the .toc is a single file
        # the length is from Track 1, Index 1 to Track 2, Index 1, so
        # does not include the htoa
        self.assertEqual(self.toc.getTrackLength(t), 19649)
        # last track has unknown length
        t = self.toc.table.tracks[-1]
        self.assertEqual(self.toc.getTrackLength(t), -1)

    def testIndexes(self):
        track01 = self.toc.table.tracks[0]
        index00 = track01.getIndex(0)
        self.assertEqual(index00.absolute, 0)
        self.assertEqual(index00.relative, 0)
        self.assertEqual(index00.counter, 0)

        index01 = track01.getIndex(1)
        self.assertEqual(index01.absolute, 15220)
        self.assertEqual(index01.relative, 0)
        self.assertEqual(index01.counter, 1)

        track05 = self.toc.table.tracks[4]

        index00 = track05.getIndex(0)
        self.assertEqual(index00.absolute, 84070)
        self.assertEqual(index00.relative, 68850)
        self.assertEqual(index00.counter, 1)

        index01 = track05.getIndex(1)
        self.assertEqual(index01.absolute, 84142)
        self.assertEqual(index01.relative, 68922)
        self.assertEqual(index01.counter, 1)

    # This disc has a pre-gap, so is a good test for .CUE writing

    def testConvertCue(self):
        self.assertTrue(self.toc.table.hasTOC())
        cue = self.toc.table.cue()
        ref = self.readCue('bloc.cue')
        common.diffStrings(ref, cue)

    def testCDDBId(self):
        # cd-discid output:
        # ad0be00d 13 15370 35019 51532 69190 84292 96826 112527 132448
        # 148595 168072 185539 203331 222103 3244

        self.assertEqual(self.toc.table.getCDDBDiscId(), 'ad0be00d')

    def testAccurateRip(self):
        # we verify it because it has failed in readdisc in the past
        self.assertEqual(self.toc.table.accuraterip_path(),
                         'e/d/2/dBAR-013-001af2de-0105994e-ad0be00d.bin')

# The Breeders - Mountain Battles has CDText


class BreedersTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__), 'breeders.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 13)

    def testCDText(self):
        cdt = self.toc.table.cdtext
        self.assertEqual(cdt['PERFORMER'], 'THE BREEDERS')
        self.assertEqual(cdt['TITLE'], 'MOUNTAIN BATTLES')

        t = self.toc.table.tracks[0]
        cdt = t.cdtext
        self.assertRaises(AttributeError, getattr, cdt, 'PERFORMER')
        self.assertEqual(cdt['TITLE'], 'OVERGLAZED')

    def testConvertCue(self):
        self.assertTrue(self.toc.table.hasTOC())
        cue = self.toc.table.cue()
        ref = self.readCue('breeders.cue')
        self.assertEqual(cue, ref)

# Ladyhawke has a data track


class LadyhawkeTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__), 'ladyhawke.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 13)
        self.assertFalse(self.toc.table.tracks[-1].audio)

    def testCDDBId(self):
        self.assertEqual(self.toc.table.getCDDBDiscId(), 'c60af50d')
        # output from cd-discid:
        # c60af50d 13 150 15687 31841 51016 66616 81352 99559 116070 133243
        # 149997 161710 177832 207256 2807

    def testMusicBrainz(self):
        self.assertEqual(self.toc.table.getMusicBrainzDiscId(),
                         "KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
        self.assertEqual(self.toc.table.getMusicBrainzSubmitURL(),
            "https://musicbrainz.org/cdtoc/attach?toc=1+12+195856+150+15687+31841+51016+66616+81352+99559+116070+133243+149997+161710+177832&tracks=12&id=KnpGsLhvH.lPrNc1PBL21lb9Bg4-")  # noqa: E501

    # FIXME: I don't trust this toc, but I can't find the CD anymore

    def testDuration(self):
        self.assertEqual(self.toc.table.duration(), 2761413)

    def testGetFrameLength(self):
        self.assertEqual(self.toc.table.getFrameLength(data=True), 210385)

    def testCue(self):
        self.assertTrue(self.toc.table.canCue())
        data = self.toc.table.cue()
        lines = data.split("\n")
        self.assertEqual(lines[0], "REM DISCID C60AF50D")


class CapitalMergeTestCase(common.TestCase):

    def setUp(self):
        self.toc1 = toc.TocFile(os.path.join(os.path.dirname(__file__),
                                             'capital.1.toc'))
        self.toc1.parse()
        self.assertEqual(len(self.toc1.table.tracks), 11)
        self.assertTrue(self.toc1.table.tracks[-1].audio)

        self.toc2 = toc.TocFile(os.path.join(os.path.dirname(__file__),
                                             'capital.2.toc'))
        self.toc2.parse()
        self.assertEqual(len(self.toc2.table.tracks), 1)
        self.assertFalse(self.toc2.table.tracks[-1].audio)

        self.table = copy.deepcopy(self.toc1.table)
        self.table.merge(self.toc2.table)

    def testCDDBId(self):
        self.assertEqual(self.table.getCDDBDiscId(), 'b910140c')
        # output from cd-discid:
        # b910140c 12 24320 44855 64090 77885 88095 104020 118245 129255 141765
        # 164487 181780 209250 4440

    def testMusicBrainz(self):
        # URL to submit: https://musicbrainz.org/cdtoc/attach?toc=1+11+
        # 197850+24320+44855+64090+77885+88095+104020+118245+129255+141765+
        # 164487+181780&tracks=11&id=MAj3xXf6QMy7G.BIFOyHyq4MySE-
        self.assertEqual(self.table.getMusicBrainzDiscId(),
                         "MAj3xXf6QMy7G.BIFOyHyq4MySE-")

    def testDuration(self):
        # this matches track 11 end sector - track 1 start sector on
        # MusicBrainz
        # compare to 3rd and 4th value in URL above
        self.assertEqual(self.table.getFrameLength(), 173530)
        self.assertEqual(self.table.duration(), 2313733)


class UnicodeTestCase(common.TestCase, common.UnicodeTestMixin):

    def setUp(self):
        # we copy the normal non-utf8 filename to a utf-8 filename
        # in this test because builds with LANG=C fail if we include
        # utf-8 filenames in the dist
        path = 'Jos\xe9Gonz\xe1lez.toc'
        self._performer = 'Jos\xe9 Gonz\xe1lez'
        source = os.path.join(os.path.dirname(__file__), 'jose.toc')
        (fd, self.dest) = tempfile.mkstemp(suffix=path)
        os.close(fd)
        shutil.copy(source, self.dest)
        self.toc = toc.TocFile(self.dest)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 10)

    def tearDown(self):
        os.unlink(self.dest)

    def testGetTrackLength(self):
        t = self.toc.table.tracks[0]
        # first track has known length because the .toc is a single file
        self.assertEqual(self.toc.getTrackLength(t), 12001)
        # last track has unknown length
        t = self.toc.table.tracks[-1]
        self.assertEqual(self.toc.getTrackLength(t), -1)

    def testGetTrackPerformer(self):
        t = self.toc.table.tracks[0]
        self.assertEqual(t.cdtext['PERFORMER'], self._performer)


# Interpol - Turn of the Bright Lights has same cddb disc id as
# Afghan Whigs - Gentlemen


class TOTBLTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__), 'totbl.fast.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 11)

    def testCDDBId(self):
        self.assertEqual(self.toc.table.getCDDBDiscId(), '810b7b0b')


class GentlemenTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__),
                                 'gentlemen.fast.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEquals(len(self.toc.table.tracks), 11)

    def testCDDBId(self):
        self.toc.table.absolutize()
        self.assertEquals(self.toc.table.getCDDBDiscId(), '810b7b0b')


# The Strokes - Someday has a 1 frame SILENCE marked as such in toc


class StrokesTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__),
                                 'strokes-someday.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 1)

    def testIndexes(self):
        t = self.toc.table.tracks[0]
        i0 = t.getIndex(0)
        self.assertEqual(i0.relative, 0)
        self.assertEqual(i0.absolute, 0)
        self.assertEqual(i0.counter, 0)
        self.assertEqual(i0.path, None)

        i1 = t.getIndex(1)
        self.assertEqual(i1.relative, 0)
        self.assertEqual(i1.absolute, 1)
        self.assertEqual(i1.counter, 1)
        self.assertEqual(i1.path, 'data.wav')

        cue = self._filterCue(self.toc.table.cue())
        with open(os.path.join(os.path.dirname(__file__),
                               'strokes-someday.eac.cue')) as f:
            ref = self._filterCue(f.read())
        common.diffStrings(ref, cue)

    @staticmethod
    def _filterCue(output):
        # helper to be able to compare our generated .cue with the
        # EAC-extracted one
        discard = ['TITLE', 'PERFORMER', 'FLAGS', 'REM']
        lines = output.split('\n')

        res = []

        for line in lines:
            found = False
            for needle in discard:
                if line.find(needle) > -1:
                    found = True

            if line.find('FILE') > -1:
                line = 'FILE "data.wav" WAVE'

            if not found:
                res.append(line)

        return '\n'.join(res)


# Surfer Rosa has
# track 00 consisting of 32 frames of SILENCE
# track 11 Vamos with an INDEX 02
# compared to an EAC single .cue file, all our offsets are 32 frames off
# because the toc uses silence for track 01 index 00 while EAC puts it in
# Range.wav
class SurferRosaTestCase(common.TestCase):

    def setUp(self):
        self.path = os.path.join(os.path.dirname(__file__), 'surferrosa.toc')
        self.toc = toc.TocFile(self.path)
        self.toc.parse()
        self.assertEqual(len(self.toc.table.tracks), 21)

    def testIndexes(self):
        # HTOA
        t = self.toc.table.tracks[0]
        self.assertEqual(len(t.indexes), 2)

        i0 = t.getIndex(0)
        self.assertEqual(i0.relative, 0)
        self.assertEqual(i0.absolute, 0)
        self.assertEqual(i0.path, None)
        self.assertEqual(i0.counter, 0)

        i1 = t.getIndex(1)
        self.assertEqual(i1.relative, 0)
        self.assertEqual(i1.absolute, 32)
        self.assertEqual(i1.path, 'data.wav')
        self.assertEqual(i1.counter, 1)

        # track 11, Vamos

        t = self.toc.table.tracks[10]
        self.assertEqual(len(t.indexes), 2)

        # 32 frames of silence, and 1483 seconds of data.wav
        self.assertEqual(t.getIndex(1).relative, 111225)
        self.assertEqual(t.getIndex(1).absolute, 111257)
        self.assertEqual(t.getIndex(2).relative, 111225 + 3370)
        self.assertEqual(t.getIndex(2).absolute, 111257 + 3370)
