#!/usr/bin/python
"""
  This script may be used from PSP (Python Server Pages) to
  produce a grid of images..

  OS: Linux / Win32
  Required: Python 2.4 (http://www.python.org), PIL 1.4 (http://www.pythonware.com/products/pil)
  Copyright: LGPL (http://www.gnu.org/copyleft/lesser.html)

  History:
      2005-09-18 Hans Maerki, Initial hack
      2005-12-03 Hans Maerki, Now with support for multiple webcams.
      2006-08-15 Hans Maerki, Obfuscating private areas
"""
import os
import os.path
import re
import inspect
import time
import datetime

# This is a very ugly hack to find out where this script resides.
def dummy(): pass
strModuleFilename = inspect.getabsfile(dummy)
strModuleDirectoryname = os.path.dirname(strModuleFilename)
strBaseDirectoryname = os.path.abspath(strModuleDirectoryname + '/../')

############################################################################################
# Configuration
############################################################################################
strFilenameRawTemplate    = '%(strWebcamPrefix)s/images_raw/%(strDate)s/image_%(strDate)s_%(strTime)s_raw.jpg'
strFilenameThumbsTemplate = '%(strWebcamPrefix)s/images_thumbs/%(strDate)s/image_%(strDate)s_%(strTime)s_%(strWidth)s.jpg'

# Example: image_2005-09-12_20-45-23_raw.jpg
# ->
#  {
#    'strSecond': '23',
#    'strDate': '2005-09-12',
#    'strDay': '12',
#    'strMonth': '09',
#    'strHour': '20',
#    'strYear': '2005',
#    'strMinute': '45',
#    'strTime': '20-45-23'
#  }
objExpressionImageFilesystem = re.compile(r"""
  ^image
  _
  (?P<strDate>
    (?P<strYear>\d{4})-(?P<strMonth>\d{2})-(?P<strDay>\d{2})         # Datum
  )
  _
  (?P<strTime>
    (?P<strHour>\d{2})-(?P<strMinute>\d{2})-(?P<strSecond>\d{2})    # Zeitpunkt
  )
  _raw.jpg$
""", re.VERBOSE)

# Example: image-webcam_west-2005-09-12_20-45-23_raw.jpg
# ->
#  {
#    'strWebcamPrefix': 'webcam_west',
#    'strSecond': '23',
#    'strDate': '2005-09-12',
#    'strDay': '12',
#    'strMonth': '09',
#    'strHour': '20',
#    'strYear': '2005',
#    'strMinute': '45',
#    'strTime': '20-45-23'
#    'strWidth': 'raw' or '160'
#  }
objExpressionImageUrl = re.compile(r"""
  ^image-
  (?P<strWebcamPrefix>.*?)
  -
  (?P<strDate>
    (?P<strYear>\d{4})-(?P<strMonth>\d{2})-(?P<strDay>\d{2})         # Datum
  )
  _
  (?P<strTime>
    (?P<strHour>\d{2})-(?P<strMinute>\d{2})-(?P<strSecond>\d{2})    # Zeitpunkt
  )
  _

  (?P<strWidth>(\d+|raw))                                         # Breite
  .jpg$
""", re.VERBOSE)

############################################################################################
# Read the filenames of all images in a folder
############################################################################################
dictDayCache = {}

def getImagesByDay(strWebcamPrefix, strDate):
  """
    Example ('09-00-00', '09-30-00', '10-30-00')
    Returns a list of all images found.
    Returns [] if no images found.
  """
  try:
    listImages = os.listdir(strBaseDirectoryname + '/' + strWebcamPrefix + '/images_raw/' + strDate)
    listImages.sort()
  # except WindowsError, e:
  except:
    return []
  listTimes = []
  for strImage in listImages:
    objMatch = objExpressionImageFilesystem.match(strImage)
    if objMatch:
      listTimes.append(objMatch.group('strTime'))
      # listTimes.append(objMatch.groupdict()['strTime'])
      # print strImage + ': ' + str(objMatch.groupdict())
  return listTimes

def getImagesByDayCached(strWebcamPrefix, strDate):
  try:
    return dictDayCache[strDate]
  except KeyError, e:
    pass

  listTimes = dictDayCache[strDate] = getImagesByDay(strWebcamPrefix, strDate)
  return listTimes

############################################################################################
# Find the best image for the grid
############################################################################################
def getNextImage(strWebcamPrefix, strDate, strTimeStart, strTimeEnd):
  """
    Example strDate: '2005-09-17'
    Example strTime: '09-00-50'
    return
      strTime of the next image
      null if no image was found.
  """
  listTimes = getImagesByDayCached(strWebcamPrefix, strDate)
  for strTime_ in listTimes:
    if strTime_ >= strTimeEnd:
      return None
    if strTime_ >= strTimeStart:
      return strTime_
  return None

