'''This document contains a number of sample trackers.'''

import os
import random
import glob
import itertools
import numpy

from CGATReport.Tracker import Tracker, Status
from collections import OrderedDict as odict
import collections


def BarData():
    return dict([("bar1", 20), ("bar2", 10)])


class LabeledDataExample(Tracker):
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):

        v = int(track[-1]) * int(slice[-1])
        if slice == "slice1":
            return odict((("column1", v),
                          ("column2", v * 2),))
        elif slice == "slice2":
            return odict((("column1", v),
                          ("column2", v * 2),
                          ("column3", v * 3),))


class LabeledDataLargeExample(Tracker):
    slices = ("slice1", "slice2")
    tracks = ["track%i" % x for x in range(0, 100)]

    def __call__(self, track, slice=None):
        if slice == "slice1":
            return odict((("column1", 10),
                          ("column2", 20),))
        elif slice == "slice2":
            return odict((("column1", 20),
                          ("column2", 10),
                          ("column3", 5),))



class LabeledDataWithErrorsExample(Tracker):
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):
        if slice == "slice1":
            return odict((
                ("column1", odict((('data', 20),
                                   ('error', 1),
                                   ))),
                ("column2", odict((('data', 10),
                                   ('error', 2),
                                   ))),
            ))
        elif slice == "slice2":
            return odict((
                ("column1", odict((('data', 20),
                                   ('error', 3),
                                   ))),
                ("column2", odict((('data', 10),
                                   ('error', 4)))),
                ("column3", odict((('data', 30),
                                   ('error', 5)))),
            ))


class LabeledDataWithErrorsAndLabelsExample(Tracker):

    '''use of ordered dictionary important as first
    element is treated as data.
    '''
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):
        if slice == "slice1":
            return odict((
                ("column1", odict((('data', 20),
                                   ('error', 5),
                                   ('label', '**')))),
                ("column2", odict((('data', 10),
                                   ('error', 2),
                                   ('label', '*'))))
            ))
        elif slice == "slice2":
            return odict((
                ("column1", odict((('data', 20),
                                   ('error', 5),
                                   ('label', '***')))),
                ("column2", odict((('data', 10),
                                   ('error', 1)))),
                ("column3", odict((('data', 30),
                                   ('error', 4)))),
            ))


class HierarchicalLabeledDataExample(Tracker):
    '''labelled data with more than three levels.'''
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):

        nvalues = 4
        values = collections.defaultdict(dict)
        levels = 3
        for x in range(nvalues):
            values[x] = collections.defaultdict(dict)
            for y in range(nvalues):
                values[x][y] = collections.defaultdict(dict)

        if slice == "slice1":
            for a, b, c in itertools.permutations(
                    range(nvalues),
                    levels):
                values[a][b][c] = (a + 1) * (b + 1) * (c + 1)
        elif slice == "slice2":
            for a, b, c in itertools.permutations(
                    range(nvalues),
                    levels):
                values[a][b][c] = (a + 1) * (b + 1) * (c + 1) * 2

        return values


class SingleColumnDataExample(Tracker):

    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):
        s = [random.randint(0, 20) for x in range(40)]
        random.shuffle(s)
        return odict((("data", s),))


class ArrayDataExample(Tracker):

    '''return two arrays of data.'''

    slices = ["slice%i" % x for x in range(0, 2)]
    tracks = ["track%i" % x for x in range(0, 3)]

    def __call__(self, track, slice=None):

        scale = (3 - int(track[-1])) * (2 - int(slice[-1]))

        data = odict((("x", range(0, 10)),
                      ("y", [x * scale for x in range(0, 10)])))

        return data


class SingleColumnDataLargeExample(Tracker):

    '''return a single column of data.'''
    slices = ("slice1", "slice2")
    tracks = ["track%i" % x for x in range(0, 20)]

    def __call__(self, track, slice=None):
        s = [random.randint(0, 20) for x in range(40)]
        random.shuffle(s)
        return odict((("data", s),))


class SingleColumnDataWithErrorExample(Tracker):

    '''return a single column of data.'''
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):
        s = [random.randint(0, 20) for x in range(40)]
        e = [random.randint(0, 3) for x in range(40)]
        random.shuffle(s)
        return odict((("data", s),
                      ("error", e)))


