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

"""GUEST plugin tasks related to Packets handling."""

import sys
import uuid
from datetime import datetime

try:
    from poppy.core.logger import logger
    from poppy.core.db.connector import Connector
    from poppy.core.db.handlers import get_or_create_with_info
    from poppy.core.task import Task
    from poppy.core.target import FileTarget, PyObjectTarget
    from poppy.core.db.dry_runner import DryRunner
except:
    sys.exit("POPPy framework seems to not be installed properly!")

from edds_process.response.make import make_tmraw_xml, make_tcreport_xml

from roc.rpl.packet_parser import RawData

from roc.guest.exceptions import GuestNoInputError
from roc.guest.guest import Test
from roc.guest.constants import INVALID_UTC_DATETIME, TCREPORT_STRTFORMAT

__all__ = ['raw_to_test', 'raw_to_tmraw', 'raw_to_tcreport']

@DryRunner.dry_run
@PyObjectTarget.input(identifier='raw_data')
@PyObjectTarget.output(identifier='test')
@Task.as_task(plugin_name='roc.guest', name='raw_to_test')
def raw_to_test(task):
    """
    Generate a Test instance from an input RawData object.

    :param task:
    :return:
    """

    # Get RawData instance
    try:
        raw_data = task.inputs['raw_data'].value
    except:
        raise GuestNoInputError("No input RawData object defined, aborting!")

    # generate test uuid
    test_uuid = str(uuid.uuid4())

    # if test_name provided, then get it
    if "out_test_name" in task.pipeline.args:
        test_name = task.pipeline.args.out_test_name
    else:
        # Else use the test_uuid as default test_name
        test_name = test_uuid

    # if test_description provided, then get it
    if "out_test_descr" in task.pipeline.args:
        test_description = task.pipeline.args.out_test_descr
    else:
        # Else use a default test description
        test_description = f"Test generated by roc.guest on {datetime.today()}"

    # Get packet_utc_time from raw_data
    packet_utc_time = raw_data.packet_utc_time

    # Define launched_date
    if ('start_time' in task.pipeline.args and
            task.pipeline.args.start_time):
        launched_date = task.pipeline.args.start_time
    else:
        launched_date = min(packet_utc_time)

    # defined terminated_date
    if ('end_time' in task.pipeline.args and
            task.pipeline.args.end_time):
        terminated_date = task.pipeline.args.end_time
    else:
        terminated_date = max(packet_utc_time)

    # No test log file name defined...
    file_path = ""

    test_log = Test(
        test_name,
        launched_date,
        test_uuid=test_uuid,
        creation_date=launched_date, # we assume that creation_date = launched_date
        description=test_description,
        terminated_date=terminated_date,
        status='Terminated',
        file_path=file_path,
    )
    test_log.invalid_packet_list = raw_data.invalid_packet_list
    test_log.rpl = raw_data.rpl

    if raw_data.rpl.idb_source == "PALISADE":
        palisade_version = raw_data.rpl.idb_version
    else:
        palisade_version = None

    test_log.events = Test.packets_to_events(raw_data.packet_list,
                               palisade_version=palisade_version)

    # And insert it into the pipeline
    task.outputs['test'] = test_log

