#!/usr/bin/env python
import os

from torii import Torii
from torii.data import Task

ICON = ''

def picom_body(ex):
    """
    The function defining the content of the Picom
    :param ex: the picom executor
    :return:
    """
    import random
    import time

    #delay = ex.picom_task.get_input('delay')
    delay = 5
    nb_threads = 20
    obsolescence_delay = 15
    max_sleep = 0
    min_sleep = 0

    def spawn_new_thread(master):
        ex.context.num_thread += 1
        sleep = random.randint(min_sleep, max_sleep)
        prev = None

        # get the slave templates sorted
        slaves = [template for template in ex.templates if template.name != 'master']
        print('spawn a new thread of {0} slaves'.format(len(slaves)))

        for template in sorted(slaves, key=lambda x:x.description):
            task = ex.tasks.new(template=template,
                                name='chunk {0} - {1}'.format(ex.context.num_thread, template.name),
                                submit=True)
            task.set_input('sleep', sleep)
            task.context.num_thread = ex.context.num_thread
            if prev is None:
                task.add_dependency(master, criterion='IN_PARALLEL')
            else:
                task.add_dependency(prev)
            prev = task

    def init_master():
        master = ex.tasks.new(template='master', name='master', submit=True)
        master.context = ex.context
        master.set_input('sleep', delay * ex.context.max_step)
        ex.context.num_thread = 0
        ex.context.master = master.ref
        return master

    def check_last_tasks():
        master = None
        active_threads = []
        all_threads = []
        for task in ex.tasks.last_tasks:
            print('check {0} ({3}) in thread {1} (status = {2})'
                  .format(task, task.context.num_thread, task.status, task.name))

            if task.name == 'master':
                master = task
            else:
                if task.context.num_thread not in all_threads:
                    all_threads.append(task.context.num_thread)

                if task.is_active() and task.context.num_thread not in active_threads:
                    active_threads.append(task.context.num_thread)

        print('Master is {0}'.format(master))
        print('Threre are {0} existing threads and {1} active ones'
              .format(len(all_threads), len(active_threads)))

        return master, active_threads, all_threads


    print('Step {0}'.format(ex.picom_task.context.step))

    current_time = time.time()
    carry_on_spawning = (ex.context.step < ex.context.max_step - 10)

    if ex.context.step == 0:
        master = init_master()
        ex.set_status(status='RUNNING', message='Master started : {0}'.format(master), delay=delay*2)
        return

    master, active_threads, all_threads = check_last_tasks()

    if master is None or not master.is_active():
        ex.stop('The master not found or inactive')
        return

    if carry_on_spawning:
        # spawn the missing threads
        nb_missing_threads = max(nb_threads - len(active_threads), 0)
        print('{0} threads are missing'.format(nb_missing_threads))
        for i in range(nb_missing_threads):
            spawn_new_thread(master)

        # # give an obsolescence date to the finished threads
        # for num_thread in [n for n in all_threads if n not in active_threads]:
        #     print('Hide the tasks in thread {0}'.format(num_thread))
        #     for task in [t for t in ex.tasks.attic if t.context.num_thread == num_thread]:
        #         print('\ttag {0} as hidden'.format(task))
        #         task.tags.append('process_mapper_98895ca2_hidden')
        #         task.update()

        ex.set_status(status='RUNNING', message='Spawn {0} new threads'.format(nb_missing_threads), delay=delay)
        return


    if not active_threads and not master.is_active():
        ex.success('Everything is done')
    else:
        ex.set_status(status='RUNNING', message='Waiting for the end of all the slaves')

    return




t = Torii(container='new', base_url='https://localhost:6443/torii')
#t = Torii(container='new', base_url='https://localhost:8443/torii', username='hpcgadmin', password='hpcgadmin')
#t = Torii(container='new', base_url='https://ssf.fujitsu.fr:8443/torii', username='hpcgadmin', password='sud0uest2016')

def test_picom_task():

    print('step 0 - open the picom to edit')
    name = 'picom operator'
    version = '0.1'
    picom = t.picoms.get(name=name, version=version, take_first=True)
    if not picom:
        # ... or create it
        picom = t.picoms.new(name=name, version=version)

    print('step 1 - set the script from a function')
    picom.set_script(picom_body)

    print('step 2 - import all the applications')
    application_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'applications', '*')
    applications = t.applications.import_application(path=application_dir, force=True)


    print('step 3 - prepare the task templates for the picom')
    picom.templateTasks = []
    for application in applications:
        picom.templateTasks.append(t.tasks.new(name = application.name.lower(),
                                               description = application.description,
                                               application=application))

    print('step 4 - update the picom')
    # define the icon
    picom.icon = ICON
    # save the picom
    picom.update()

    print('step 5 - create a new picom task and submit')
    picom_task = t.tasks.new(name = 'operator task', description = 'start a sismic threads and operate them', picom=picom)
    setattr(picom_task.context, 'max_step', 1000)

    picom_task.submit()

    print('step 1 - wait for the completion')
    picom_task.wait(status=['PAUSED'] + Task.COMPLETE_STATUS)

    print('Task {0} is {1}'.format(picom_task, picom_task.status))
    assert picom_task.status == 'FINISHED'


if __name__ == '__main__':
    test_picom_task()
