#!/usr/libexec/platform-python

"""
----------------------------------------------------------------------------
Copyright (c) 2024, YumaWorks, Inc., All Rights Reserved.

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
----------------------------------------------------------------------------
"""

import os
import sys
import time
import pycontrol
import pysil

from typing import *

from pysilcommon import types as pysiltypes
from pysilcommon.addons import set_py_sil_modpath, set_log_level, enable_log_colors
from pysilcommon.addons import log as addon_log


STATUS_T = pysiltypes.STATUS_T_


def log(msg: str, log_level="INFO"):
    """Logging function"""
    addon_log(f"PY-SIL-APP: {msg}", log_level)


def get_argv_param(
    argv: list,
    name: str,
    err_empty: bool = False,
    error_not_present: bool = False,
    remove: bool = False,
) -> (int, str):
    """
    search parameters in argv list

    argv - list of cli parameters
    name - name of parameter to search
    err_empty - returns error if parameter exists but empty
    error_not_present - returns error if parameter doesn't exist
    remove - remove from argv

    * RETURNS:
    *   (status code, value)
    """
    full_name = f"--{name}"
    l = len(full_name)
    val = None
    for i, v in enumerate(argv):
        if v.startswith(full_name):
            if l == len(v):
                # found, no value means bool True
                val = True
            elif v[l] == "=":
                # found, value after "="
                val = v[(l + 1) :]
            else:
                # diffenet parameter with the same start letters
                continue
            if remove:
                # remove from argv
                del argv[i]
            break

    if (error_not_present and val is None) or (err_empty and val == ""):
        return (STATUS_T.ERR_NCX_INVALID_VALUE, val)
    return (STATUS_T.NO_ERR, val)


def print_usage():
    """
    * FUNCTION print_usage
    *
    * Print the program usage text
    """
    print("\nUsage:")
    print(
        "\n   py-sil-app [--pymodpath=string] [<logging-parameters>] "
        "[--subsys-id=string] [--library=name] [--retry-limit=num]"
    )
    print(
        "\nPy-sil-app is a sample YumaPro subsystem program to demonstrate how "
        "the PY-SIL and YControl libraries can be used within an application."
        "\nRefer to the online YumaPro Developer Manual for full details on PY-SIL development:"
        "\n\nhttps://docs.yumaworks.com/en/latest/dev/index.html."
    )
    print("\nExample:")
    print("\n   py-sil-app --pymodpath=/home/user/pysil --library=sample")
    exit()


def get_cli_parms(argv: list) -> (pysiltypes.status_t, dict):
    """
    /********************************************************************
    * FUNCTION get_cli_parms
    *
    * Check the CLI parameters for the getconfig commands
    *
    * RETURNS:

    *   status code
    * dict with parameters:
                char **address,
                uint16 *port,
                boolean *if_notif,
                uint16 *retry_limit
    *********************************************************************/
    """
    params = {
        "address": None,
        "port": 0,
        "if_notif": False,
        "retry_limit": 0,
        "log_level": 3,  # info
    }

    ncx_invalid_value_response = (STATUS_T.ERR_NCX_INVALID_VALUE, params)

    ad = "--address="
    po = "--port="
    li = "--library="
    if_notif = "--with-notif"
    rl = "--retry-limit="
    i = 1
    for a in argv:
        if a.startswith(ad):
            params["address"] = a[len(ad) :]
            if not params["address"]:
                return ncx_invalid_value_response

        elif a.startswith(if_notif):
            params["if_notif"] = True
        elif a.startswith(po):
            try:
                params["port"] = int(a[len(po) :])
                if not (0 < params["port"] <= 65535):
                    return ncx_invalid_value_response
            except:
                return ncx_invalid_value_response

        elif a.startswith(li):
            library = a[len(li) :]
            if not library:
                return ncx_invalid_value_response

            res = pysil.sil_sa_add_library_parm(library)
            if res != STATUS_T.NO_ERR:
                return ncx_invalid_value_response
            if "library" not in params:
                params["library"] = []
            params["library"].append(library)

        elif a.startswith(rl):
            try:
                params["retry_limit"] = int(a[len(rl) :])
                if not (0 < params["retry_limit"] <= 65535):
                    return ncx_invalid_value_response
            except:
                return ncx_invalid_value_response

        # else ignore because probably an NCX CLI parm

    return (STATUS_T.NO_ERR, params)