def getImagePath(strImageDescription):
  """
    Get the image-link.
    If the image - in case of a thumb - does not exist, it will be created.
    Example strImageDescription: 'image-webcam_west-2005-09-12_20-45-23_300.jpg'
  """
  objMatch = objExpressionImageUrl.match(strImageDescription)
  if not objMatch:
    raise "Pattern '%s' does not match '%s'!" % (strImageDescription, objExpressionImageUrl.pattern)
  strWidth = objMatch.group('strWidth')
  strDate = objMatch.group('strDate')
  strWebcamPrefix = objMatch.group('strWebcamPrefix')
  strFilenameRaw = strBaseDirectoryname + '/' + strFilenameRawTemplate % objMatch.groupdict()
  strFilenameThumb = strBaseDirectoryname + '/' + strFilenameThumbsTemplate % objMatch.groupdict()
  if strWidth == 'raw':
    if True:
      # Now, raw images has to be processed too: obfuscate private areas.
      print 'strFilenameThumb: ' + strFilenameThumb
      if not os.path.exists(strFilenameThumb):
        # Neet do obfuscate private areas
        create_thumb(strFilenameRaw, strFilenameThumb, strDate, strWebcamPrefix)
      # TODO: remove
      # create_thumb(strFilenameRaw, strFilenameThumb, strDate, strWebcamPrefix)
      return strFilenameThumb
    # A 'raw' image was requested, this image already exists in the filesystem.
    return strFilenameRaw
  if not os.path.exists(strFilenameThumb):
    # Need to compress the image
    create_thumb(strFilenameRaw, strFilenameThumb, strDate, strWebcamPrefix, int(objMatch.group('strWidth')))
  # TODO: remove
  # create_thumb(strFilenameRaw, strFilenameThumb, strDate, strWebcamPrefix, int(objMatch.group('strWidth')))
  return strFilenameThumb

############################################################################################
# Resize a image
############################################################################################
def create_thumb(strFilenameImage, strFilenameThumb, strDate, strWebcamPrefix, iDestinationWidth=None):
  """
    Create a thumbnail from a give image.
  """
  import PIL.Image
  import PIL.ImageFileIO

  fp = open(strFilenameImage, "rb")
  image = PIL.Image.open(PIL.ImageFileIO.ImageFileIO(fp))
  image.load() # make sure we can read the raster data
  if iDestinationWidth != None:
    iWidth, iHeight = image.size
    iDestinationHeight = iHeight*iDestinationWidth/iWidth
    # Resize the image
    image = image.resize((iDestinationWidth, iDestinationHeight), PIL.Image.ANTIALIAS)
    
  obfuscate_private_areas(image, strWebcamPrefix, strDate)

  strDir = os.path.dirname(strFilenameThumb)
  if not os.path.exists(strDir):
    os.makedirs(strDir)
  image.save(strFilenameThumb, quality=70)

def getTimeInSeconds(strTime):
  """
    Example: strTime = '09-30'
    returns seconds since '00-00'
  """
  return (int(strTime[0:2]) * 60 + int(strTime[3:5]) ) * 60

import config_obfuscated_areas

############################################################################################
# Obfuscate areas to protect privacy
############################################################################################
def obfuscate_private_areas(im, strWebcamPrefix, strDateStart):
  import Image
  import ImageFilter
  import ImageDraw

  # Loop ueber alle zu korrigierenden Stellen (Boxen)
  for strName, strActualWebCamPrefix, strActualDateStart, iCount, rectArea in config_obfuscated_areas.listAreas:
    if strActualWebCamPrefix != strWebcamPrefix:
      # This is not our cam: skip
      continue
    if strDateStart < strActualDateStart:
      # The building doesn't exist yet: skip
      continue
    # Prozentzahlen auf Pixel hochrechnen
    rectArea = (rectArea[0]*im.size[0], rectArea[1]*im.size[1], rectArea[2]*im.size[0], rectArea[3]*im.size[1])
    rectArea = map(lambda r:int(r), rectArea)
    size = (rectArea[2]-rectArea[0], rectArea[3]-rectArea[1])
  
    # Box herauschneiden
    imBox = im.crop(rectArea)
  
    if config_obfuscated_areas.bDebug:
      # Hilfslinien und Texte
      draw = ImageDraw.Draw(imBox)
      draw.text((5,20), strName, fill='yellow')
      draw = ImageDraw.Draw(im)
      draw.line(rectArea, 'yellow')
  
    imFlip = imBox.transpose(Image.FLIP_LEFT_RIGHT)
    if iCount>0:
      iStart, iStop = 0, iCount
    else:
      iStart, iStop = iCount-1, -1
    for i in range(iStart, iStop):
      upperLeftCorner = (rectArea[0]+(i+1)*imBox.size[0], rectArea[1])
      if i%2==0:
        im.paste(imBox, upperLeftCorner)
      else:
        im.paste(imFlip, upperLeftCorner)
      if config_obfuscated_areas.bDebug:
        downRightCorner = (rectArea[0]+(i+2)*imBox.size[0], rectArea[1]+imBox.size[1])
        draw.line((upperLeftCorner, downRightCorner), 'blue')


