#!/usr/bin/env python

import logging
from multiprocessing.dummy import Pool
import os
import time
import sys

import begin
import coloredlogs
from tqdm import tqdm

from client import QueryClient, ModifyClient
from ltu.engine.result import Result
from ltu.engine.stat import Stat

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# TODO: Stat class is not thread safe, we should update it.
global stat
stat = Stat()


def print_stat(nb_threads):
    """ print all the statistics global and per action """
    stat.print_stat_global()
    logger.info("")
    stat.print_result_per_action(nb_threads)


def get_action_name_from_function(function):
    """return from a function (add_image, search_image, delete_image) the name of the action concerned
    """
    return function.__name__.split('_')[0]


def run_single_task(item):
    """Run given action for one file
    """
    in_file = item[0]["in"]
    action_function = item[1]
    action = get_action_name_from_function(item[1])
    out_file = ""

    if action in item[0]:
        out_file = item[0][action]
    if out_file:
        # launch action
        try:
            result = action_function(in_file)
            logger.debug("Finish with status %s" %(result.status_code))
            if result.status_code < 0:
                logger.debug('An issue occuted with the file {}. Consult the json result. file'.format(in_file))
                stat.nb_errors[action] += 1
            else:
                stat.treated[action] += 1

        except Exception as e:
            logger.critical('An issue has occured. Could not perform the action {}. The process is stopped: {}'.format(action,e))
            sys.exit(-1)

        # save the result in a json file
        try:
            result.save_json(out_file)
        except Exception as e:
            logger.critical('Could not save the result for the action {}: {}'.format(action,e))
            sys.exit(-1)


def run_task_mono_thread(action_function, files, action_label, nb_threads=1, offset=0):
    """Run given action on every files, one at a time.
    """
    for file in files[offset:]:
        item = (file, action_function)
        logger.info("")
        logger.info("%s: %s" % (action_label, file["in"]))
        run_single_task(item)


def run_task_multi_thread(action_function, files, action_label, nb_threads=2, offset=0):
    """Run given action on every files using a threading pool.
       It uses a progress bar instead of a usual verbose log.
    """
    pool = Pool(processes=nb_threads)
    items = [(file, action_function) for file in files[offset:]]
    pool_iterable = pool.imap_unordered(run_single_task, items)
    progress_bar_items = tqdm(total=len(items),
                              iterable=pool_iterable,
                              unit='images',
                              desc='{0: <30}'.format(action_label))
    for item in progress_bar_items:
        pass


def generate_actions_list_per_images(actions_list, input_dir, force):
    """Generate a list of actions to process per image. For each image are saved:
        - input path of the image
        - out path where save the result for each action to perform
    """
    # create results paths if they don't exist
    # result main repertory: out_result
    out_base_path = os.path.join(os.getcwd(), "out_result")
    if not os.path.exists(out_base_path):
        try:
            os.mkdir(out_base_path)
        except Exception as e:
            logger.critical('Could not create the out path "out_result": {}'.format(e))
            sys.exit(-1)

    # create one result folder per action
    for action in actions_list:
        action_path = os.path.join(out_base_path, action)
        if not os.path.exists(action_path):
            try:
                os.mkdir(action_path)

            except Exception as e:
                logger.critical('Could not create the action {} out path: {}'.format(e,action))
                sys.exit(-1)

    files = []
    b_file = False # indicate if there are files to performed
    untreated = 0

    image_path = os.path.basename(input_dir)

    for dirpath, _, fnames in os.walk(input_dir):
        # relative path from the input images folder, repertory per repertory
        relativ_path = os.path.relpath(dirpath,input_dir)

        if relativ_path == ".":
            relativ_path = ""

        # create actions repertories
        for action in actions_list:
            complete_path = os.path.join(out_base_path, action, image_path)
            if not os.path.exists(complete_path):
                try:
                    os.mkdir(complete_path)
                except Exception as e:
                    logger.critical('Could not create the out path {}: {}'.format(complete_path, e))
                    sys.exit(-1)