# end get_cli_parms

if __name__ == "__main__":
    argv = sys.argv

    # get log_level param
    res, log_level = get_argv_param(argv, name="log-level")
    # set log level value
    if log_level:
        set_log_level(log_level)

    # Redirect stdout to a file if --log= param provided to app
    res, log_file_name = get_argv_param(argv, name="log", remove=True)
    res, log_append = get_argv_param(argv, name="log-append", remove=True)
    if log_file_name:
        mode = "a" if log_append else "w"
        log_file = open(log_file_name, mode)
        sys.stdout.flush()
        os.dup2(log_file.fileno(), sys.stdout.fileno())
    else:
        # allow colors for console only
        enable_log_colors()

    try:
        # log(f"START PY-SIL")
        ycontrol_done = False

        # set PY_SIL_MODPATH from input parameters
        res, pymodpath = get_argv_param(
            argv, name="pymodpath", err_empty=True, error_not_present=False, remove=True
        )
        if res != STATUS_T.NO_ERR:
            print_usage()

        if pymodpath:
            set_py_sil_modpath(pymodpath)

        """
        need to check for the subsys-id parm before
        the system is initialized
        """
        res, subsys = get_argv_param(argv, name="subsys-id", err_empty=True)
        if res != STATUS_T.NO_ERR:
            print_usage()

        # 1) setup yumapro messaging service profile
        if res == STATUS_T.NO_ERR:
            if subsys is None:
                subsys = "subsys1"
            log(f"subsys={subsys}")
            # cut parameters - remove name of python file
            # argv = sys.argv[1:]
            log(f"Pass parameters to ycontrol_init: {argv}")

            log("pycontrol.ycontrol_init")

            pycontrol.ycontrol_init(len(argv), argv, subsys)
            log(f"ycontrol_init res={res}")
            ycontrol_done = True

        # 2) register services with the control layer
        if res == STATUS_T.NO_ERR:
            log("pysil.sil_sa_register_service")
            res = pysil.sil_sa_register_service()
            log(f"sil_sa_register_service res={res}")

        # get the CLI parameters after the system is initialized
        # so library parameter handled correctly
        if res == STATUS_T.NO_ERR:
            res, params = get_cli_parms(argv)
            if res != STATUS_T.NO_ERR:
                print_usage()

        # set the retry limit if provided
        if res == STATUS_T.NO_ERR and params["retry_limit"] > 0:
            log("pycontrol.ycontrol_set_retry_limit")
            pycontrol.ycontrol_set_retry_limit(params["retry_limit"])

        # 3) do 2nd stage init of the control manager (connect to server)
        if res == STATUS_T.NO_ERR:
            if params["address"]:
                if params["port"] == 0:
                    params["port"] = 2023
                # res = ycontrol_init2_ha("server1", address, port)
                res = pycontrol.ycontrol_init2_ha(
                    "server1", params["address"], params["port"]
                )
            else:
                log("pycontrol.ycontrol_init2")
                res = pycontrol.ycontrol_init2()

        sleep_val = 0.01  # 1/100 sec
        done = False

        # 4) call ycontrol_check_io periodically from the main program control loop
        log("wait cycle")
        while not done and res == STATUS_T.NO_ERR:
            res = pycontrol.ycontrol_check_io()

            if pycontrol.ycontrol_shutdown_now():
                # YControl has received a <shutdown-event>
                # from the server subsystem is no longer active
                # could ignore or shut down YControl IO loop
                log(f"YControl has received a <shutdown-event>")
                done = True

            # Using sleep to represent other program work; remove for real
            if not done and res == STATUS_T.NO_ERR:
                time.sleep(sleep_val)

        log("Done, result: %s" % res)

    except Exception as e:
        print(f"An error occurred: {e}")

# Close the log file when done
if log_file_name:
    log_file.close()
