///
/// Nanotec Nanolib example
/// Copyright (C) Nanotec GmbH & Co. KG - All Rights Reserved
///
/// This product includes software developed by the
/// Nanotec GmbH & Co. KG (http://www.nanotec.com/).
///
/// The Nanolib interface headers and the examples source code provided are 
/// licensed under the Creative Commons Attribution 4.0 Internaltional License. 
/// To view a copy of this license, 
/// visit https://creativecommons.org/licenses/by/4.0/ or send a letter to 
/// Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
///
/// The parts of the library provided in binary format are licensed under 
/// the Creative Commons Attribution-NoDerivatives 4.0 International License. 
/// To view a copy of this license, 
/// visit http://creativecommons.org/licenses/by-nd/4.0/ or send a letter to 
/// Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
///
/// <file_name>Example.cs</file_name>
///
/// <summary>Main function, definition of menu structure, signal handling etc.</summary>
///
/// <date>29-10-2024</date>
///
/// <author>Michael Milbradt<author>
///

﻿using BusFunctionsExample;
using DeviceFunctionsExample;
using LoggingFunctionsExample;
using MenuUtils;
using MotorFunctionsExample;
using Nlc;
using OdInterfaceFunctionsExample;
using ProfinetFunctionsExample;
using SamplerFunctionsExample;

namespace NanolibExample
{
    public class Example
    {
        /// <summary>
        /// Creates the connectDeviceMenu with dynamic entries linked to connectDevice function.
        /// </summary>
        /// <param name="ctx">The menu context.</param>
        public static void BuildConnectDeviceMenu(Context ctx)
        {
            List<Menu.MenuItem> vmi = [];
            Menu connectDeviceMenu = new Menu(MenuTexts.DEVICE_CONNECT_MENU, vmi, (Object)DeviceFunctions.ConnectDevice);
            connectDeviceMenu.MenuLoop(connectDeviceMenu, ctx);
        }

        /// <summary>
        /// Creates the disconnectFromDeviceMenu with dynamic entries linked to disconnectDevice function.
        /// </summary>
        /// <param name="ctx">The menu context.</param>
        public static void BuildDisconnectDeviceMenu(Context ctx)
        {
            List<Menu.MenuItem> vmi = [];
            Menu disconnectFromDeviceMenu = new Menu(MenuTexts.DEVICE_DISCONNECT_MENU, vmi, (Object)DeviceFunctions.DisconnectDevice);
            disconnectFromDeviceMenu.MenuLoop(disconnectFromDeviceMenu, ctx);
        }

        /// <summary>
        /// Creates the openBusHwMenu with dynamic entries linked to openBusHardware function.
        /// </summary>
        /// <param name="ctx">The menu context.</param>
        public static void BuildOpenBusHwMenu(Context ctx)
        {
            List<Menu.MenuItem> vmi = [];
            Menu openBusHwMenu = new Menu(MenuTexts.BUS_HARDWARE_OPEN_MI, vmi, (Object)BusFunctions.OpenBusHardware);
            openBusHwMenu.MenuLoop(openBusHwMenu, ctx);
        }

        /// <summary>
        /// Creates the closeBusHwMenu with dynamic entries linked to closeBusHardware function.
        /// </summary>
        /// <param name="ctx">The menu context.</param>
        public static void BuildCloseBusHwMenu(Context ctx)
        {
            List<Menu.MenuItem> vmi = [];
            Menu closeBusHwMenu = new Menu(MenuTexts.BUS_HARDWARE_CLOSE_MI, vmi, (Object)BusFunctions.CloseBusHardware);
            closeBusHwMenu.MenuLoop(closeBusHwMenu, ctx);
        }

        /// <summary>
        /// Creates the selectActiveDeviceMenu with dynamic entries linked to selectActiveDevice function.
        /// </summary>
        /// <param name="ctx">The menu context.</param>
        public static void BuildSelectActiveDeviceMenu(Context ctx)
        {
            List<Menu.MenuItem> vmi = [];
            Menu selectActiveDeviceMenu = new Menu(MenuTexts.DEVICE_SELECT_ACTIVE_MENU, vmi, (Object)DeviceFunctions.SelectActiveDevice);
            selectActiveDeviceMenu.MenuLoop(selectActiveDeviceMenu, ctx);
        }

