from __future__ import absolute_import
__author__ = 'mathijs'

from .hbobject import HbObject
from .hbsettings import Settings, NotValidError
from .celery_expert import get_status_async, is_async

import json
import logging

# from celery import Task
from celery import Celery, chain, group
from celery.local import Proxy as CeleryTaskProxy
from celery import shared_task
# import celery.send_task
import celery.result 
from celery import current_app

import requests
# from .load_task_from_db import load_task_from_db
# from celery.contrib.methods import task_method
# import traceback
# traceback.print_stack()
# app = Celery('processing',backend='amqp')

base_url = 'http://127.0.0.1:8000/api'


try:
    import tasks.models
    from tasks.experts.tools.celery_expert import is_async, get_status_async
    DJANGO=True
    print 'DJANGO FOUND'

except:
    DJANGO=False
    print 'DJANGO NOT FOUND'

@shared_task(name='SaveContent', ignore_result=True)
def save_hbobject_content(content,hash):
    print 'SAVE {} in {}'.format(content,hash)

    if isinstance(content,dict) and content.has_key('info') and content.has_key('content'):
        info = content['info']
        content = content['content']
    else:
        info=None

    obj = HbObject(hash=hash)
    obj.log.info('Replace with finished content')

    if info is not None:
        obj.info = info

    obj.content = content
    obj.save()
    # print 'Saved {}'.format(hash)
    if DJANGO:
        # TODO: remotes need access to the database: through api?
        check_stored_status(obj)

def check_stored_status_hash(hash):
    if (type(hash) in [str,unicode] and hash.find(':')>0):
        obj = HbObject(hash)
    try:
        obj = HbObject(hash=hash)
    except:
        raise Exception('Not found {}'.format(hash))
    check_stored_status(obj)

def check_status(obj): 
    """ independent of django connection, check status on filesystem
    """
    if obj.content == []:
        return 0,'Empty' # tasks.models.HBTask.NO_STATUS

    elif is_async ( obj.content ):
        status_checked = get_status_async(obj.content)
        return {'FAILURE':  -1, # tasks.models.HBTask.ERROR_STATUS,
                 'SUCCESS':   2, # tasks.models.HBTask.OK_STATUS,
                 'PROGRESS':  1, # tasks.models.HBTask.PENDING_STATUS,
                 'PENDING':   1, # tasks.models.HBTask.PENDING_STATUS
                }.get(status_checked.get('status')),status_checked.get('msg')
    else:
        return 2,'Found content' # tasks.models.HBTask.OK_STATUS


def check_stored_status(obj):
    status_checked = None
    newstatus = check_status(obj)[0]
    # print 'CHECK_STORED_STATUS : ',obj.hash,newstatus

    if DJANGO==False: # now to always try the remote
        stored = tasks.models.HBTask.objects.get(resulthash=obj.hash)
        if not newstatus == stored.status:
            stored.status = newstatus
            stored.save()

        return newstatus,status_checked
    
    else:
        if (newstatus==2):
            url = base_url + '/finished/{}'.format(obj.hash)
            
            try:
                print 'Object info is now:'
                print obj.info
                info = obj.info.get('short',False)
                r = requests.get(url,params={'short_info':info})
            except:
                r = requests.get(url)    

            # print r.ok,r.status_code
        return newstatus,status_checked


