# ------------------------------------------------------------------------------
import os.path, time
from appy.fields.file import FileInfo
from appy.shared import utils as sutils

# ------------------------------------------------------------------------------
class Migrator:
    '''This class is responsible for performing migrations, when, on
       installation, we've detected a new Appy version.'''
    def __init__(self, installer):
        self.installer = installer
        self.logger = installer.logger
        self.app = installer.app
        self.tool = self.app.config.appy()

    @staticmethod
    def migrateBinaryFields(obj):
        '''Ensures all file and frozen pod fields on p_obj are FileInfo
           instances.'''
        migrated = 0 # Count the number of migrated fields
        for field in obj.fields:
            if field.type == 'File':
                oldValue = getattr(obj, field.name)
                if oldValue and not isinstance(oldValue, FileInfo):
                    # A legacy File object. Convert it to a FileInfo instance
                    # and extract the binary to the filesystem.
                    setattr(obj, field.name, oldValue)
                    migrated += 1
            elif field.type == 'Pod':
                frozen = getattr(obj.o.aq_base, field.name, None)
                if frozen:
                    # Dump this file on disk
                    tempFolder = sutils.getOsTempFolder()
                    fmt = os.path.splitext(frozen.filename)[1][1:]
                    fileName = os.path.join(tempFolder,
                                            '%f.%s' % (time.time(), fmt))
                    f = file(fileName, 'wb')
                    if frozen.data.__class__.__name__ == 'Pdata':
                        # The file content is splitted in several chunks
                        f.write(frozen.data.data)
                        nextPart = frozen.data.next
                        while nextPart:
                            f.write(nextPart.data)
                            nextPart = nextPart.next
                    else:
                        # Only one chunk
                        f.write(frozen.data)
                    f.close()
                    f = file(fileName)
                    field.freeze(obj, template=field.template[0], format=fmt,
                                 noSecurity=True, upload=f,
                                 freezeOdtOnError=False)
                    f.close()
                    # Remove the legacy in-zodb file object
                    setattr(obj.o, field.name, None)
                    migrated += 1
        return migrated

    def migrateTo_0_9_0(self):
        '''Migrates this DB to Appy 0.9.x'''
        # Put all binaries to the filesystem
        tool = self.tool
        tool.log('Migrating binary fields...')
        context = {'migrate': self.migrateBinaryFields, 'nb': 0}
        for className in tool.o.getAllClassNames():
            tool.compute(className, context=context, noSecurity=True,
                         expression="ctx['nb'] += ctx['migrate'](obj)")
        tool.log('Migrated %d binary field(s).' % context['nb'])

    def run(self, force=False):
        '''Executes a migration when relevant, or do it for sure if p_force is
           True.'''
        appyVersion = self.tool.appyVersion
        # appyVersion being None simply means that we are creating a new DB
        if force or (appyVersion and (appyVersion < '0.9.0')):
            # Migration is required
            self.logger.info('Appy version (DB) is %s' % appyVersion)
            startTime = time.time()
            self.migrateTo_0_9_0()
            stopTime = time.time()
            elapsed = (stopTime-startTime) / 60.0
            self.logger.info('Migration done in %d minute(s).' % elapsed)
# ------------------------------------------------------------------------------
