using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Automation.Peers;
using FwUpdateDriverApi;

namespace FwUpdateApiSample
{
    internal static class IntelUsb4Validator
    {
        const uint defaultDromAddressNvmLocation = 0x10E;

        /// <summary>
        /// Validates if the given image descriptor can be used to update the device
        /// </summary>
        /// <param name="desc">Image descriptor to use for update</param>
        /// <param name="device">Device interface</param>
        /// <returns></returns>
        public static bool DoesMatch(NvmImageDescriptor desc, IDriverDevice device)
        {
            return _ValidateDrom(desc, device.Drom);
        }
        /// <summary>
        /// Validates if the given image descriptor can be used to update the retimer in provided route.
        /// </summary>
        /// <param name="desc">Image descriptor to use for update</param>
        /// <param name="device">Device interface</param>
        /// <param name="route">Retimer's route</param>
        /// <returns></returns>
        public static bool DoesMatch(NvmImageDescriptor desc, IDriverDevice device, RetimerRoute route)
        {
            var retimer = device.OnBoardRetimers.FirstOrDefault(r => r.Route.IsSame(route));
            if (retimer == null)
            {
                throw new TbtException(TbtStatus.SDK_RETIMER_NOT_FOUND, $"Retimer with given route - {route} not present in the device");
            }

            return _ValidateDrom(desc, retimer.Drom);
        }

        private static bool _ValidateDrom(NvmImageDescriptor desc, IDrom currentDrom)
        {
            var imageDrom = DromFactory.CreateDrom((o, l) => _ReadDromFromFile(desc, o, l), Logger.Instance);
            if (imageDrom.Tbt3Compatible)
            {
                if (!currentDrom.Tbt3Compatible)
                {
                    return false;
                }

                var tbt3DeviceHdr = new Tbt3DromHeader(currentDrom.Sections[DromSectionName.Header]);
                var tbt3ImageHdr = new Tbt3DromHeader(imageDrom.Sections[DromSectionName.Header]);
                return tbt3DeviceHdr.IsSame(tbt3ImageHdr);
            }
            else
            {
                if (currentDrom.Tbt3Compatible)
                {
                    return false;
                }
                var usb4DeviceProductDescriptor = new Usb4DromProductDescriptor(currentDrom.Sections[DromSectionName.ProductDescriptor]);
                var usb4ImageProductDescriptor = new Usb4DromProductDescriptor(imageDrom.Sections[DromSectionName.ProductDescriptor]);
                return usb4DeviceProductDescriptor.IsSame(usb4ImageProductDescriptor);
            }
        }

        private static byte[] _ReadDromFromFile(NvmImageDescriptor desc, uint offset, uint length)
        {
            var nvmRelativeOffset = BitConverter.ToUInt32(desc.WholeImage, 0) & Utilities.RelativeNvmOffsetRetrieveMask;
            var dromStartOffset =
                BitConverter.ToUInt32(desc.WholeImage.Skip((int) (nvmRelativeOffset + defaultDromAddressNvmLocation))
                    .Take(4).ToArray(), 0);
            return desc.WholeImage.Skip((int)(nvmRelativeOffset + dromStartOffset + offset)).Take((int)length).ToArray();
        }
    }
}
