#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Defines various package utilities objects.
"""

from __future__ import division

import itertools
import os
import re
from collections import OrderedDict

import PyOpenColorIO as ocio

__author__ = 'ACES Developers'
__copyright__ = 'Copyright (C) 2014 - 2016 - ACES Developers'
__license__ = ''
__maintainer__ = 'ACES Developers'
__email__ = 'aces@oscars.org'
__status__ = 'Production'

__all__ = [
    'ColorSpace', 'mat44_from_mat33', 'filter_words', 'files_walker',
    'replace', 'sanitize', 'compact', 'colorspace_prefixed_name',
    'unpack_default', 'cmp'
]


class ColorSpace(object):
    """
    A container for data needed to define an *OCIO* *ColorSpace*.
    """

    def __init__(self,
                 name,
                 aliases=None,
                 description=None,
                 bit_depth=ocio.Constants.BIT_DEPTH_F32,
                 equality_group='',
                 family=None,
                 is_data=False,
                 to_reference_transforms=None,
                 from_reference_transforms=None,
                 allocation_type=ocio.Constants.ALLOCATION_UNIFORM,
                 allocation_vars=None,
                 aces_transform_id=None):
        """
        Constructor for ColorSpace container class.

        Parameters
        ----------
        name : str or unicode
            Name of the colorspace.
        All other arguments are optional
        """

        if aliases is None:
            aliases = []

        if to_reference_transforms is None:
            to_reference_transforms = []

        if from_reference_transforms is None:
            from_reference_transforms = []

        if allocation_vars is None:
            allocation_vars = [0, 1]

        self.name = name
        self.aliases = aliases
        self.bit_depth = bit_depth
        self.description = description
        self.equality_group = equality_group
        self.family = family
        self.is_data = is_data
        self.to_reference_transforms = to_reference_transforms
        self.from_reference_transforms = from_reference_transforms
        self.allocation_type = allocation_type
        self.allocation_vars = allocation_vars
        self.aces_transform_id = aces_transform_id


def mat44_from_mat33(mat33):
    """
    Creates a 4x4 matrix from given 3x3 matrix.

    Parameters
    ----------
    mat33 : array of float
        A 3x3 matrix

    Returns
    -------
    array of float
         A 4x4 matrix
    """

    return [
        mat33[0], mat33[1], mat33[2], 0, mat33[3], mat33[4], mat33[5], 0,
        mat33[6], mat33[7], mat33[8], 0, 0, 0, 0, 1
    ]


def filter_words(words, filters_in=None, filters_out=None, flags=0):
    """
    A function to filter strings in an array.

    Parameters
    ----------
    words : array of str or unicode
        Array of strings
    filters_in : array of str or unicode, optional
        Words to match
    filters_out : array of str or unicode, optional
        Words to NOT match
    flags : int, optional
        Flags for re.search

    Returns
    -------
    array of str or unicode
         An array of matched or unmatched strings
    """

    filtered_words = []
    for word in words:
        if filters_in:
            filter_matched = False
            for filter in filters_in:
                if re.search(filter, word, flags):
                    filter_matched = True
                    break
            if not filter_matched:
                continue

        if filters_out:
            filter_matched = False
            for filter in filters_out:
                if re.search(filter, word, flags):
                    filter_matched = True
                    break
            if filter_matched:
                continue
        filtered_words.append(word)
    return filtered_words


def files_walker(directory, filters_in=None, filters_out=None, flags=0):
    """
    A function to walk a directory hierarchy, only returning items that do or
    do not match the specified filters

    Parameters
    ----------
    directory : str or unicode
        The starting point for directory walking
    filters_in : array of str or unicode, optional
        File or directory names to match
    filters_out : array of str or unicode, optional
        File or directory names to NOT match
    flags : int, optional
        Flags for re.search

    Returns
    -------
    iterable
         The next matching file or directory name
    """

    for parent_directory, directories, files in os.walk(
            directory, topdown=False, followlinks=True):
        for file in files:
            path = os.path.join(parent_directory, file)
            if os.path.isfile(path):
                if not filter_words((path, ), filters_in, filters_out, flags):
                    continue

                yield path


def replace(string, data):
    """
    Replaces the data occurrences in the string.

    Parameters
    ----------
    string : str or unicode
        String to manipulate.
    data : dict
        Replacement occurrences.

    Returns
    -------
    unicode
        Manipulated string.

    Examples
    --------
    >>> patterns = {'John' : 'Luke',
    ...             'Jane' : 'Anakin',
    ...             'Doe' : 'Skywalker',
    ...             'Z6PO' : 'R2D2'}
    >>> data = 'Users are: John Doe, Jane Doe, Z6PO.'
    >>> replace(data,patterns )
    u'Users are: Luke Skywalker, Anakin Skywalker, R2D2.'
    """

    for old, new in data.items():
        string = string.replace(old, new)
    return string


def sanitize(path):
    """
    Replaces occurrences of ' ', '(', or ')' in the string with an underscore.

    Parameters
    ----------
    path : str or unicode
        Path string to manipulate.

    Returns
    -------
    unicode
        Manipulated string.
    """

    return replace(path, {' ': '_', ')': '_', '(': '_'})


def compact(string):
    """
    Removes blanks, underscores, dashes and parentheses.

    Parameters
    ----------
    string : str or unicode
        String to compact.

    Returns
    -------
    str or unicode
         A compact version of that string.
    """

    return replace(
        string.lower(),
        OrderedDict(((' ', '_'), ('(', '_'), (')', '_'), ('.', '_'),
                     ('-', '_'), ('___', '_'), ('__', '_'), ('_', ''))))


def colorspace_prefixed_name(colorspace):
    """
    Returns given *OCIO* colorspace prefixed name with its family name.

    Parameters
    ----------
    colorspace : ColorSpace
        ColorSpace to prefix.

    Returns
    -------
    str or unicode
         Family prefixed *OCIO* colorspace name.
    """
    prefix = colorspace.family.replace('/', ' - ')

    return '{0} - {1}'.format(prefix, colorspace.name)


def unpack_default(iterable, length=3, default=None):
    """
    Unpacks given iterable maintaining given length and filling missing
    entries with given default.

    Parameters
    ----------
    iterable : object
        Iterable.
    length : int
        Iterable length.
    default : object
        Filling default object.

    Returns
    -------
    iterable
    """

    return itertools.islice(
        itertools.chain(iter(iterable), itertools.repeat(default)), length)


def cmp(x, y):
    """
    Comparison function compatible with Python 2.

    Parameters
    ----------
    x : object
        Object to compare.
    y : object
        Object to compare.

    Returns
    -------
    int
        Comparison result.
    """

    return (x > y) - (x < y)
