#!/usr/bin/env python
# encoding: utf-8

import unittest

import numpy as np
from scipy.ndimage.filters import gaussian_filter

from ocupy import fixmat


class TestComputeFDM(unittest.TestCase):
    """``Test_compute_fdm(unittest.TestCase)``
    Test the compute_fdm method of the fixmat class. 
    What is tested for:
    - Assertions: The Program should throw an exception if:
        - the image_size attribute is empty, 
        - fixations are from category == -1
        - fixations are not on the image
    - The resulting map should have the size as specified in 
      image_size or as scale_factor*image_size
    - Values in the fdm should sum to 1 / the fdm should be a probability density
      distribution
    """
    def setUp(self):
        self.fm = fixmat.TestFixmatFactory()
        
    # test for empty-image-size-assertion
    def test_empty_image_size(self):
        self.fm.image_size = []
        self.fm.filter( self.fm.category > 0)
        self.assertRaises(AssertionError, fixmat.compute_fdm,self.fm)

    def test_scale_factor_assertion(self):
        """``test_scale_factor_assertion(self)``
        Tests for assertion that scaling factor is greater than 0
        """
        self.assertRaises(AssertionError, 
            fixmat.compute_fdm,self.fm,scale_factor = -1)
    
    def test_no_fixations(self):
        """ ``test_no_fixations(self)``
        Tests the assertion that the fixmat has at least one fixation.
        """
        #returns an empty fixmat
        fm = self.fm.filter(self.fm.category == 3) 
        self.assertRaises(fixmat.NoFixations, fixmat.compute_fdm, fm)

    # tests for properties of a probability density function, i.e.
    # elements must sum to one and be in the range of [0 1]    
    def test_pdf(self):
        fdm = fixmat.compute_fdm(self.fm)
        # ensure that we have a probability density function
        if fdm.min()< 0:
            fdm = fdm - fdm.min()
        self.assertFalse(abs(fdm.sum()-1) > np.finfo('single').eps)
      
    def test_corners(self):    
        """ ``test_corners(self)``
        Tests whether handling of fixations in the corners is correct. 
        It manually generates an fdm with four fixations in the corners of a
        922x1272 array and compares it to the map generated by compute_fdm. 
        The difference between the maps must not be larger than the machine
        precision for floats.
        """    
        yvec = [922, 922,0   ,0]
        xvec = [1272,0  ,1272,0]
        self.fm = fixmat.TestFixmatFactory(points = (xvec, yvec))
        fdm = fixmat.compute_fdm(self.fm)
        # manually calculate the fdm
        fdm_man = np.zeros((922,1272))
        fdm_man[0][0] = 1
        fdm_man[921][1271] = 1
        fdm_man[0][1271] = 1
        fdm_man[921][0] = 1        
        # use default settings for fwhm, pixels_per_degree and scale_factor
        kernel_sigma = 2 * 36 * 1
        kernel_sigma = kernel_sigma / (2 * (2 * np.log(2)) ** .5)
        fdm_man = gaussian_filter(np.array(fdm_man), kernel_sigma, order=0,
            mode='constant')
        fdm_man = fdm_man / fdm_man.sum()
        diff = fdm - fdm_man
        self.assertFalse((diff > np.finfo('float').eps).any())
        
    def test_scaling(self):
        """``test_scaling(self)``
        Tests that the size of the resulting fdm corresponds to 
        image_size*scale_factor. Tests with different image sizes.
        """
        size = self.fm.image_size
        sf = [0.6, 1.6, 1.0/2.0]
        # if the scaling factor is not a float, this does not work but seems to 
        # be a problem of the inbuilt function.
        for i in range(len(sf)):
            fdm = fixmat.compute_fdm(self.fm,scale_factor = sf[i])
            self.assertEqual((int(size[0]*sf[i]),int(size[1]*sf[i])),
                np.shape(fdm))

    def test_relative_bias(self):
        fm = fixmat.TestFixmatFactory(categories = [1,2], 
                filenumbers = range(1,11),
                subjectindices = [1, 2, 3, 4, 5, 6],
                params = {'pixels_per_degree':1, 'image_size':[10,10]})
        rb = fixmat.relative_bias(fm)   
        self.assertEquals(rb.shape,(20, 20))
        self.assertEquals(rb[9][9],960)

    def tearDown(self):
        self.fm = None
        
if __name__ == '__main__':
    unittest.main()
