#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.anjos@idiap.ch>
# Thu Feb  7 09:58:22 2013
#
# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland

"""Re-usable decorators and utilities for xbob test code
"""

import os
import functools
import nose.plugins.skip
from distutils.version import StrictVersion as SV

def datafile(f, module=None, path='data'):
  """Returns the test file on the "data" subdirectory of the current module.

  Keyword attributes

  f: str
    This is the filename of the file you want to retrieve. Something like
    ``'movie.avi'``.

  package: string, optional
    This is the python-style package name of the module you want to retrieve
    the data from. This should be something like ``xbob.io.test``, but you
    normally refer it using the ``__name__`` property of the module you want to
    find the path relative to.

  path: str, optional
    This is the subdirectory where the datafile will be taken from inside the
    module. Normally (the default) ``data``. It can be set to ``None`` if it
    should be taken from the module path root (where the ``__init__.py`` file
    sits).

  Returns the full path of the file.
  """

  resource = __name__ if module is None else module
  final_path = f if path is None else os.path.join(path, f)
  return __import__('pkg_resources').resource_filename(resource, final_path)

def temporary_filename(prefix='bobtest_', suffix='.hdf5'):
  """Generates a temporary filename to be used in tests"""

  (fd, name) = __import__('tempfile').mkstemp(suffix, prefix)
  os.close(fd)
  os.unlink(name)
  return name

# Here is a table of ffmpeg versions against libavcodec, libavformat and
# libavutil versions
ffmpeg_versions = {
    '0.5':  [ SV('52.20.0'),   SV('52.31.0'),   SV('49.15.0')   ],
    '0.6':  [ SV('52.72.2'),   SV('52.64.2'),   SV('50.15.1')   ],
    '0.7':  [ SV('52.122.0'),  SV('52.110.0'),  SV('50.43.0')   ],
    '0.8':  [ SV('53.7.0'),    SV('53.4.0'),    SV('51.9.1')    ],
    '0.9':  [ SV('53.42.0'),   SV('53.24.0'),   SV('51.32.0')   ],
    '0.10': [ SV('53.60.100'), SV('53.31.100'), SV('51.34.101') ],
    '0.11': [ SV('54.23.100'), SV('54.6.100'),  SV('51.54.100') ],
    '1.0':  [ SV('54.59.100'), SV('54.29.104'), SV('51.73.101') ],
    '1.1':  [ SV('54.86.100'), SV('54.59.106'), SV('52.13.100') ],
    '1.2':  [ SV('54.92.100'), SV('54.63.104'), SV('52.18.100') ],
    '2.0':  [ SV('55.18.102'), SV('55.12.100'), SV('52.38.100') ],
    '2.1':  [ SV('55.39.100'), SV('55.19.104'), SV('52.48.100') ],
    }

def ffmpeg_version_lessthan(v):
  '''Returns true if the version of ffmpeg compiled-in is at least the version
  indicated as a string parameter.'''

  from .._externals import versions
  if versions['FFmpeg']['ffmpeg'] == 'unavailable': return False
  avcodec_inst= SV(versions['FFmpeg']['avcodec'])
  avcodec_req = ffmpeg_versions[v][0]
  return avcodec_inst < avcodec_req

def ffmpeg_found(version_geq=None):
  '''Decorator to check if a codec is available before enabling a test

  To use this, decorate your test routine with something like:

  .. code-block:: python

    @ffmpeg_found()

  You can pass an optional string to require that the FFMpeg version installed
  is greater or equal that version identifier. For example:

  .. code-block:: python

    @ffmpeg_found('0.10') #requires at least version 0.10

  Versions you can test for are set in the ``ffmpeg_versions`` dictionary in
  this module.
  '''

  def test_wrapper(test):

    @functools.wraps(test)
    def wrapper(*args, **kwargs):
      try:
        from .._externals import versions
        avcodec_inst = SV(versions['FFmpeg']['avcodec'])
        avformat_inst = SV(versions['FFmpeg']['avformat'])
        avutil_inst = SV(versions['FFmpeg']['avutil'])
        if version_geq is not None:
          avcodec_req,avformat_req,avutil_req = ffmpeg_versions[version_geq]
          if avcodec_inst < avcodec_req:
            raise nose.plugins.skip.SkipTest('FFMpeg/libav version installed (%s) is smaller than required for this test (%s)' % (version['FFmpeg']['ffmpeg'], version_geq))
        return test(*args, **kwargs)
      except KeyError:
        raise nose.plugins.skip.SkipTest('FFMpeg was not available at compile time')

    return wrapper

  return test_wrapper

def codec_available(codec):
  '''Decorator to check if a codec is available before enabling a test'''

  def test_wrapper(test):

    @functools.wraps(test)
    def wrapper(*args, **kwargs):
      from ..io import supported_video_codecs
      d = supported_video_codecs()
      if codec in d and d[codec]['encode'] and d[codec]['decode']:
        return test(*args, **kwargs)
      else:
        raise nose.plugins.skip.SkipTest('A functional codec for "%s" is not installed with FFmpeg' % codec)

    return wrapper

  return test_wrapper

def extension_available(extension):
  '''Decorator to check if a extension is available before enabling a test'''

  def test_wrapper(test):

    @functools.wraps(test)
    def wrapper(*args, **kwargs):
      from .._externals import extensions
      if extension in extensions():
        return test(*args, **kwargs)
      else:
        raise nose.plugins.skip.SkipTest('Extension to handle "%s" files was not available at compile time' % extension)

    return wrapper

  return test_wrapper