class HbTask():
    """
    A task that takes zero or more HbObjects, 
    does something, and returns another HbObject.
    """
    def __init__(self,log=None,fromdb=False,**kwargs):

        self.name = ''
        self.version = ''
        self.settings = Settings()
        self.result = None

        self.retry_wait = 10 # 10 sec default, wait for dependencies

        # overload this define 
        self.define()

        # user short name if there is no long one
        self.longname = getattr(self,'longname',self.name)
        self.resulttype = self.result.type
        
        # celery task
        self.name = getattr(self,'name',self.longname.title().replace(' ',''))
        self.log = logging.getLogger(self.name)

        # Set settings
        try:
            self.settings.set(**kwargs)
        except NotValidError as e:
            self.log.error('Settings not valid: {}'.format(e.msg))
            
        if self.settings.valid:
            self.set_result()


    def runme(self):
        """ gives the thing that can be run: a signature
        """
        # What to do?
        if isinstance(self.runtask,celery.local.Proxy):
            runtask = self.runtask.si
        elif callable(self.runtask):
            runtask = self.runtask()
        else:
            self.log.error('The runtask should be a shared_task or something running quickly that returns an AsyncResult')
        # Settings
        settings = self.settings.getstr
        for i in self.dependencies:
            settings[i] = HbObject(self.settings.get[i]).content
        # Go
        return runtask(**settings)

    def define(self):
        """ TO BE OVERLOADED
        """
        pass

    def execute(self):
        """ TO BE OVERLOADED
        """
        pass

    def set_result(self):
        """ Based on the settings, compute the result hash and save an empty result file.
        """
        if self.settings.valid:
            # Create result log/hash
            if isinstance(self.result,HbObject):
                result = self.result
            else:
                result = HbObject(self.result)

            result.log.head(self.longname+' v. '+self.version)
            for k,v in self.settings.getstr.iteritems():
                result.log.head('  {} : {}'.format(k,v))
            
            # If new, save empty
            if not result.known:
                self.log.info('Save an empty result: {}'.format(result))
                
                # print 'INFO ',self.result.info
                # print 'CONT ',self.result.content

                result.save()
            
            # Use shorthand version
            self.result = str(result)

            if DJANGO:
                self.log.info('SAVE TO DJANGO {}'.format(DJANGO))
                self.save_task()

            return True
        else:
            self.log.error('Try to set without proper settings')
            return False

    @property
    def dependencies(self):
        return self.settings.dependencies

    @property
    def dependency_dict(self):
        return self.settings.dependency_dict

    # @property
    # def dependency_dictstr(self):
    #     return self.settings.dependency_dictstr

    @property
    def ready_to_go(self):
        """ Tell me if the task is ready to go: all dependencies are met
        """
        for d in self.dependency_dict.values():
            if (not HbObject(d).available) or is_async(HbObject(d).content):
                return False
        return True

    @property
    def dependencies_todo(self):
        """ a list of dependencies not started yet 
        """
        return [d for d in self.dependency_dict.values() if not HbObject(d).available]

    @property
    def dependencies_pending(self):
        """ a list of dependencies I'm still waiting for
        """
        # return [r for r in self.dependencies if is_async(r)]
        return [d for d in self.dependency_dict.values() if is_async(HbObject(d).content)]
          

    def dependency_status(self):
        out = {}
        for par,d in self.dependency_dict.iteritems():

            h = HbObject(d)

            if h.known:
                out[par] = h.status
            else:
                # print 'Object {} was not available!'.format(str(h))
                out[par] = ('ERROR_STATUS','Not available')
        return out

    def submit(self,redo=False):
        """ Really do something here: wrap the specific 'execute'
        """

        if not self.settings.valid:
            self.log.error('Settings not valid')
            return

        # try if we already have this item
        # The task has already been submitted and saved 
        resulthash = self.result.split(':')[1]

        result = HbObject(hash=resulthash)

        if result.content and not redo:
            self.log.info('Result already found ({})'.format(resulthash))
            self.log.info('Progress : {}'.format(get_status_async(result.content)))

        # Otherwise, submit it now
        else:
            self.log.info('Start {} for {}'.format(self.name, resulthash))
            if DJANGO:
                self.save_task()

            # All the arguments to the task: dependencies loaded, and settings            
            # self.log.info('_do_runtask is now : {}'.format(self._do_runtask))
            # Do the work and save it

            # if not self.ready_to_go:
            #     raise Exception('Not all dependencies ready')

            signature = chain(
                    # check_and_wait.s(self.dependency_dict),
                    self.runme(),
                    save_hbobject_content.s(resulthash)
                )

            result.content = signature.delay()

            if DJANGO:
                self.save_run(result.content.task_id)

            self.log.info('Submitted {}'.format(resulthash))
            # for the time being, we store the complete chain as the result
            result.save(status=1)

        
    if DJANGO:

        def save_task(self):
            stored_task, isnew = tasks.models.HBTask.objects.get_or_create(
                resulthash = self.result.split(':')[1]
            )
            if isnew:
                self.log.info('SAVE TASK TO DATABASE')
                stored_task.hb_taskname = self.name
                stored_task.celery_taskname = getattr(self.runtask,'name','')
                stored_task.parameters = json.dumps(self.settings.get)
                stored_task.status = tasks.models.HBTask.NO_STATUS
                stored_task.resulttype = self.resulttype
                stored_task.save()
                self.log.info('SAVED WITH ID {}'.format(stored_task.id))

        # def load_task(self,hash):
        #    self.name = stored_task.hb_taskname

        def save_run(self,task_id):
            try:
                stored_task = tasks.models.HBTask.objects.get(resulthash= self.result.split(':')[1])
            except :
                raise Exception('Not found stored task with result='.format( self.result ))


            stored_run = tasks.models.HBTaskRun.objects.create(
                task = stored_task,
                celery_id = task_id,
            )

            if stored_task.status == tasks.models.HBTask.NO_STATUS:
                stored_task.status = tasks.models.HBTask.PENDING_STATUS
                stored_task.save()


    @property
    def description(self):
        # convert from typehash to items
        tp,h =self.result.split(':') 
        return ({'name':self.name,
                 'version':self.version,
                 'settings':self.settings.getstr,
                 'dependencies':self.settings.dependency_dict,
                 'result': {'type':tp, 'hash':h}})


    @property
    def api(self):
       # print '\nTASK NAME IS ', self.name        
       return {'name':self.name,
                 'longname':self.longname,
                 'version':self.version,
                 'settings':self.settings.api,
                 'dependencies':self.settings.dependencies,
                 'result': self.result.type}

    @property
    def json(self):
        return json.dumps(self.description)


# #    @celery.task
#     def execute(self):
#         """
#         Perform the action through celery queue handlers
#         Workers are activated using:    celery -A tasks worker --loglevel=info

#         During the execution, we produce the content carried in
#         self.result.content

#         """
#         print '----------------------------------------'
#         print 'Now executing the HbTask'
#         print '----------------------------------------'
#         print 'name     -> ', self.name
#         print 'settings -> ', self.settings.get
#         print 'result   -> ', self.result
#         print '----------------------------------------'
#         self.result.content = [1,2,3]
#         self.result.save()


# if True:
#    p0 = Polygon()
#    p0.log.head('Niks mee gedaan')
#    print 'log of p0:'
#    p0.log.show()
#    print '--> ',p0.hash
#    print
#
#    p1 = BufferPolygon(parent=p0, bufferwidth=100)
#
#
#    print 'log of p1:'
#    p1.result.log.show()
#    print '--> ',p1.result.hash
#
