#!/usr/bin/python
# coding: utf-8
# Author(s): Julien Brusset <julien.brusset.prestataire@bpce-it.fr>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

"""
This module declare some custom filters for Ansible helping users to
validating/manipulating WWN addresses in various formats.
"""

import re
from ansible.errors import AnsibleError


class WWNError(Exception):
    """
    Generic Exception raised if the given string is not a good WWN
    """
    pass


class WWN(object):
    """
    Transform strings into WWN objects
    """

    def __init__(self, address):
        """
        Initialization
        :param address: (str) a string containing a WWN (ex: 1122334455667788)
        """
        if isinstance(address, WWN):
            self._address = address.wwn
        else:
            self._address = self._normalize(address)

    def __format__(self, format_spec):
        return self.__str__()

    @classmethod
    def _normalize(cls, address):
        """
        Normalize the given WWN into a lowercase with colons
        :param address:
        :return: (str) WWN normalized
        """
        _regexDot = re.compile("([0-9a-fA-F]{2}|:){15}")
        _regexNoDot = re.compile("([0-9a-fA-F]{16})")

        if len(address) == 16 or len(address) == 23:
            if _regexDot.match(address):
                cls._address = address
            elif _regexNoDot.match(address):
                cls._address = ':'.join(re.findall('..', address))
            else:
                raise WWNError(address)
        else:
            raise WWNError(address)
        return cls._address.lower()

    def __eq__(self, other):
        """
        Compare two WWNs
        :param other: (str) WWN to compare
        :return: (bool) True if WWN are identical else False
        """
        if isinstance(other, str):
            try:
                other = WWN(other)
            except WWNError:
                return False
        elif not isinstance(other, WWN):
            return False
        return self._address == other._address

    def __repr__(self):
        return self._address

    @property
    def wwn(self):
        """
        Returns the embedded WWN
        :return: (str) WWN string
        """
        return self._address


class FilterModule(object):
    """
    Ansible filters declaration
    """
    def filters(self):
        return {
            'wwn': self.wwn_filter,
            'wwn_nodots': self.wwn_nodots_filter,
            'WWN': self.WWN_filter,
            'WWN_nodots': self.WWN_nodots_filter
        }

    def wwn_filter(self, a_variable):
        """
        Returns WWN lowercase with colons like 11:22:33:44:55:66:aa:bb
        :param a_variable: (str) variable to transform
        :return: (str) transformed string
        """
        try:
            return WWN(a_variable).wwn
        except WWNError as error:
            raise AnsibleError(f'Malformed WWN {error}')

    def wwn_nodots_filter(self, a_variable):
        """
        Returns WWN lowercase without colons like 112233445566aabb
        :param a_variable: (str) variable to transform
        :return: (str) transformed string
        """
        try:
            return WWN(a_variable).wwn.replace(':', '')
        except WWNError as error:
            raise AnsibleError(f'Malformed WWN {error}')

    def WWN_filter(self, a_variable):
        """
        Returns WWN uppercase with colons like 11:22:33:44:55:66:AA:BB
        :param a_variable: (str) variable to transform
        :return: (str) transformed string
        """
        try:
            return WWN(a_variable).wwn.upper()
        except WWNError as error:
            raise AnsibleError(f'Malformed WWN {error}')

    def WWN_nodots_filter(self, a_variable):
        """
        Returns WWN uppercase without colons like 112233445566AABB
        :param a_variable: (str) variable to transform
        :return: (str) transformed string
        """
        try:
            return WWN(a_variable).wwn.upper().replace(':', '')
        except WWNError as error:
            raise AnsibleError(f'Malformed WWN {error}')