#
#  Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

"""
Media asset file adapter.

Authors: Johann Schimpf, Erich Seifert.

$Id$
"""

from cStringIO import StringIO
from logging import getLogger
import mimetypes
import os, re, sys

from zope import component
from zope.interface import implements
from cybertools.media.interfaces import IMediaAsset, IFileTransform
from cybertools.media.piltransform import PILTransform
from cybertools.storage.filesystem import FileSystemStorage

TRANSFORM_STATEMENT = re.compile(r"\s*(\+?)([\w]+[\w\d]*)\(([^\)]*)\)\s*")

DEFAULT_FORMATS = {
    "image": "image/jpeg"
}

def parseTransformStatements(txStr):
    """ Parse statements in transform chain strings."""
    statements = TRANSFORM_STATEMENT.findall(txStr)
    return statements

def getMimeBasetype(mimetype):
    return mimetype.split("/",1)[0]

def getMimetypeExt(mimetype):
    exts = mimetypes.guess_all_extensions(mimetype)
    return exts and exts[-1] or ""


class MediaAssetFile(object):
    """ Class for extracting metadata from assets and to create transformed
        variants using file representations in subdirectories.
    """

    implements(IMediaAsset)

    def __init__(self, dataPath, rules, contentType):
        self.dataPath = dataPath
        self.rules = rules
        self.mimeType = contentType

    def getData(self, variant=None):
        if variant is None:
            return self.getOriginalData()
        path = self.getPath(variant)
        if not os.path.exists(path):
            getLogger('cybertools.media.asset.MediaAssetFile').warn(
                'Media asset directory %r not found.' % path)
            self.transform()
            #return self.getOriginalData()
        f = open(path, 'rb')
        data =f.read()
        f.close()
        return data

    def getImageSize(self, variant=None, data=None):
        if data is None:
            data = self.getData(variant)
        pt = PILTransform()
        pt.open(StringIO(data))
        if pt.im is None:
            return (0, 0)
        return pt.im.size

    def getContentType(self, variant=None):
        contentType = self.getMimeType()
        if variant is None:
            return contentType
        outputFormat = None
        # Scan all statements for a defintion of an output format
        optionsstr = self.rules.get(variant)
        if optionsstr:
            statements = parseTransformStatements(optionsstr)
            for prefix, command, args in statements:
                if command == "output":
                    outputFormat = args
                    break
        # Return default type if no defintion was found
        if not outputFormat:
            baseType = getMimeBasetype(contentType)
            return DEFAULT_FORMATS.get(baseType) or contentType
        return outputFormat

    def transform(self, rules=None):
        if rules is None:
            rules = self.rules
        for variant, commands in rules.items():
            self.createVariant(variant, commands)

    def createVariant(self, variant, commands):
        oldassetdir = self.getDataPath()
        # get or create output directory
        path = self.getPath(variant)
        assetdir = os.path.dirname(path)
        if not os.path.exists(assetdir):
            os.makedirs(assetdir)
        excInfo = None  # Save info of exceptions that may occur
        try:
            mediaFile = PILTransform()
            mediaFile.open(oldassetdir)
            statements = parseTransformStatements(commands)
            for prefix, command, args in statements:
                if command == "rotate":
                    rotArgs = args.split(",")
                    angle = float(rotArgs[0])
                    resize = False
                    if len(rotArgs) > 1:
                       resize = bool(int(rotArgs[1]))
                    mediaFile.rotate(angle, resize)
                elif command == "color":
                    mode = args
                    mediaFile.color(mode)
                elif command == "crop":
                    dims = [float(i) for i in args.split(",")]
                    if dims and (2 <= len(dims) <= 4):
                        mediaFile.crop(*dims)
                elif command == "size":
                    size = [int(i) for i in args.split(",")]
                    if size:
                        mediaFile.resize(*size)
            outputFormat = self.getContentType(variant)
            mediaFile.save(path, outputFormat)
        except Exception, e:
            excInfo = sys.exc_info()
        # Handle exceptions that have occured during the transformation
        # in order to provide information on the affected asset
        if excInfo:
            eType, eValue, eTraceback = excInfo  # Extract exception information
            raise eType("Error transforming asset '%s': %s" %
                        (oldassetdir, eValue)), None, eTraceback

    def getPath(self, variant):
        pathOrig = self.getDataPath()
        dirOrig, fileOrig = os.path.split(pathOrig)
        pathTx = os.path.join(dirOrig, variant, self.getName())
        outputFormat = self.getContentType(variant)
        #outputExt = getMimetypeExt(outputFormat)
        basePath = os.path.splitext(pathTx)[0]
        for ext in mimetypes.guess_all_extensions(outputFormat):
            pathTx = basePath + ext
            if os.path.exists(pathTx):
                return pathTx
        return pathTx

    def getMimeType(self):
        return self.mimeType

    def getName(self):
        return os.path.split(self.getDataPath())[1]

    def getDataPath(self):
        return self.dataPath

    def getOriginalData(self):
        #f = self.getDataPath().open()
        f = open(self.getDataPath(), 'rb')
        data = f.read()
        f.close()
        return data
