import logging

from textwrap import dedent

from ..tecutil import _tecutil
from ..constant import *
from ..exception import *
from .. import layout, session
from ..tecutil import Index, lock, log_setattr
from .array import Array

log = logging.getLogger(__name__)


@log_setattr
class Variable(object):
    """Key value for a data array within a `Dataset`.

    `Variables <Variable>` can be identified (uniquely) by the index within
    their parent `Dataset` or (non-uniquely) by name. In general, a `Zone <data_access>` must
    also be selected to access the underlying data array. This object is used
    by several style controlling classes such as contours and vectors. The
    following example sets the contour variable for the first contour group to
    the first variable named 'S'::

        >>> plot.contour(0).variable = dataset.variable('S')
    """
    def __init__(self, uid, dataset):
        self.uid = uid
        self.dataset = dataset

    def __str__(self):
        """Brief string representation.

        Returns:
            `string <str>`: Brief representation of this `Variable`, showing
            `Zones <data_access>`.

        Example::

            >>> x = dataset.variable('x')
            >>> print(x)
            Variable: 'x'
              Dataset: 'My Dataset'
              Zones: ['Rectangular zone']
        """
        fmt = dedent('''\
            Variable: '{}'
              Dataset: '{}'
              Zones: {}''')
        znames = [str(z.name) for z in self.dataset.zones()]
        return fmt.format(self.name, self.dataset.title, znames)

    def __repr__(self):
        """Executable string representation.

        Returns:
            `string <str>`: Internal representation of this `Variable`.

        The string returned can be executed to generate a
        clone of this `Variable` object::

            >>> x = dataset.variable('x')
            >>> print(repr(x))
            Variable(uid=41, Dataset(uid=21, frame=Frame(uid=11,
            page=Page(uid=1)))
            >>> exec('x_clone = '+repr(x))
            >>> x_clone
            Variable(uid=41, Dataset(uid=21, frame=Frame(uid=11,
            page=Page(uid=1)))
            >>> x == x_clone
            True
        """
        return 'Variable(uid={uid}, dataset={dataset})'.format(
            uid=self.uid, dataset=repr(self.dataset))

    def __eq__(self, other):
        """Checks for equality in the |Tecplot Engine|.

        Returns:
            `bool`: `True` if the unique ID numbers are the same for both
            `Variables <Variable>`.
        """
        return self.uid == other.uid

    def __ne__(self, other):
        return not (self == other)

    @property
    def index(self):
        """Zero-based position within the parent `Dataset`.

        :type: `Index`

        Example usage::

            >>> plot.contour(0).variable_index = dataset.variable('S').index
        """
        return Index(_tecutil.VarGetNumByUniqueID(self.uid) - 1)

    @property
    def name(self):
        """Returns or sets the name.

        :type: `string <str>`

        Example usage::

            >>> print(dataset.variable(0).name)
            X
        """
        res, var_name = _tecutil.VarGetNameByDataSetID(self.dataset.uid,
                                                       self.index + 1)
        if not res:
            raise TecplotSystemError()
        return var_name

    @name.setter
    @lock()
    def name(self, name):
        _tecutil.VarRenameByDataSetID(self.dataset.uid, self.index + 1, name)

    @property
    def num_zones(self):
        """Number of `Zones <data_access>` in the parent `Dataset`.

        :type: `integer <int>`

        Example usage, looping over all zones by index::

            >>> for zindex in range(dataset.num_zones):
            ...     zone = dataset.zone(zindex)
        """
        return self.dataset.num_zones

    def values(self, pattern):
        """Returns `Array` by index or string pattern.

        Parameters:
            pattern (`integer <int>` or `string <str>`):  Zero-based index or
                `glob-style pattern <fnmatch.fnmatch>` in which case, the
                first match is returned.

        The `Zone.name <OrderedZone.name>` attribute is used to match the
        *pattern* to the desired `Array` though this is not necessarily
        unique::

            >>> ds = frame.dataset
            >>> print(ds)
            Dataset:
              Zones: ['Rectangular zone']
              Variables: ['x', 'y', 'z']
            >>> x = ds.variable('x')
            >>> rectzone = x.values('Rectangular zone')
            >>> rectzone == x.values(0)
            True
        """
        return Array(self.dataset.zone(pattern), self)