class SingleColumnDataExampleWithoutSlices(Tracker):

    '''return a single column of data.'''
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):
        s = [random.randint(0, 20) for x in range(40)]
        random.shuffle(s)
        return odict((("data", s),))


class MultipleColumnDataExample(Tracker):

    '''multiple columns each with a column with data.'''
    mColumns = ["col1", "col2", "col3"]
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2")

    def __call__(self, track, slice=None):
        data = []
        if slice == "slice1":
            for x in range(len(self.mColumns) - 1):
                data.append([y + random.gauss(0, 0.2) for y in range(10)])
        elif slice == "slice2":
            for x in range(len(self.mColumns)):
                data.append([y + random.gauss(0, 0.5) for y in range(10)])
        return odict(zip(self.mColumns, data))


class MultipleColumnDataFullExample(Tracker):

    '''multiple columns each with a column with data.'''
    mColumns = ["col1", "col2", "col3"]
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2")

    def __call__(self, track, slice=None):
        data = []
        if slice == "slice1":
            for x in range(len(self.mColumns)):
                data.append([y + random.gauss(0, 0.2) for y in range(20)])
        elif slice == "slice2":
            for x in range(len(self.mColumns)):
                data.append([y + random.gauss(0, 0.5) for y in range(20)])
        return odict(zip(self.mColumns, data))


class ErrorInTracker1(Tracker):

    '''A tracker that creates an error - exception while collecting data.'''
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2")

    def __call__(self, track, slice=None):
        raise ValueError("testing: could not collect data")


class ErrorInTracker2(Tracker):

    '''A tracker that creates an error - problems while returning tracks.'''
    slices = ("slice1", "slice2")

    def getTracks(self):
        raise ValueError("testing: could not build tracks")

    def __call__(self, track, slice=None):
        return odict((("data", range(0, 10)),))


class ErrorInTracker3(Tracker):

    '''A tracker that creates an error - no data.'''
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2")

    def __call__(self, track, slice=None):
        return None


class EmptyTracker(Tracker):

    '''A tracker that creates a warning.'''
    slices = ("slice1", "slice2")
    tracks = []

    def __call__(self, track, slice=None):
        return None


class LabeledDataTest(Tracker):
    slices = ("slice1", "slice2")
    tracks = ("track1", "track2", "track3")

    def __call__(self, track, slice=None):
        if slice == "slice1":
            return odict((("column1", 10),
                          ("column2", 20),))
        elif slice == "slice2":
            return odict((("column1", 20),
                          ("column2", 10),
                          ("column3", 5),))


class StatusTracker(Status):
    tracks = ("track1", "track2", "track3")

    def testTest1(self, track):
        '''test1 passes'''
        return "PASS", 0.5

    def testTest2(self, track):
        '''test2 fails -
        A large test.'''
        return "FAIL", 2

    def testTest3(self, track):
        '''test3 gives a warning'''
        return "WARN", "a string"

    def testTest4(self, track):
        '''test4 is not available/applicable'''
        return "NA", None


class MatrixTracker(Tracker):

    '''returns matrices.'''

    tracks = 'example1', 'example2'

    def __call__(self, track):

        if track == 'example1':
            matrix = numpy.arange(0, 10)
        elif track == 'example2':
            matrix = numpy.arange(10, 0, -1)

        matrix.shape = (2, 5)

        r = {'rows': list(map(str, range(0, 2))),
             'columns': list(map(str, range(0, 5))),
             'matrix': matrix}

        return r


def getSingleValue():
    return 12

IMAGEDIR = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), "..", "images")


class DataWithImagesExample(Tracker):
    tracks = ("all",)

    def __call__(self, track):

        images = glob.glob(os.path.join(IMAGEDIR, "*.png"))

        data = odict()
        data["numbers"] = range(len(images))
        data["images"] = [".. image:: %s" % x for x in images]

        return data


class VennTracker(Tracker):

    tracks = ('two-circle', 'three-circle')
    slices = ('data1', 'data2')

    def __call__(self, track, slice):

        if slice == 'data1':
            f = 10
        else:
            f = 1

        if track == 'two-circle':
            return {'01': 10, '10': 20 * f, '11': 5 * f,
                    'labels': ("SetA", "SetB")}

        elif track == 'three-circle':
            return {'001': 10, '010': 20 * f, '100': 5 * f,
                    '011': 10, '110': 20 * f, '101': 5 * f,
                    '111': 10,
                    'labels': ("SetA", "SetB", "SetC")}
