""" A module for working with images in matrix format.
  An image is represented by two matrices, representing points
  and colors.
  The points matrix has row labels {'x', 'y', 'u'} and column
  labels (0,0) through (w, h), inclusive, where (w, h) are the
  width and height of the original image. The colors matrix has
  row labels {'r', 'g', 'b'} and column labels (0,0) through
  (w-h, h-1).
  
  The column labels for these matrices represent "lattice points"
  on the original image. For pixel (i,j) in the original image,
  the (i,j) column in the colors matrix represents the pixel
  color and the (i,j), (i+1, j), (i+1, j+1), and (i, j+1)
  columns in the points matrix represent the boundary of the
  pixel region.
  """

import mat
import image
from math import floor, sqrt
import sys
from feedback import *

# Native imports
import math

def file2mat(path, row_labels = ('x', 'y', 'u')):
  """input: a filepath to an image in .png format
    output: the pair (points, matrix) of matrices representing
    the image."""
  return image2mat(image.file2image(path), row_labels)

def image2mat(image, ext, row_labels = ('x', 'y', 'u')):
  """ input: an image in list-of-lists format
    output: a pair (points, colors) of matrices representing the
    image.
    Note: The input list-of-lists can consist of either integers
    in [0, 255] for grayscale or 3-tuple of integers representing
    the rgb color coordinates
    """
  min,max = ext
  h = len(image)
  w = len(image[0])
  rx, ry, ru = row_labels
  xrng = range(min[0],max[0]+1,1)
  yrng = range(min[1],max[1]+1,1)
  ptsD = (set(row_labels), {(x,y) for x in xrng for y in yrng})
  ptsF = {}
  colorsD = ({'r', 'g', 'b'}, {(x,y) for x in xrng for y in yrng})
  colorsF = {}

  createprogmeter("Extracting points from the image",
                  int(max[1]+1-min[1]))  
  for y in yrng:
    for x in xrng:
      pt = (x,y)
      ptsF[(rx, pt)] = x
      ptsF[(ry, pt)] = y
      ptsF[(ru, pt)] = 1
      if x < w and y < h:
        col = image[y][x]
        if type(col) is int:
          red, green, blue = col, col, col
        else:
          red, green, blue = col
        colorsF[('r', pt)] = red
        colorsF[('g', pt)] = green
        colorsF[('b', pt)] = blue
    progress()
  finishprogress()
  return mat.Mat(ptsD, ptsF), mat.Mat(colorsD, colorsF)

def mat2image(pts, colors, ext, yext, xscale, row_labels,
              xmin, ymin, xmax, ymax, tl, bl):
  rx, ry, ru = row_labels

  xext = floor(yext * xscale)
  yinc = (ymax-ymin)/yext
  xinc = (xmax-xmin)/xext
  
  createprogmeter("Generating pixel information", int(yext))

  img = []
  try:
    for i in range(int(yext)):
      fac = i/yext
      k = (round(tl[0]+((bl[0]-tl[0])*fac)),
           round(tl[1]+((bl[1]-tl[1])*fac)))
      y = ymin + i * yinc
      row = []
      for j in range(int(xext)):
        x = xmin + j * xinc
        col,k = get_color(k, pts, colors, ext, x, y)
        row.append(col)
      img.append(row)
      progress()
  except:
    info = sys.exc_info()
    printtolog("Unexpected error: {0}".
               format(info[0]),
               True)
    raise
  finally:
    finishprogress()
  return img

def get_color(k, pts, colors, ext, x, y):
  adj = get_adjacent(k,pts,ext,x,y)
  best = [k for k,v in sorted(adj, key=lambda kv: kv[1])][0]
  ret = (colors[('r',best)],
         colors[('g',best)],
         colors[('b',best)]), best
  return ret

def get_adjacent(k, pts, ext, x, y):
  kx, ky = k
  ((minx,miny),(maxx,maxy)) = ext
  
  xr = range(-1,2,1)
  yr = range(-1,2,1)
  adj = [(kx+i,ky+j) for i in xr for j in yr
         if kx+i > minx and kx+i < maxx and ky+j > miny
         and ky+j < maxy]
  return [(xy,quad_midpoint(xy,pts,x,y)) for xy in adj]

def quad_midpoint(k, pts, x, y):
  kx,ky = k
  rx,ry = 'y1','y2'
  x0 = pts[(rx, k)]
  y0 = pts[(ry, k)]
  x2 = pts[(rx, (kx+1, ky+1))]
  y2 = pts[(ry, (kx+1, ky+1))]
  midx = x0+((x2-x0)/2)
  midy = y0+((y2-y0)/2)
  return sqrt((x-midx)**2 + (y-midy)**2)