#!/usr/bin/env python
#
# Hack to show image generation realtime, sample tile server implementation
#
# Rick van der Zwet <info@rickvanderzwet.nl>
from PIL import Image
from django.core.management import setup_environ
from django.http import HttpResponse
from gheat.models import *
import ImageDraw
import logging
import numpy as np
import settings
import sys

log = logging.getLogger('sample_tile')
logging.basicConfig(level=logging.DEBUG)
setup_environ(settings)


def make_circle(draw, center, radius,colour=(0,255,0)):
  """ Cicle gradient is created by creating smaller and smaller cicles """
  (center_x, center_y) = center
  for i in range(0,radius):
    draw.ellipse(
      (center_x - radius + i,
       center_y - radius + i,
       center_x + radius - i,
       center_y + radius - i
      ),
      colour +(255 * i/(radius * 2),)
    )


class Picture():
  """ Basic class, allowing simple image manipulations """
  im = None
  def __init__(self, method, size):
    self.im = Image.new(method, size)
    self.data = np.array(self.im)

  def save(self,*args, **kw):
        self.im.save(*args, **kw)

  def add_circle(self, center, radius, colour):
    """ Adding a new cicle is a matter of creating a new one in a empty layer
    and merging it with the current one 

    XXX: Very heavy code, should actually only work on the data arrays, instead
    of doing all the magic with high-level images """

    im_new = Image.new("RGBA", self.im.size)
    draw = ImageDraw.Draw(im_new)
    make_circle(draw, center, radius, colour)
    
    data2 = np.array(im_new)
    
    # Add channels to make new images
    self.data = self.data + data2
    self.im = Image.fromarray(self.data)



class LatLonDeg():
  """ Helper class for coordinate conversions """
  def __init__(self,lat_deg, lon_deg):
    self.lat = lat_deg
    self.lon = lon_deg
  def __str__(self):
    return "%.5f,%.5f" % (self.lat, self.lon)

  def deg_per_pixel(self,other,pixel_max):
    return(LatLonDeg(abs(self.lat - other.lat) / pixel_max, abs(self.lon - other.lon) / pixel_max))



# Convertions of tile XYZ to WSG coordinates stolen from:
#    http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
# <stolen>
import math
def deg2num(lat_deg, lon_deg, zoom):
  lat_rad = math.radians(lat_deg)
  n = 2.0 ** zoom
  xtile = int((lon_deg + 180.0) / 360.0 * n)
  ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
  return(xtile, ytile)

def num2deg(xtile, ytile, zoom):
  n = 2.0 ** zoom
  lon_deg = xtile / n * 360.0 - 180.0
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return(LatLonDeg(lat_deg,lon_deg))
# </stolen>


def boundbox_deg(x,y,z):
  """ Calculate the boundingbox for a image """
  return (num2deg(x,y,z), num2deg(x+1,y+1,z))



def make_tile(x,y,z):
  """ Crude attempt to generate tiles, by placing a gradient circle on a
  coordinate point. Many stuff NOT implemented yet, like:
  * Caching Images
  * Behaviour around edges (generate larger imagea)
  """
  nw_deg,se_deg = boundbox_deg(x,y,z)
  
  resolution_deg = nw_deg.deg_per_pixel(se_deg, 250)
  im = Picture("RGBA",(250,250))
  
  lat_min = 999
  lon_min = 999
  lat_max = 0
  lon_max = 0
  metingen = Meting.objects.select_related().filter(
     latitude__lte=nw_deg.lat,latitude__gte=se_deg.lat,
     longitude__lte=se_deg.lon,longitude__gte=nw_deg.lon)
  if metingen.count() > 100:
    metingen = metingen[1:100]
  
  def dif(x,y):
    return max(x,y) - min(x,y)
  
  for meting in metingen:
    lat_min = min(lat_min, meting.latitude)
    lat_max = max(lat_max, meting.latitude)
    lon_min = min(lon_min, meting.longitude)
    lon_max = max(lon_max, meting.longitude)
    xcoord = dif(nw_deg.lon,meting.longitude) / (resolution_deg.lon)
    ycoord = dif(nw_deg.lat,meting.latitude) / (resolution_deg.lat)
    log.info(meting.accespoint.ssid, meting.latitude, meting.longitude, xcoord, ycoord)
    im.add_circle((xcoord,ycoord),z,(255,0,0))
  
  log.info("BoundingBox NW: %s" % nw_deg)
  log.info("BoundingBox SE: %s" % se_deg)
  log.info("")
  log.info("MetingBox   NW: %.5f,%.5f" % (lat_max, lon_min))
  log.info("MetingBox   SE: %.5f,%.5f" % (lat_min, lon_max))
  log.info("")
  log.info("Metingen Count: %s" % metingen.count())

  return im


# Create your views here.
def serve_tile(request,zoom,x,y):
  im = make_tile(int(x),int(y),int(zoom))
  response = HttpResponse(mimetype="image/png")
  im.save(response, 'PNG')
  return response

if __name__ == '__main__':
  x = int(sys.argv[1])
  y = int(sys.argv[2])
  z = 14

  im = make_tile(x,y,z)
  filename = 'sample-gradient.png'
  im.save(filename)
  log.info("#INFO: Output saved as '%s'" % filename)
