# -*- coding: utf-8 -*-
# Copyright (c) 2015, Vispy Development Team.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.

from __future__ import division

import numpy as np

from ...util import transforms
from ...geometry import Rect
from ._util import arg_to_vec4, as_vec4
from .base_transform import BaseTransform


class NullTransform(BaseTransform):
    """ Transform having no effect on coordinates (identity transform).
    """
    glsl_map = "vec4 null_transform_map(vec4 pos) {return pos;}"
    glsl_imap = "vec4 null_transform_imap(vec4 pos) {return pos;}"

    Linear = True
    Orthogonal = True
    NonScaling = True
    Isometric = True

    @arg_to_vec4
    def map(self, coords):
        """Map coordinates

        Parameters
        ----------
        coords : array-like
            Coordinates to map.
        """
        return coords

    def imap(self, coords):
        """Inverse map coordinates

        Parameters
        ----------
        coords : array-like
            Coordinates to inverse map.
        """
        return coords

    def __mul__(self, tr):
        return tr

    def __rmul__(self, tr):
        return tr


class STTransform(BaseTransform):
    """ Transform performing only scale and translate, in that order.

    Parameters
    ----------
    scale : array-like
        Scale factors for X, Y, Z axes.
    translate : array-like
        Scale factors for X, Y, Z axes.
    """
    glsl_map = """
        vec4 st_transform_map(vec4 pos) {
            return vec4(pos.xyz * $scale.xyz + $translate.xyz * pos.w, pos.w);
        }
    """

    glsl_imap = """
        vec4 st_transform_imap(vec4 pos) {
            return vec4((pos.xyz - $translate.xyz * pos.w) / $scale.xyz,
                        pos.w);
        }
    """

    Linear = True
    Orthogonal = True
    NonScaling = False
    Isometric = False

    def __init__(self, scale=None, translate=None):
        super(STTransform, self).__init__()

        self._update_map = True
        self._update_imap = True
        self._scale = np.ones(4, dtype=np.float32)
        self._translate = np.zeros(4, dtype=np.float32)

        s = ((1.0, 1.0, 1.0, 1.0) if scale is None else
             as_vec4(scale, default=(1, 1, 1, 1)))
        t = ((0.0, 0.0, 0.0, 0.0) if translate is None else
             as_vec4(translate, default=(0, 0, 0, 0)))
        self._set_st(s, t)

    @arg_to_vec4
    def map(self, coords):
        """Map coordinates

        Parameters
        ----------
        coords : array-like
            Coordinates to map.

        Returns
        -------
        coords : ndarray
            Coordinates.
        """
        m = np.empty(coords.shape)
        m[:, :3] = (coords[:, :3] * self.scale[np.newaxis, :3] +
                    coords[:, 3:] * self.translate[np.newaxis, :3])
        m[:, 3] = coords[:, 3]
        return m

    @arg_to_vec4
    def imap(self, coords):
        """Invert map coordinates

        Parameters
        ----------
        coords : array-like
            Coordinates to inverse map.

        Returns
        -------
        coords : ndarray
            Coordinates.
        """
        m = np.empty(coords.shape)
        m[:, :3] = ((coords[:, :3] -
                     coords[:, 3:] * self.translate[np.newaxis, :3]) /
                    self.scale[np.newaxis, :3])
        m[:, 3] = coords[:, 3]
        return m

    def shader_map(self):
        if self._update_map:
            self._shader_map['scale'] = self.scale
            self._shader_map['translate'] = self.translate
            self._update_map = False
        return self._shader_map

    def shader_imap(self):
        if self._update_imap:
            self._shader_imap['scale'] = self.scale
            self._shader_imap['translate'] = self.translate
            self._update_imap = False
        return self._shader_imap

    @property
    def scale(self):
        return self._scale.copy()

    @scale.setter
    def scale(self, s):
        s = as_vec4(s, default=(1, 1, 1, 1))
        self._set_st(scale=s)

    @property
    def translate(self):
        return self._translate.copy()

    @translate.setter
    def translate(self, t):
        t = as_vec4(t, default=(0, 0, 0, 0))
        self._set_st(translate=t)

    def _set_st(self, scale=None, translate=None):
        update = False

        if scale is not None and not np.all(scale == self._scale):
            self._scale[:] = scale
            update = True

        if translate is not None and not np.all(translate == self._translate):
            self._translate[:] = translate
            update = True

        if update:
            self._update_map = True
            self._update_imap = True
            self.update()   # inform listeners there has been a change

    def move(self, move):
        """Change the translation of this transform by the amount given.

        Parameters
        ----------
        move : array-like
            The values to be added to the current translation of the transform.
        """
        move = as_vec4(move, default=(0, 0, 0, 0))
        self.translate = self.translate + move

    def zoom(self, zoom, center=(0, 0, 0), mapped=True):
        """Update the transform such that its scale factor is changed, but
        the specified center point is left unchanged.

        Parameters
        ----------