#!/usr/bin/python3
"""
@file
@ingroup lisaGui
@copyright ESO - European Southern Observatory
@author Jan Bolcek <Jan.Bolcek@eso.org>

@brief lisaGui `main` source file
The lisaGui is a GUI to operate the LISA camera, it is based on the DCF GUI. 

"""
import sys
import click
import logging
import taurus
import yaml
from datetime import timedelta
from typing import Optional

import re

from taurus.core.util.log import Logger
from taurus.qt.qtgui.application import TaurusApplication

from ifw.wdglib.log.wdglib_logger import WdglibLogger
from ifw.core.utils.utils import find_file
import ifw.core.stooUtils.consul as consul_utils

from lisa.gui.lisaGui.lisaguimainwindow import LisaGuiMainWindow

import elt.pymal as mal
from ModDcsif.Dcsif.DcsCmds import DcsCmdsSync

@click.command('lisaGui')
@click.option('-l', '--log-level', default='ERROR', help='Debugging Level',
               type=click.Choice(["TRACE", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], 
                                 case_sensitive=False))
@click.option('-u', '--uri', default=None, help='Service URI, e.g. "zpb.rr://127.0.0.1:12081/"')
@click.option('-n', '--name', default=None, help='Registered name in Consul, e.g. lisa-req')
@click.pass_context
def main(ctx, log_level, uri, name):
    """
    @brief Entry point of main

    @ingroup lisaGui
    """

    app = TaurusApplication(
        app_name="lisaGui",
        app_version="0.2.0",
        org_name="ESO",
        org_domain="eso.org",
        cmd_line_parser=None,
    )

    logger = Logger(app.applicationName())
    logger.setLogLevel(getattr(taurus, log_level.title()))
    logger.info(f"Application LogLevel=|{log_level}|")

    if name is None and uri is None:
        logger.error(
            "LISA GUI needs a consul request service reference (command line option --name) or a Req/Rep URI endpoint (command line option --uri) to start"
        )
        print(
            "LISA GUI needs a consul request service reference (command line option --name) or a Req/Rep URI endpoint (command line option --uri) to start"
        )
        sys.exit()

    uri = get_uri(ctx, logger=logger)
    
    if uri is None:
        sys.exit()
    
    logger.info(f'URI - {uri}')

    config_file = get_config(uri)

    if config_file is None:
        logger.error(
            "Unable to retrive config file from NGC"
        )
        print(
            "Unable to retrive config file from NGC"
        )
        sys.exit()


    '''--- DCF GUI related config ---'''
    if not is_config_in_file('cfg.oldb.uri_prefix', config_file, "ERROR: The oldb_prefix defined by cfg.oldb.uri_prefix parameter is missing in the configuration file", logger):
        sys.exit()

    if 'cfg.procname' not in config_file:
        logger.info("cfg.procname not in config, set procname to default - dcf")
        procname = 'dcf'
    else:
        procname = config_file['cfg.procname']
        logger.info(f"Set procname to {procname}")
        
    dcs_db_prefix = f"{config_file['cfg.oldb.uri_prefix']}/{procname}"
    
    '''--- LISA related config ---'''
    if not is_config_in_file('cfg.gui.logfile', config_file, "ERROR: Logging config file, defined by cfg.gui.logfile parameter for a Lisa GUI is missing", logger):
        sys.exit()

    logger_config_file_path = find_file(config_file['cfg.gui.logfile'])
    if logger_config_file_path is None:
        logger.error("Log file not found")
        sys.exit()

    if not is_config_in_file('cfg.gui.opc_server', config_file, "ERROR: The OPC server adress defined by cfg.gui.opc_server parameter is missing in the configuration file", logger):
        sys.exit()

    if not is_config_in_file('cfg.gui.iodev_suffix', config_file, "ERROR: The IODEV device name defined by cfg.gui.iodev_suffix parameter is missing in the configuration file", logger):
        sys.exit()

    if not is_config_in_file('cfg.gui.fcs_db_prefix', config_file, "ERROR: The fcs_db_prefix defined by cfg.gui.fcs_db_prefix parameter missing in the configuration file", logger):
        sys.exit()


    WdglibLogger.init(logger_config_file_path)
    WdglibLogger.set_application_name('Lisa')
    
    window = LisaGuiMainWindow(ctx, 
                               base_uri=uri, 
                               opc_server=config_file['cfg.gui.opc_server'], 
                               dcs_db_prefix=dcs_db_prefix,
                               fcs_db_prefix=config_file['cfg.gui.fcs_db_prefix'],
                               iodev_suffix=config_file['cfg.gui.iodev_suffix'],
                               )
    
    window.setWindowTitle('Lisa GUI')
    window.show()
    sys.exit(app.exec_())