@PyObjectTarget.input(identifier='raw_data')
@FileTarget.output(identifier='dds_tmraw_xml')
@Task.as_task(plugin_name='roc.guest', name='raw_to_tmraw')
def raw_to_tmraw(task):
    """
    Save input RPW TM binary packet into a MOC DDS response TmRaw XML file

    :param task:
    :return:
    """
    # get the raw_data object from pipeline properties
    try:
        raw_data = task.inputs['raw_data'].value
    except:
        logger.warning("No input RawData object available, aborting!")
        return False

    # If failed_files list not defined in the pipeline properties, initialize it
    failed_files = task.pipeline.get('failed_files', default=[], create=True)

    # If processed_files list not defined in the pipeline properties, initialize it
    processed_files = task.pipeline.get('processed_files', default=[], create=True)

    # Get overwrite argument or define it
    overwrite = task.pipeline.get('overwrite', default=False, args=True)

    # Get data version (if any)
    data_version = task.pipeline.get('data_version', default=[None])[0]

    # Get output filepath or define it
    output_filepath = task.pipeline.get('output_tmraw_xml', default=None, args=True)
    if not output_filepath:
        if isinstance(raw_data, Test):
            output_filepath = raw_data.file_name_format(
                task.pipeline,
                "solo_LZ_rpw-dds-tmraw",
                overwrite=overwrite,
                data_version=data_version) + '.xml'
        else:
            output_filepath = "solo_LZ_rpw-dds-tmraw_" + str(uuid.uuid4())[:7] + '_V01.xml'

    # Add dummy scos header (as hexa string)
    if ('scos_header_size' in task.pipeline.args and
        task.pipeline.args.scos_header_size):
        scos_header = bytearray(task.pipeline.args.scos_header_size).hex()
    else:
        scos_header = None

    # Get TM packet only
    tm_packets = RawData.filter(raw_data.packet_list, by_type='TM')

    # Get binary
    binary_list = []
    for current_packet in tm_packets:
        current_binary = current_packet['binary']
        # If requested, add the scos header hexa before the packet binary
        if scos_header:
            current_binary = scos_header + current_binary

        binary_list.append(current_binary)

    logger.info(f'Writing {output_filepath}...')
    if make_tmraw_xml(binary_list, output_filepath,
                    overwrite=overwrite):
        logger.info("{0} saved".format(output_filepath))
        processed_files.append(output_filepath)
    else:
        logger.error(f'Saving {output_filepath} has failed')
        failed_files.append(output_filepath)

    task.outputs['dds_tmraw_xml'].filepath = output_filepath


@PyObjectTarget.input(identifier='raw_data')
@FileTarget.output(identifier='dds_tcreport_xml')
@Task.as_task(plugin_name='roc.guest', name='raw_to_tcreport')
def raw_to_tcreport(task):
    """
    Save input binary packet into a MOC DDS response TmRaw XML file

    :param task:
    :return:
    """
    # get the raw_data object from pipeline properties
    try:
       raw_data = task.inputs['raw_data'].value
    except:
       logger.warning("No input RawData object available, aborting!")
       return None

    # Get data version
    data_version = task.pipeline.get('data_version',
                                     default=[None])[0]

    # Get overwrite argument or define it
    overwrite = task.pipeline.get('overwrite', default=False, args=True)

    # If failed_files list not defined in the pipeline properties, initialize it
    failed_files = task.pipeline.get('failed_files', default=[], create=True)

    # If processed_files list not defined in the pipeline properties, initialize it
    processed_files = task.pipeline.get('processed_files', default=[], create=True)

    # Get output filepath or define it
    output_filepath = task.pipeline.get('output_tcreport_xml', default=None, args=True)
    if not output_filepath:
        if isinstance(raw_data, Test):
            output_filepath = raw_data.file_name_format(
                task.pipeline,
                "solo_LZ_rpw-dds-tcreport",
                data_version=data_version,
                overwrite=overwrite) + '.xml'
        else:
            output_filepath = "solo_LZ_rpw-dds-tcreport_" + str(uuid.uuid4())[:7] + '_V01.xml'

    # Get TM packets only
    tc_packets = RawData.filter(raw_data.packet_list, by_type='TC').copy()

    # Prepare output value as expected in tcreport
    for pkt in tc_packets:
        if pkt['utc_time'] != INVALID_UTC_DATETIME:
            pkt['utc_time'] = pkt['utc_time'].strftime(TCREPORT_STRTFORMAT)

    # Write Tc report
    logger.info(f'Writing {output_filepath}...')
    if make_tcreport_xml(tc_packets,
                         output_filepath,
                         overwrite=overwrite):
        logger.info("{0} saved".format(output_filepath))
        processed_files.append(output_filepath)
    else:
        logger.error(f'Saving {output_filepath} has failed')
        failed_files.append(output_filepath)

    task.outputs['dds_tcreport_xml'].filepath = output_filepath