        /// <summary>
        /// Signal handler function.
        /// </summary>
        /// <param name="args">The signal received.</param>
        protected static void SignalHandler(object sender, ConsoleCancelEventArgs args)
        {
            Console.WriteLine($"  Key pressed: {args.SpecialKey}");
            Console.WriteLine($"  Cancel property: {args.Cancel}");

        }

        /// <summary>
        /// Main function.
        /// </summary>
        public static void Main()
        {
            // Register signal handler for SIGINT
            Console.CancelKeyPress += (sender, e) => SignalHandler(sender: sender, args: e);
            Context context = new Context(); // The menu context

            // Set log level to off
            context.NanolibAccessor.setLoggingLevel(LogLevel.Off);
            context.CurrentLogLevel = LogLevel.Off;

            // build the motorMenu menu as a submenu
	        // every menu has a title and menu items
	        // every menu item has a name and an object to a function or another menu
	        // on dynamic menus the function is used to build the underlaying menu
            List<Menu.MenuItem> vmi = [new Menu.MenuItem(MenuTexts.MOTOR_AUTO_SETUP_MI, (Object)MotorFunctions.MotorAutoSetup, IsActive: false),
                new Menu.MenuItem(MenuTexts.MOTOR_VELOCITY_MI, (Object)MotorFunctions.ExecuteProfileVelocityMode, false),
                new Menu.MenuItem(MenuTexts.MOTOR_POSITIONING_MI, (Object)MotorFunctions.ExecutePositioningMode, false)];
            Menu motorMenu = new Menu(MenuTexts.MOTOR_EXAMPLE_MENU, vmi);

            // build the samplerMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.SAMPLER_NORMAL_WO_NOTIFY_MI, (Object)SamplerFunctions.ExecuteSamplerWithoutNotificationNormalMode, false),
                new Menu.MenuItem(MenuTexts.SAMPLER_REPETETIVE_WO_NOTIFY_MI, (Object)SamplerFunctions.ExecuteSamplerWithoutNotificationRepetitiveMode, false),
                new Menu.MenuItem(MenuTexts.SAMPLER_CONTINUOUS_WO_NOTIFY_MI, (Object)SamplerFunctions.ExecuteSamplerWithoutNotificationContinuousMode, false),
                new Menu.MenuItem(MenuTexts.SAMPLER_NORMAL_WITH_NOTIFY_MI, (Object)SamplerFunctions.ExecuteSamplerWithNotificationNormalMode, false),
                new Menu.MenuItem(MenuTexts.SAMPLER_REPETETIVE_WITH_NOTIFY_MI, (Object)SamplerFunctions.ExecuteSamplerWithNotificationRepetitiveMode, false),
                new Menu.MenuItem(MenuTexts.SAMPLER_CONTINUOUS_WITH_NOTIFY_MI, (Object)SamplerFunctions.ExecuteSamplerWithNotificationContinuousMode, false)];
            Menu samplerMenu = new Menu(MenuTexts.SAMPLER_EXAMPLE_MENU, vmi);

            // build the logCallbackMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.LOG_CALLBACK_CORE_MI, (Object)LoggingFunctions.SetLoggingCallback, true),
                new Menu.MenuItem(MenuTexts.LOG_CALLBACK_CANOPEN_MI, (Object)LoggingFunctions.SetLoggingCallback, true),
                new Menu.MenuItem(MenuTexts.LOG_CALLBACK_ETHERCAT_MI, (Object)LoggingFunctions.SetLoggingCallback, true),
                new Menu.MenuItem(MenuTexts.LOG_CALLBACK_MODBUS_MI, (Object)LoggingFunctions.SetLoggingCallback, true),
                new Menu.MenuItem(MenuTexts.LOG_CALLBACK_REST_MI, (Object)LoggingFunctions.SetLoggingCallback, true),
                new Menu.MenuItem(MenuTexts.LOG_CALLBACK_USB_MI, (Object)LoggingFunctions.SetLoggingCallback, true),
                new Menu.MenuItem(MenuTexts.LOG_CALLBACK_DEACTIVATE_MI, (Object)LoggingFunctions.SetLoggingCallback, true)];
            Menu logCallbackMenu = new Menu(MenuTexts.LOG_CALLBACK_MENU, vmi);

