#!/usr/bin/env python
# -*- coding: utf-8 -*-

##############################################################################
#  Copyright Kitware Inc.
#
#  Licensed under the Apache License, Version 2.0 ( the "License" );
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
##############################################################################

import os.path

from bson.objectid import ObjectId

from girder import logger
from girder.api import access
from girder.api.describe import autoDescribeRoute, Description
from girder.api.rest import filtermodel, RestException, \
                            boundHandler, getCurrentUser

from girder.constants import AccessType, TokenScope
from girder.plugins.worker import utils as workerUtils
# from girder.utility.model_importer import ModelImporter

from ..constants import JobStatus


def addItemRoutes(item):
    routes = createRoutes(item)
    item.route('GET', (':id', 'video'), routes['getVideoMetadata'])
    item.route('PUT', (':id', 'video'), routes['processVideo'])
    item.route('DELETE', (':id', 'video'), routes['deleteProcessedVideo'])
    item.route('GET', (':id', 'video', 'frame'), routes['getVideoFrame'])

def createRoutes(item):
    @autoDescribeRoute(
        Description('Return video metadata if it exists.')
        .param('id', 'Id of the item.', paramType='path')
        .errorResponse()
        .errorResponse('Read access was denied on the item.', 403)
    )
    @access.public
    @boundHandler(item)
    def getVideoMetadata(self, id, params):
        return {
            'a': 1,
            'b': 2
        }

    @autoDescribeRoute(
        Description('Create a girder-worker job to process the given video.')
        .param('id', 'Id of the item.', paramType='path')
        .param('fileId', 'Id of the file to use as the video.', required=False)
        .param('force', 'Force the creation of a new job.', required=False,
            dataType='boolean', default=False)
        .errorResponse()
        .errorResponse('Read access was denied on the item.', 403)
    )
    @access.public
    @boundHandler(item)
    def processVideo(self, id, params):
        force = params['force']
        user, userToken = getCurrentUser(True)

        itemModel = self.model('item')
        fileModel = self.model('file')
        tokenModel = self.model('token')
        jobModel = self.model('job', 'jobs')

        item = itemModel.load(id, user=user, level=AccessType.READ)

        itemVideoData = item.get('video', {})
        jobId = itemVideoData.get('jobId')

        itemAlreadyProcessed = False
        job = None
        if jobId is not None:
            job = jobModel.load(jobId, level=AccessType.READ, user=user)

        if not force:
            if job is not None:
                status = job['status']
                if status not in (
                        None, JobStatus.ERROR, JobStatus.CANCELED):
                    itemAlreadyProcessed = True

            if itemAlreadyProcessed:
                result = {
                    'video': {
                        'jobCreated': False,
                        'message': 'Processing job already created.'
                    }
                }

                result.update(job)
                return result

        # if user provided fileId, use that one
        fileId = params.get('fileId')
        if fileId is not None:
            # ensure the provided fileId is valid
            inputFile = fileModel.findOne({
                'itemId': ObjectId(id), '_id': ObjectId(fileId)})

            if inputFile is None:
                raise RestException(
                    'Item with id=%s has no such file with id=%s' %
                    (id, fileId))

        else:
            # User did not provide a fileId.
            #
            # If we're *re*running a processing job (force=True), look
            # for the fileId used by the old job.
            if force and job:
                fileId = job.get('meta', {}).get('video', {}).get('fileId')
                if fileId:
                    # ensure the provided fileId is valid, but in this case,
                    # don't raise an exception if it is not -- just discard the
                    # fileId and move on
                    inputFile = fileModel.findOne({
                        'itemId': ObjectId(id), '_id': ObjectId(fileId)})

                    if inputFile is None:
                        fileId = None

        # if we *still* don't have a fileId, just grab the first one found under
        # the given item.
        if fileId is None:
            inputFile = fileModel.findOne({'itemId': ObjectId(id)})

            # if there *are* no files, bail
            if inputFile is None:
                raise RestException('item %s has no files' % itemId)

            fileId = inputFile['_id']

        # if we are *re*running a processing job (force=True), remove all files
        # from this item that were created by the last processing job...
        #
        # ...unless (for some reason) the user is running the job against that
        # particular file (this is almost certainly user error, but for now,
        # we'll just keep the file around).
        if force:
            fileIdList = itemVideoData.get('createdFiles', [])
            for f in fileIdList:
                if f == fileId:
                    continue

                theFile = fileModel.load(
                        f, level=AccessType.WRITE, user=user)

                if theFile:
                    fileModel.remove(theFile)
            itemVideoData['createdFiles'] = []

        # begin construction of the actual job
        if not userToken:
            # It seems like we should be able to use a token without USER_AUTH
            # in its scope, but I'm not sure how.
            userToken = tokenModel.createToken(
                user, days=1, scope=TokenScope.USER_AUTH)

        jobTitle = 'Video Processing'