///
/// 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>OdInferfaceFunctionsExample.cs</file_name>
///
/// <summary>Definition of object dictionary interface specific functions</summary>
///
/// <date>29-10-2024</date>
///
/// <author>Michael Milbradt<author>
///

using MenuUtils;
using Nlc;

namespace OdInterfaceFunctionsExample
{
    public class OdInterfaceFunctions
    {
        /// <summary>
        /// Function to read a number (no interpretation of the data possible)
        /// </summary>
        /// <param name="ctx">Menu context</param>
        public static void ReadNumber(Context ctx)
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            Console.WriteLine($"Reading mode of operation ({OdIndices.OdModeOfOperation.ToString()}) ...");
            var resultInt = ctx.NanolibAccessor.readNumber(ctx.ActiveDevice, OdIndices.OdModeOfOperation);
            if (resultInt.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumber: ", resultInt.getError());
            }
            Console.WriteLine($"{OdIndices.OdModeOfOperation.ToString()} = {resultInt.getResult()}");
            Console.WriteLine("This is only the raw value. The OD value might be signed or unsigned up to a total length of 4 bytes\n");

            Console.WriteLine($"Reading SI unit position ({OdIndices.OdSIUnitPosition.ToString()}) ...");
            resultInt = ctx.NanolibAccessor.readNumber(ctx.ActiveDevice, OdIndices.OdSIUnitPosition);
            if (resultInt.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumber: ", resultInt.getError());
                return;
            }
            Console.WriteLine($"{OdIndices.OdSIUnitPosition.ToString()} = {resultInt.getResult()}");
            Console.WriteLine("This is only the raw value. The OD value might be signed or unsigned up to a total length of 4 bytes");
        }