            // build the logLevelgMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.LOG_LEVEL_TRACE_MI, (Object)LoggingFunctions.SetLogLevel, true),
                new Menu.MenuItem(MenuTexts.LOG_LEVEL_DEBUG_MI, (Object)LoggingFunctions.SetLogLevel, true),
                new Menu.MenuItem(MenuTexts.LOG_LEVEL_INFO_MI, (Object)LoggingFunctions.SetLogLevel, true),
                new Menu.MenuItem(MenuTexts.LOG_LEVEL_WARN_MI, (Object)LoggingFunctions.SetLogLevel, true),
                new Menu.MenuItem(MenuTexts.LOG_LEVEL_ERROR_MI, (Object)LoggingFunctions.SetLogLevel, true),
                new Menu.MenuItem(MenuTexts.LOG_LEVEL_CRITICAL_MI, (Object)LoggingFunctions.SetLogLevel, true),
                new Menu.MenuItem(MenuTexts.LOG_LEVEL_OFF_MI, (Object)LoggingFunctions.SetLogLevel, true)];
            Menu logLevelMenu = new Menu(MenuTexts.LOG_LEVEL_MENU, vmi);

            // build the loggingMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.LOGGING_SET_LOG_LEVEL_MI, logLevelMenu, true),
                new Menu.MenuItem(MenuTexts.LOGGING_SET_LOG_CALLBACK_MI, logCallbackMenu, true)];
            Menu loggingMenu = new Menu(MenuTexts.LOGGING_MENU, vmi);

            // build the odAccessMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.OD_ASSIGN_OD_MI, (Object)OdInterfaceFunctions.AssignObjectDictionary, false),
                new Menu.MenuItem(MenuTexts.OD_READ_NUMBER_MI, (Object)OdInterfaceFunctions.ReadNumber, false),
                new Menu.MenuItem(MenuTexts.OD_READ_NUMBER_VIA_OD_MI, (Object)OdInterfaceFunctions.ReadNumberViaDictionaryInterface, false),
                new Menu.MenuItem(MenuTexts.OD_WRITE_NUMBER_MI, (Object)OdInterfaceFunctions.WriteNumber, false),
                new Menu.MenuItem(MenuTexts.OD_WRITE_NUMBER_VIA_OD_MI, (Object)OdInterfaceFunctions.WriteNumberViaDictionaryInterface, false),
                new Menu.MenuItem(MenuTexts.OD_READ_STRING_MI, (Object)OdInterfaceFunctions.ReadString, false),
                new Menu.MenuItem(MenuTexts.OD_READ_BYTES_MI, (Object)OdInterfaceFunctions.ReadArray, false)];
            Menu odAccessMenu = new Menu(MenuTexts.OD_INTERFACE_MENU, vmi);

            // build the deviceInfoMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.DEVICE_GET_VENDOR_ID_MI, (Object)DeviceFunctions.GetDeviceVendorId, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_PRODUCT_CODE_MI, (Object)DeviceFunctions.GetDeviceProductCode, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_DEVICE_NAME_MI, (Object)DeviceFunctions.GetDeviceName, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_HW_VERSION_MI, (Object)DeviceFunctions.GetDeviceHardwareVersion, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_FW_BUILD_ID_MI, (Object)DeviceFunctions.GetDeviceFirmwareBuildId, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_BL_BUILD_ID_MI, (Object)DeviceFunctions.GetDeviceBootloaderBuildId, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_SERIAL_NUMBER_MI, (Object)DeviceFunctions.GetDeviceSerialNumber, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_UNIQUE_ID_MI, (Object)DeviceFunctions.GetDeviceUid, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_BL_VERSION_MI, (Object)DeviceFunctions.GetDeviceBootloaderVersion, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_HW_GROUP_MI, (Object)DeviceFunctions.GetDeviceHardwareGroup, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_CON_STATE_MI, (Object)DeviceFunctions.GetConnectionState, false)];
            Menu deviceInfoMenu = new Menu(MenuTexts.DEVICE_INFORMATION_MENU, vmi);

            // build the deviceMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.DEVICE_SCAN_MI, (Object)DeviceFunctions.ScanDevices, false),
                new Menu.MenuItem(MenuTexts.DEVICE_CONNECT_MENU, (Object)BuildConnectDeviceMenu, false),
                new Menu.MenuItem(MenuTexts.DEVICE_DISCONNECT_MENU, (Object)BuildDisconnectDeviceMenu, false),
                new Menu.MenuItem(MenuTexts.DEVICE_SELECT_ACTIVE_MENU, (Object)BuildSelectActiveDeviceMenu, false),
                new Menu.MenuItem(MenuTexts.DEVICE_REBOOT_MI, (Object)DeviceFunctions.RebootDevice, false),
                new Menu.MenuItem(MenuTexts.DEVICE_INFORMATION_MENU, deviceInfoMenu, false),
                new Menu.MenuItem(MenuTexts.DEVICE_UPDATE_FW_MI, (Object)DeviceFunctions.UpdateFirmware, false),
                new Menu.MenuItem(MenuTexts.DEVICE_UPDATE_BL_MI, (Object)DeviceFunctions.UpdateBootloader, false),
                new Menu.MenuItem(MenuTexts.DEVICE_UPLOAD_NANOJ_MI, (Object)DeviceFunctions.UploadNanoJ, false),
                new Menu.MenuItem(MenuTexts.DEVICE_RUN_NANOJ_MI, (Object)DeviceFunctions.RunNanoJ, false),
                new Menu.MenuItem(MenuTexts.DEVICE_STOP_NANOJ_MI, (Object)DeviceFunctions.StopNanoJ, false),
                new Menu.MenuItem(MenuTexts.DEVICE_GET_ERROR_FIELD_MI, (Object)DeviceFunctions.GetErrorFields, false),
                new Menu.MenuItem(MenuTexts.DEVICE_RESTORE_ALL_DEFAULT_PARAMS_MI, (Object)DeviceFunctions.RestoreDefaults, false)];
            Menu deviceMenu = new Menu(MenuTexts.DEVICE_MENU, vmi);

            // build the busHwMenu menu as a submenu
            vmi = [new Menu.MenuItem(MenuTexts.BUS_HARDWARE_SCAN_MI, (Object)BusFunctions.ScanBusHardware, true),
                new Menu.MenuItem(MenuTexts.BUS_HARDWARE_OPEN_MI, (Object)BuildOpenBusHwMenu, false),
                new Menu.MenuItem(MenuTexts.BUS_HARDWARE_CLOSE_MI, (Object)BuildCloseBusHwMenu, false),
                new Menu.MenuItem(MenuTexts.BUS_HARDWARE_CLOSE_ALL_MI, (Object)BusFunctions.CloseAllBusHardware, false)];
            Menu busHwMenu = new Menu(MenuTexts.BUS_HARDWARE_MENU, vmi);

            // build the mainMenu menu as a mainmenu
            vmi = [new Menu.MenuItem(MenuTexts.BUS_HARDWARE_MENU, busHwMenu, true),
                new Menu.MenuItem(MenuTexts.DEVICE_MENU, deviceMenu, false),
                new Menu.MenuItem(MenuTexts.OD_INTERFACE_MENU, odAccessMenu, false),
                new Menu.MenuItem(MenuTexts.LOGGING_MENU, loggingMenu, true),
                new Menu.MenuItem(MenuTexts.SAMPLER_EXAMPLE_MENU, samplerMenu, false),
                new Menu.MenuItem(MenuTexts.MOTOR_EXAMPLE_MENU, motorMenu, false),
                new Menu.MenuItem(MenuTexts.PROFINET_EXAMPLE_MI, (Object)ProfinetFunctions.ProfinetDCPExample, false)];
            Menu mainMenu = new Menu(MenuTexts.MAIN_MENU, vmi);

            // Start the main menu
            mainMenu.MenuLoop(mainMenu, context);

            // Close all opened bus hardware
            // Connected devices will be disconnected / removed automatically
            BusFunctions.CloseAllBusHardware(context);

            /// IMPORTANT: the member function pointer of NlcLoggingCallback::callback gets
            /// bound to a std::function pointer in between (because of SWIG not supporting std::function)
            /// and has to be (manually) unset/deleted in the destructor / during garbage collection!
            /// since C# GC does not clean-up immediately, we have to clean-up manually here!!
            context.LoggingCallback.Dispose();

            // Exit main program
            Environment.Exit(0);
        }
    }
}
