# -*- coding: UTF-8 -*-
#Copyright (c) 2008, Media Modifications Ltd.

#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:

#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.

#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.


import uuid
import urllib
import string
import fnmatch
import os
import random
import cairo
import gtk
import gtk.gdk
import pygtk
pygtk.require('2.0')
import gc
import math
import time
import gobject
import operator

import logging
logger = logging.getLogger('record:model.py')

import sugar.env

from constants import Constants
from instance import Instance
from recorded import Recorded
from color import Color
from ui import UI
import utils
import record
import serialize


class Model:
    def __init__( self, pca ):
        self.ca = pca
        self.MODE = Constants.MODE_PHOTO
        self.UPDATING = True
        self.RECORDING = False
        self.FULL = self._isXoFull()

        self.mediaHashs = {}
        for key,value in Constants.mediaTypes.items():
            self.mediaHashs[key] = []


    def updateXoFullStatus( self ):
        self.FULL = self._isXoFull()


    def _isXoFull( self ):
        full = False
        if (utils.getFreespaceKb() <= Constants.keepFreeKbOnXo):
            full = True

        return full


    def getRecdByMd5( self, md5 ):
        for mh in range (0, len(self.mediaHashs)):
            for r in range (0, len(self.mediaHashs[mh])):
                recd = self.mediaHashs[mh][r]
                if (recd.thumbMd5 == md5):
                    return recd
                elif (recd.mediaMd5 == md5):
                    return recd

        return None


    def isVideoMode( self ):
        return self.MODE == Constants.MODE_VIDEO


    def isPhotoMode( self ):
        return self.MODE == Constants.MODE_PHOTO


    def displayThumb( self, recd, forceUpdating ):
        #to avoid Xlib: unexpected async reply error when taking a picture on a gst callback, always call with idle_add
        #this happens b/c this might get called from a gstreamer callback
        if (not recd.type == self.MODE):
            return

        if (forceUpdating):
            self.setUpdating( True )
        hash = self.mediaHashs[recd.type]
        if (len(hash) > 0):
            self.ca.ui.addThumb(recd, forceUpdating)
        if (forceUpdating):
            self.setUpdating( False )


    def setupMode( self, type, update ):
        if (not type == self.MODE):
            return

        self.setUpdating( True )
        self.ca.ui.removeThumbs()
        hash = self.mediaHashs[type]
        for i in range (0, len(hash)):
            self.ca.ui.addThumb( hash[i], True )
        if (update):
            self.ca.ui.updateModeChange()
        self.setUpdating(False)


    def showNextThumb( self, shownRecd ):
        if (shownRecd == None):
            self.showLastThumb()
        else:
            hash = self.mediaHashs[self.MODE]
            if (len(hash) > 0):
                hash = self.mediaHashs[self.MODE]
                i = operator.indexOf( hash, shownRecd )
                i = i+1
                if (i>=len(hash)):
                    i = 0
                self.ca.ui.showThumbSelection( hash[i] )


    def showPrevThumb( self, shownRecd ):
        if (shownRecd == None):
            self.showLastThumb()
        else:
            hash = self.mediaHashs[self.MODE]
            if (len(hash) > 0):
                hash = self.mediaHashs[self.MODE]
                i = operator.indexOf( hash, shownRecd )
                i = i-1
                if (i<0):
                    i = len(hash)-1
                self.ca.ui.showThumbSelection( hash[i] )


    def showLastThumb( self ):
        hash = self.mediaHashs[self.MODE]
        if (len(hash) > 0):
            self.ca.ui.showThumbSelection( hash[len(hash)-1] )


    def doShutter( self ):
        if (self.UPDATING):
            return

        if (self.MODE == Constants.MODE_PHOTO):
            self.startTakingPhoto()
        elif (self.MODE == Constants.MODE_VIDEO):
            if (not self.RECORDING):
                self.startRecordingVideo()
            else:
                #post-processing begins now, so queue up this gfx
                self.ca.ui.showPostProcessGfx(True)
                self.stopRecordingVideo()
        elif (self.MODE == Constants.MODE_AUDIO):
            if (not self.RECORDING):
                self.startRecordingAudio()
            else:
                #post-processing begins now, so queue up this gfx
                self.ca.ui.showPostProcessGfx(True)
                self.stopRecordingAudio()


    def stopRecordingAudio( self ):
        gobject.source_remove( self.ca.ui.UPDATE_DURATION_ID )
        self.ca.ui.progressWindow.updateProgress( 0, "" )
        self.setUpdating( True )
        self.setRecording( False )
        self.ca.ui.FULLSCREEN = False
        self.ca.ui.updateVideoComponents()

        self.ca.glive.stopRecordingAudio( )


    def saveAudio( self, tmpPath, pixbuf ):
        self.setUpdating( True )

        recd = self.createNewRecorded( Constants.TYPE_AUDIO )
        os.rename( tmpPath, os.path.join(Instance.instancePath,recd.mediaFilename))

        thumbPath = os.path.join(Instance.instancePath, recd.thumbFilename)
        scale = float((UI.dim_THUMB_WIDTH+0.0)/(pixbuf.get_width()+0.0))
        thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT)
        thumbImg.write_to_png(thumbPath)

        imagePath = os.path.join(Instance.instancePath, "audioPicture.png")
        imagePath = utils.getUniqueFilepath( imagePath, 0 )
        pixbuf.save( imagePath, "png", {} )
        recd.audioImageFilename = os.path.basename(imagePath)

        #at this point, we have both audio and thumb sapath, so we can save the recd
        self.createNewRecordedMd5Sums( recd )

        audioHash = self.mediaHashs[Constants.TYPE_AUDIO]
        audioHash.append( recd )
        gobject.idle_add(self.displayThumb, recd, True)
        self.doPostSaveVideo()
        self.meshShareRecd( recd )


    def startRecordingVideo( self ):
        self.setUpdating( True )
        self.setRecording( True )
        #let the red eye kick in before we start the video underway
        gobject.idle_add( self.beginRecordingVideo )


    def beginRecordingVideo( self ):
        self.ca.ui.recordVideo()
        self.setUpdating( False )


    def startRecordingAudio( self ):
        self.setUpdating( True )
        self.setRecording( True )
        self.ca.ui.recordAudio()
        self.setUpdating( False )


    def setUpdating( self, upd ):
        self.UPDATING = upd
        self.ca.ui.updateButtonSensitivities()


    def setRecording( self, rec ):
        self.RECORDING = rec
        self.ca.ui.updateButtonSensitivities()


    def stopRecordingVideo( self ):
        self.ca.glive.stopRecordingVideo()
        gobject.source_remove( self.ca.ui.UPDATE_DURATION_ID )
        self.setUpdating( True )
        self.setRecording( False )
        self.ca.ui.FULLSCREEN = False
        self.ca.ui.updateVideoComponents()


    def saveVideo( self, pixbuf, tmpPath, wid, hit ):
        recd = self.createNewRecorded( Constants.TYPE_VIDEO )
        os.rename( tmpPath, os.path.join(Instance.instancePath,recd.mediaFilename))

        thumbPath = os.path.join(Instance.instancePath, recd.thumbFilename)
        scale = float((UI.dim_THUMB_WIDTH+0.0)/(wid+0.0))
        thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT)
        thumbImg.write_to_png(thumbPath)

        self.createNewRecordedMd5Sums( recd )

        videoHash = self.mediaHashs[Constants.TYPE_VIDEO]
        videoHash.append( recd )
        self.doPostSaveVideo()
        gobject.idle_add(self.displayThumb, recd, True)
        self.meshShareRecd( recd )


    def meshShareRecd( self, recd ):
        #hey, i just took a cool video.audio.photo!  let me show you!
        if (self.ca.recTube != None):
            logger.debug('meshShareRecd')
            recdXml = serialize.getRecdXmlMeshString(recd)
            self.ca.recTube.notifyBudsOfNewRecd( Instance.keyHashPrintable, recdXml )


    def cannotSaveVideo( self ):
        self.doPostSaveVideo()


    def doPostSaveVideo( self ):
        self.ca.ui.showPostProcessGfx(False)

        #prep the ui for your return
        self.ca.ui.LAST_MODE = -1
        self.ca.ui.TRANSCODING = False

        #resume live video from the camera (if the activity is active)
        if (self.ca.ui.ACTIVE):
            self.ca.ui.updateVideoComponents()

        self.ca.ui.progressWindow.updateProgress( 0, "" )
        self.setRecording( False )
        self.setUpdating( False )


    def abandonRecording( self ):

        self.ca.ui.LAST_MODE = -1
        self.ca.ui.TRANSCODING = False
        self.ca.ui.completeTimer()
        self.ca.ui.completeCountdown()
        self.setRecording(False)

        self.ca.ui.progressWindow.updateProgress( 0, "" )

        self.ca.glive.abandonMedia()


    def stoppedRecordingVideo( self ):
        self.setUpdating( False )


    def startTakingPhoto( self ):
        self.setUpdating( True )
        self.ca.glive.takePhoto()


    def savePhoto( self, pixbuf ):
        recd = self.createNewRecorded( Constants.TYPE_PHOTO )

        imgpath = os.path.join(Instance.instancePath, recd.mediaFilename)
        pixbuf.save( imgpath, "jpeg" )

        thumbpath = os.path.join(Instance.instancePath, recd.thumbFilename)
        scale = float((UI.dim_THUMB_WIDTH+0.0)/(pixbuf.get_width()+0.0))
        thumbImg = utils.generateThumbnail(pixbuf, scale, UI.dim_THUMB_WIDTH, UI.dim_THUMB_HEIGHT)
        thumbImg.write_to_png(thumbpath)
        gc.collect()
        #now that we've saved both the image and its pixbuf, we get their md5s
        self.createNewRecordedMd5Sums( recd )

        photoHash = self.mediaHashs[Constants.TYPE_PHOTO]
        photoHash.append( recd )
        gobject.idle_add(self.displayThumb, recd, True)

        self.meshShareRecd( recd )


    def addMeshRecd( self, recd ):
        #todo: sort on time-taken, not on their arrival time over the mesh (?)
        self.mediaHashs[recd.type].append( recd )

        #updateUi, but don't lock up the buttons if they're recording or whatever
        gobject.idle_add(self.displayThumb, recd, False)


    def createNewRecorded( self, type ):
        recd = Recorded( )

        recd.recorderName = Instance.nickName
        recd.recorderHash = Instance.keyHashPrintable

        #to create a file, use the hardware_id+time *and* check if available or not
        nowtime = int(time.time())
        recd.time = nowtime
        recd.type = type

        mediaThumbFilename = str(recd.recorderHash) + "_" + str(recd.time)
        mediaFilename = mediaThumbFilename
        mediaFilename = mediaFilename + "." + Constants.mediaTypes[type][Constants.keyExt]
        mediaFilepath = os.path.join( Instance.instancePath, mediaFilename )
        mediaFilepath = utils.getUniqueFilepath( mediaFilepath, 0 )
        recd.mediaFilename = os.path.basename( mediaFilepath )

        thumbFilename = mediaThumbFilename + "_thumb.jpg"
        thumbFilepath = os.path.join( Instance.instancePath, thumbFilename )
        thumbFilepath = utils.getUniqueFilepath( thumbFilepath, 0 )
        recd.thumbFilename = os.path.basename( thumbFilepath )

        stringType = Constants.mediaTypes[type][Constants.keyIstr]
        recd.title = Constants.istrBy % {"1":stringType, "2":str(recd.recorderName)}

        recd.colorStroke = Instance.colorStroke
        recd.colorFill = Instance.colorFill

        logger.debug('createNewRecorded: ' + str(recd) + ", thumbFilename:" + str(recd.thumbFilename))
        return recd


    def createNewRecordedMd5Sums( self, recd ):
        recd.thumbMd5 = recd.mediaMd5 = str(uuid.uuid4())

        #load the thumbfile
        thumbFile = os.path.join(Instance.instancePath, recd.thumbFilename)
        tBytes = os.stat(thumbFile)[6]
        recd.thumbBytes = tBytes

        recd.tags = ""

        #load the mediafile
        mediaFile = os.path.join(Instance.instancePath, recd.mediaFilename)
        mBytes = os.stat(mediaFile)[6]
        recd.mediaBytes = mBytes


    def deleteRecorded( self, recd ):
        recd.deleted = True

        #clear the index
        hash = self.mediaHashs[recd.type]
        index = hash.index(recd)
        hash.remove( recd )

        if (not recd.meshUploading):
            self.doDeleteRecorded( recd )


    def doDeleteRecorded( self, recd ):
        #remove files from the filesystem if not on the datastore
        if (recd.datastoreId == None):
            mediaFile = recd.getMediaFilepath()
            if (os.path.exists(mediaFile)):
                os.remove(mediaFile)

            thumbFile = recd.getThumbFilepath( )
            if (os.path.exists(thumbFile)):
                os.remove(thumbFile)
        else:
            #remove from the datastore here, since once gone, it is gone...
            serialize.removeMediaFromDatastore( recd )


    def doVideoMode( self ):
        if (self.MODE == Constants.MODE_VIDEO):
            return

        self.MODE = Constants.MODE_VIDEO
        self.setUpdating(True)
        gobject.idle_add( self.setupMode, self.MODE, True )


    def doPhotoMode( self ):
        if (self.MODE == Constants.MODE_PHOTO):
            return

        self.MODE = Constants.MODE_PHOTO
        self.setUpdating(True)
        gobject.idle_add( self.setupMode, self.MODE, True )


    def doAudioMode( self ):
        if (self.MODE == Constants.MODE_AUDIO):
            return

        self.MODE = Constants.MODE_AUDIO
        self.setUpdating(True)
        gobject.idle_add( self.setupMode, self.MODE, True )