def get_uri(cmd_line_ctx, logger: Logger = logging.getLogger('Null')) -> Optional[str]:
    """
    Cleans uri, if uri is not available - gets server uri using service name from consul.

    """

    name = cmd_line_ctx.params.get("name")
    uri = cmd_line_ctx.params.get("uri")

    if (uri == None):
        try:
            cons = consul_utils.ConsulClient()
            uri = cons.get_uri(name)
        except Exception as e:
            logger.error(f"Unable to retrieve uri via Consul")
            logger.error(f"{e}")
            uri = None

    else:
        try:
            """ removes any quotes an user may add """
            uri = uri.strip(' " " ')
            uri = uri.strip(' \' \' ')

            """ remove / from the uri because it creates problem with MAL """
            if uri.endswith('/'):
                uri = uri[:-1]
        except Exception as e:
            logger.error(f"Unable to clean provided uri")
            logger.error(f"{e}")
            uri = None
    
    return uri


def clean_uri(uri):
    """ removes any quotes an user may add """
    uri = uri.strip(' " " ')
    uri = uri.strip(' \' \' ')
    """ remove / from the uri because it creates problem with MAL """
    if uri.endswith('/'):
        uri = uri[:-1]
    return uri

def is_config_in_file(config_key: str, config_file: dict, error_message: str, logger: Logger):
    """
    Check if config if key is in config file
    
    """
    if config_key in config_file:
        logger.info(f'key {config_key} found in config')
    else:
        logger.error(error_message)
    return config_key in config_file

def parse_string_config(json_yaml_config: str, content_keyw: str = "cfg.filename.content") -> dict:
    """
    Parse the json_yaml string got from ngc and deturn it as a dict
    
    """
    
    match = re.search(rf'{{"{content_keyw}": "(.*)"}}\s*$', json_yaml_config, re.DOTALL)
    yaml_content = match.group(1)
    config_dict = yaml.safe_load(yaml_content)

    return config_dict['app']

def get_config(base_uri, 
               config_key: str = "cfg.filename.content", 
               logger: Logger = logging.getLogger('Null'),
               parser: callable = parse_string_config,
               ) -> Optional[str]:
    """
    Loads the config file that was used to start the ngc2 server at the start of the GUI with the connection to MAL interface 
    then it is discarded.
    
    """        
    ciiFactory = mal.CiiFactory.getInstance()
    wait_time = timedelta(seconds=10)
    qos = qos=mal.rr.qos.ReplyTime(wait_time)

    mal_properties = {}
    zpbmal = mal.loadMal('zpb', mal_properties) 

    ciiFactory.registerMal('zpb.rr', zpbmal)

    logger.info(f"{base_uri}/DcsCmds")
    with ciiFactory.getClient(f"{base_uri}/DcsCmds", DcsCmdsSync, qos, {}) as client:
        try:
            reply_json = client.GetConfig(config_key)
            parsed_config = parser(reply_json)
            return parsed_config
            
        except Exception as e:
            logger.error(f"Unable to get client {base_uri}/DcsCmds - Reason: {e}")
            return None
    logger.error(f"Unable to get client {base_uri}/DcsCmds")
    return None

if __name__ == "__main__":
    main()