############################################################################################
# Prepare the grid
############################################################################################
def getIntervals(strWebcamPrefix, dictForm):
  """
    return a tuple:
      - listColumns: list of time's (for the table header)
      - listRows: list of
        - date (for the first table column
        - list of links (for images)
  """
  global dictDayCache
  dictDayCache = {}  # Empty cache
  listColumns = []
  iColIntervalSec = getTimeInSeconds(dictForm['col_interval'])
  iColStartSec = getTimeInSeconds(dictForm['col_start'])
  for iCol in range(int(dictForm['col_count'])):
    # for 'time' we use gmtime() (no daylight saving...)
    listColumns.append(time.strftime('%H-%M', time.gmtime(iColStartSec+iCol*iColIntervalSec)))

  listRows = []
  fRowStartSec = time.mktime(time.strptime(dictForm['row_start'], '%Y-%m-%d'))
  iRowIntervalSec = int(dictForm['row_interval']) * 24 * 60 * 60
  iRowCount = int(dictForm['row_count'])
  for iRow in range(iRowCount):
    if dictForm['forward'] == '1':
      # fRowActualSec = fRowStartSec + iRow*iRowIntervalSec
      fRowActualSec = fRowStartSec - (iRowCount-iRow-1)*iRowIntervalSec
    else:
      # fRowActualSec = fRowStartSec + (iRowCount-iRow-1)*iRowIntervalSec
      fRowActualSec = fRowStartSec - iRow*iRowIntervalSec
    listRowColumns = []
    listRowColumns.append(time.strftime('%A<br/>%Y-%m-%d', time.localtime(fRowActualSec)))
    for iCol in range(int(dictForm['col_count'])):
      strTargetDate = time.strftime('%Y-%m-%d', time.localtime(fRowActualSec))
      strTargetTime = time.strftime('%H-%M-%S', time.gmtime(iColStartSec + iCol*iColIntervalSec))
      strTargetTimeEnd = time.strftime('%H-%M-%S', time.gmtime(iColStartSec + (iCol+1)*iColIntervalSec))
      strAvailableTime = getNextImage(strWebcamPrefix, strTargetDate, strTargetTime, strTargetTimeEnd)
      strImagePathThumb = None
      strImagePathRaw = None
      if strAvailableTime:
        strImagePathThumb = "image-%s-%s_%s_%s.jpg" % (strWebcamPrefix, strTargetDate, strAvailableTime, dictForm['width'])
        strImagePathRaw = "image-%s-%s_%s_raw.jpg" % (strWebcamPrefix, strTargetDate, strAvailableTime)
      listRowColumns.append((strImagePathThumb, strImagePathRaw))
    listRows.append(listRowColumns)
    
  return listColumns, listRows

############################################################################################
# Main: Do some basic testing
############################################################################################
if __name__ == '__main__':
  print getIntervals('webcam_west',
    {
      'row_count': '20',
      'row_start': '2005-12-03',
      'row_interval': '1',
      'col_count': '8',
      'col_start': '09-00',
      'col_interval': '01-15',
      'width': '100',
      'forward': '', 
    }
  )

  listTimes = getImagesByDayCached('webcam_west', '2005-12-03')
  print listTimes
  listTimes = getImagesByDayCached('webcam_west', '2005-12-03')
  print listTimes
  print getNextImage('webcam_west', '2005-12-03', '08-15-00', '09-00-00')
  print getImagePath('image-webcam_west-2005-12-03_10-16-10_raw.jpg')
  print getImagePath('image-webcam_west-2005-12-03_10-16-10_300.jpg')
  print getImagePath('image-webcam_west-2006-01-01_01-01-01_300.jpg')
  print getImagePath('image-webcam_west-2006-01-01_01-01-01_800.jpg')
  print getImagePath('image-webcam_west-2006-01-01_01-01-01_raw.jpg')
