"""
Collection of utility functions for wrapping-textures.

Written by Zachary Ferguson
"""

from __future__ import print_function

import sys
import time
import math
import itertools
import numpy

from recordclass import recordclass

######################################
# Record classes for neccessary data #
######################################
UV    = recordclass('UV', ['u', 'v'])
Pixel = recordclass('Pixel', ['x', 'y'])
XY    = recordclass('XY', ['x', 'y'])
XYZ   = recordclass('XYZ', ['x', 'y', 'z'])

# Quadtratic energy: x.T @ Q @ x + 2 * x.T @ L + C = 0
QuadEnergy = recordclass('QuadraticEnergy', ['Q', 'L', 'C'])


def pairwise(iterable):
    """ Returns: s -> (s0,s1), (s1,s2), (s2, s3), ... """
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)


def pairwise_loop(iterable):
    """
    Create pair wise list of the iterable given with the last element being the
    first. Returns: s -> (s0,s1), (s1,s2), (s2, s3), ..., (sN, s0)
    """
    return tuple(pairwise(iterable)) + ((iterable[-1], iterable[0]),)


def rowcol_to_index(row, col, width):
    """ Convert row major coordinates to 1-D index. """
    return width * row + col


def lerp(t, x0, x1):
    """ Linearly interpolate between x0 and x1. """
    return x0 + t * (x1 - x0)


def lerpPair(t, p0, p1):
    """ Linearly interpolate independent indexed paires """
    return [lerp(t, p0[0], p1[0]), lerp(t, p0[1], p1[1])]


def lerp_UV(t, uv0, uv1):
    """
        Linearly interpolate between (u0,v0) and (u1,v1).
        Returns a UV object.
    """
    return UV(*lerpPair(t, uv0, uv1))


def lerp_XY(t, xy0, xy1):
    """
    Linearly interpolate between (x0,y0) and (x1,y1).
    Returns a XY object.
    """
    return XY(*lerpPair(t, xy0, xy1))


def UV_to_XY(uv, width, height, is_clamped = False):
    """
    Converts the given UV to XY coordinates
    uv is defined in terms of GPU UV space.
    """
    # s*width - 0.5; t*height - 0.5
    xy = XY(x = uv.u * width - 0.5, y = uv.v * height - 0.5)

    if is_clamped:
        xy = (numpy.clip(xy[0], 0, max(0, width  - 1)),
              numpy.clip(xy[1], 0, max(0, height - 1)))
    return xy


def UVs_to_XYs(uvEdges, width, height):
    """ Convert a UV edge to XY space in the texture """
    return [UV_to_XY(vert, width, height) for edge in uvEdges for vert in edge]


def globalUV_to_local(uv, minX, minY, width, height):