        /// <summary>
        /// Function to read a string (string might be zero)
        /// </summary>
        /// <param name="ctx">Menu context</param>
        public static void ReadString(Context ctx)
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            Console.WriteLine($"Reading Nanotec home page string ({OdIndices.OdHomePage.ToString()}) ...");
            var resultString = ctx.NanolibAccessor.readString(ctx.ActiveDevice, OdIndices.OdHomePage);
            if (resultString.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadString: ", resultString.getError());
            }
            Console.WriteLine($"{OdIndices.OdHomePage.ToString()} = '{resultString.getResult()}'");
        }

        /// <summary>
        /// Function to read an array (no interpretion of the data possible)
        /// </summary>
        /// <param name="ctx">Menu context</param>
        public static void ReadArray(Context ctx)
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            Console.WriteLine("Reading device error stack (0x1003) ...");
            var resultArrayInt = ctx.NanolibAccessor.readNumberArray(ctx.ActiveDevice, OdIndices.OdErrorStackIndex);
            if (resultArrayInt.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadArray: ", resultArrayInt.getError());
            }

            // Output only the first field (error count)
            // Fields with index > 0 would contain specific stored errors
            IntVector errorStack = resultArrayInt.getResult();
            Console.WriteLine($"The error stack has {errorStack.Count} elements");
            Console.WriteLine($"The first element (error count) is: {errorStack[0]}");
        }

        /// <summary>
        /// Function to write a number with a certain length
        /// </summary>
        /// <param name="ctx">Menu context</param>
        public static void WriteNumber(Context ctx)
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            Console.WriteLine($"Writing motor stop command to control word ({OdIndices.OdControlWord.ToString()} = 0x06) ...");
            var resultVoid = ctx.NanolibAccessor.writeNumber(ctx.ActiveDevice, 6, OdIndices.OdControlWord, 16);
            if (resultVoid.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during WriteNumber: ", resultVoid.getError());
            }
        }

        /// <summary>
        /// Assign a valid object dictionary to the current active device
        /// </summary>
        /// <param name="ctx">Menu context</param>

        public static void AssignObjectDictionary(Context ctx)
        {
            ctx.WaitForUserConfirmation = false;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            string inputPath;
            Console.Write("Please enter the directory (path) where the od.xml is located: ");
            inputPath = Console.ReadLine();

            var resultObjectDictionary = ctx.NanolibAccessor.autoAssignObjectDictionary(ctx.ActiveDevice, inputPath);

            if (resultObjectDictionary.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during AssignObjectDictionary: ", resultObjectDictionary.getError());
                return;
            }
        }

        /// <summary>
        /// Function to read a number (with interpretation of the data)
        /// </summary>
        /// <param name="ctx">Menu context</param>
        public static void ReadNumberViaDictionaryInterface(Context ctx)
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            var resultObjectDictionary = ctx.NanolibAccessor.getAssignedObjectDictionary(ctx.ActiveDevice);
            if (resultObjectDictionary.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumberViaDictionaryInterface: ", resultObjectDictionary.getError());
                return;
            }

            if (string.IsNullOrEmpty(resultObjectDictionary.getResult().getXmlFileName().getResult()))
            {
                Console.WriteLine($"{ctx.LightYellow}No valid object dictionary assigned. Using fallback method!{ctx.Def}");
            }

            var objectDictionary = resultObjectDictionary.getResult();

            if (!objectDictionary.getDeviceHandle().getResult().Equals(ctx.ActiveDevice))
            {
                StringUtils.HandleErrorMessage(ctx, "", "Object dictionary mismatch in ReadNumberViaDictionaryInterface.");
                return;
            }

            Console.WriteLine($"Reading mode of operation ({OdIndices.OdModeOfOperation.ToString()}) ...");
            var resultInt = objectDictionary.readNumber(OdIndices.OdModeOfOperation);
            if (resultInt.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumberViaDictionaryInterface: ", resultInt.getError());
            }

            // OD 0x6060:00 is of type sbyte so the user has to cast accordingly
            sbyte modeOfOperation = (sbyte)resultInt.getResult();
            Console.WriteLine($"{OdIndices.OdModeOfOperation.ToString()} = {modeOfOperation}");

            Console.WriteLine("Some object entry properties:");
            var objectEntry = objectDictionary.getObjectEntry(OdIndices.OdModeOfOperation.Index).getResult();
            Console.WriteLine($"Object({OdIndices.OdModeOfOperation.ToString()}).ObjectCode = {objectEntry.getObjectCode()}");

            Console.WriteLine($"Object(0x6060).DataType = {OdTypesHelper.objectEntryDataTypeToString(objectEntry.getDataType())}");

            Console.WriteLine("Some ObjectSubEntry properties:");
            var objectSubEntry = objectDictionary.getObject(OdIndices.OdModeOfOperation).getResult();
            Console.WriteLine($"OdIndex({OdIndices.OdModeOfOperation.ToString()}).DataType = {OdTypesHelper.objectEntryDataTypeToString(objectSubEntry.getDataType())}");
            Console.WriteLine($"OdIndex({OdIndices.OdModeOfOperation.ToString()}).BitLength = {objectSubEntry.getBitLength()}\n");

            Console.WriteLine($"Reading SI unit position ({OdIndices.OdSIUnitPosition.ToString()}) ...");
            resultInt = objectDictionary.readNumber(OdIndices.OdSIUnitPosition);
            if (resultInt.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumberViaDictionaryInterface: ", resultInt.getError());
                return;
            }

            // OD 0x60A8:00 is of type uint so the user has to cast accordingly
            uint unitPosition = (uint)resultInt.getResult();
            Console.WriteLine($"{OdIndices.OdSIUnitPosition.ToString()} = {unitPosition}");

            Console.WriteLine("Some object entry properties:");
            var objectEntry2 = objectDictionary.getObjectEntry(OdIndices.OdSIUnitPosition.Index).getResult();
            Console.WriteLine($"Object({OdIndices.OdSIUnitPosition.ToString()}).ObjectCode = {objectEntry2.getObjectCode()}");

            Console.WriteLine($"Object(0x60A8).DataType = {OdTypesHelper.objectEntryDataTypeToString(objectEntry2.getDataType())}");

            Console.WriteLine("Some ObjectSubEntry properties:");
            var objectSubEntry2 = objectDictionary.getObject(OdIndices.OdSIUnitPosition).getResult();
            Console.WriteLine($"OdIndex({OdIndices.OdSIUnitPosition.ToString()}).DataType = {OdTypesHelper.objectEntryDataTypeToString(objectSubEntry2.getDataType())}");
            Console.WriteLine($"OdIndex({OdIndices.OdSIUnitPosition.ToString()}).BitLength = {objectSubEntry2.getBitLength()}");
        }


        /*
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            var resultObjectDictionary = ctx.NanolibAccessor.getAssignedObjectDictionary(ctx.ActiveDevice);
            if (resultObjectDictionary.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumberViaDictionaryInterface: ", resultObjectDictionary.getError());
                return;
            }

            if (string.IsNullOrEmpty(resultObjectDictionary.getResult().getXmlFileName().getResult()))
            {
                Console.WriteLine($"{ctx.LightYellow}No valid object dictionary assigned. Using fallback method!{ctx.Def}");
            }

            var objectDictionary = resultObjectDictionary.getResult();

            if (!objectDictionary.getDeviceHandle().getResult().Equals(ctx.ActiveDevice))
            {
                StringUtils.HandleErrorMessage(ctx, "", "Object dictionary mismatch in ReadNumberViaDictionaryInterface.");
                return;
            }

            Console.Write("Reading subindex 0 of index 0x60A8 = ");
            var numberResult = objectDictionary.readNumber(OdIndices.OdSIUnitPosition);
            if (numberResult.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during ReadNumberViaDictionaryInterface: ", numberResult.getError());
                return;
            }
            var unitPosition = numberResult.getResult();
            Console.WriteLine(unitPosition);

            Console.WriteLine("Some object entry properties: ");
            var objectEntry = objectDictionary.getObjectEntry(OdIndices.OdSIUnitPosition.Index).getResult();
            Console.Write($"Object({OdIndices.OdSIUnitPosition}).ObjectCode = ");
            string objectCodeString = objectEntry.getObjectCode() switch
            {
                ObjectCode.Null => "Null",
                ObjectCode.Deftype => "Deftype",
                ObjectCode.Defstruct => "Defstruct",
                ObjectCode.Var => "Var",
                ObjectCode.Array => "Array",
                ObjectCode.Record => "Record",
                _ => ((int)objectEntry.getObjectCode()).ToString()
            };
            Console.WriteLine(objectCodeString);

            Console.WriteLine($"Object(0x60A8).DataType = {OdTypesHelper.objectEntryDataTypeToString(objectEntry.getDataType())}");

            Console.WriteLine("Some ObjectSubEntry properties: ");
            var objectSubEntry = objectDictionary.getObject(OdIndices.OdSIUnitPosition).getResult();
            Console.WriteLine($"OdIndex({OdIndices.OdSIUnitPosition}).DataType = {OdTypesHelper.objectEntryDataTypeToString(objectSubEntry.getDataType())}");
            Console.WriteLine($"OdIndex({OdIndices.OdSIUnitPosition}).BitLength = {objectSubEntry.getBitLength()}");
        }
        */

        /// <summary>
        /// Function to write a number (no length has to be defined)
        /// </summary>
        /// <param name="ctx">Menu context</param>
        public static void WriteNumberViaDictionaryInterface(Context ctx)
        {
            ctx.WaitForUserConfirmation = true;

            if (ctx.ActiveDevice.Equals(new DeviceHandle()))
            {
                StringUtils.HandleErrorMessage(ctx, "No active device set. Select an active device first.");
                return;
            }

            var resultObjectDictionary = ctx.NanolibAccessor.getAssignedObjectDictionary(ctx.ActiveDevice);
            if (resultObjectDictionary.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during WriteNumberViaDictionaryInterface: ", resultObjectDictionary.getError());
                return;
            }

            if (string.IsNullOrEmpty(resultObjectDictionary.getResult().getXmlFileName().getResult()))
            {
                Console.WriteLine($"{ctx.LightYellow}No valid object dictionary assigned. Using fallback method!{ctx.Def}");
            }

            var objectDictionary = resultObjectDictionary.getResult();

            if (!objectDictionary.getDeviceHandle().getResult().Equals(ctx.ActiveDevice))
            {
                StringUtils.HandleErrorMessage(ctx, "", "Object dictionary mismatch in WriteNumberViaDictionaryInterface.");
                return;
            }

            Console.WriteLine($"Writing motor stop command to control word ({OdIndices.OdControlWord.ToString()}) with value 0x06 ...");
            long value = 6;
            var writeResult = objectDictionary.writeNumber(OdIndices.OdControlWord, value);

            if (writeResult.hasError())
            {
                StringUtils.HandleErrorMessage(ctx, "Error during WriteNumberViaDictionaryInterface: ", writeResult.getError());
                return;
            }
        }
    }
}

