//                           UNCLASSIFIED
/*-------------------------------------------------------------------------
RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
COMPANY.

THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
GOVERNMENT.

UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
-------------------------------------------------------------------------*/

using System;
using System.IO;
using System.Linq;
using System.Text;

namespace Raytheon.Common
{
   /// <summary>
   /// A collection of utilities.
   /// </summary>
   public static class Util
   {
      #region PublicFunctions

      /// <summary>
      /// 
      /// </summary>
      /// <param name="registerContents"></param>
      /// <param name="startBit"></param>
      /// <param name="numOfBitsToMask"></param>
      /// <param name="inverseFlag"></param>
      /// <returns></returns>
      public static uint ApplyBitMask(uint registerContents, int startBit, int numOfBitsToMask, bool inverseFlag = false)
      {
         uint mask = 1;
         for (int i = 0; i < numOfBitsToMask - 1; i++)
         {
            mask = mask << 1;
            mask = mask + 1;
         }

         //left shift to startbit position
         mask = mask << startBit;

         //Inverse the mask
         if (inverseFlag)
         {
            mask = ~mask;
         }

         //Apply Mask and return
         return (registerContents & mask);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="ba"></param>
      /// <returns></returns>
      public static string ByteArrayToHexString(byte[] ba)
      {
         StringBuilder hex = new StringBuilder(ba.Length * 2);

         foreach (byte b in ba)
         {
            hex.AppendFormat("{0:x2}", b);
         }

         return hex.ToString();
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="src"></param>
      /// <param name="pattern"></param>
      /// <returns></returns>
      public static int ByteArraySearch(byte[] src, int srcStartIndex, byte[] pattern)
      {
         int maxFirstCharSlot = src.Length - pattern.Length + 1;
         for (int i = srcStartIndex; i < maxFirstCharSlot; i++)
         {
            if (src[i] != pattern[0]) // compare only first byte
            {
               continue;
            }

            // found a match on first byte, now try to match rest of the pattern
            for (int j = pattern.Length - 1; j >= 1; j--)
            {
               if (src[i + j] != pattern[j]) break;
               if (j == 1) return i;
            }
         }
         return -1;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="stringToConvert"></param>
      /// <returns></returns>
      public static double ConvertStringToDouble(string stringToConvert)
      {
         try
         {
            double rsp = Convert.ToDouble(stringToConvert);

            return rsp;
         }
         catch (Exception err)
         {
            throw new Exception("Util::ConvertStringToDouble() - " + err.Message + ". Faulty String: " + stringToConvert);
         }
      }

      /// <summary>
      /// Converts a string to a ushort
      /// </summary>
      /// <param name="stringToConvert"></param>
      /// <returns></returns>
      public static ushort ConvertStringToUInt16(string stringToConvert)
      {
         try
         {
            ushort rsp = Convert.ToUInt16(stringToConvert);

            return rsp;
         }
         catch (Exception err)
         {
            throw new Exception("Util::ConvertStringToUInt16() - " + err.Message + ". Faulty String: " + stringToConvert);
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="stringToConvert"></param>
      /// <returns></returns>
      public static int ConvertStringToInt32(string stringToConvert)
      {
         try
         {
            int rsp = Convert.ToInt32(stringToConvert);

            return rsp;
         }
         catch (Exception err)
         {
            throw new Exception("Util::ConvertStringToInt32() - " + err.Message + ". Faulty String: " + stringToConvert);
         }
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="sourceDirName"></param>
      /// <param name="destDirName"></param>
      /// <param name="copySubDirs"></param>
      public static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
      {
         // Get the subdirectories for the specified directory.
         DirectoryInfo dir = new DirectoryInfo(sourceDirName);

         if (!dir.Exists)
         {
            throw new DirectoryNotFoundException(
                "Source directory does not exist or could not be found: "
                + sourceDirName);
         }

         DirectoryInfo[] dirs = dir.GetDirectories();
         // If the destination directory doesn't exist, create it.
         if (!Directory.Exists(destDirName))
         {
            Directory.CreateDirectory(destDirName);
         }

         // Get the files in the directory and copy them to the new location.
         FileInfo[] files = dir.GetFiles();
         foreach (FileInfo file in files)
         {
            string temppath = Path.Combine(destDirName, file.Name);
            file.CopyTo(temppath, false);
         }

         // If copying subdirectories, copy them and their contents to new location.
         if (copySubDirs)
         {
            foreach (DirectoryInfo subdir in dirs)
            {
               string temppath = Path.Combine(destDirName, subdir.Name);
               DirectoryCopy(subdir.FullName, temppath, copySubDirs);
            }
         }
      }

      /// <summary>
      /// Compares the contents of two files
      /// </summary>
      /// <param name="fileName1"></param>
      /// <param name="fileName2"></param>
      /// <returns></returns>
      public static bool FileEquals(string fileName1, string fileName2)
      {
         using (var file1 = new FileStream(fileName1, FileMode.Open))
         using (var file2 = new FileStream(fileName2, FileMode.Open))
            return FileStreamEquals(file1, file2);
      }

      /// <summary>
      /// Compares the contents of two file streams
      /// </summary>
      /// <param name="stream1"></param>
      /// <param name="stream2"></param>
      /// <returns></returns>
      public static bool FileStreamEquals(Stream stream1, Stream stream2)
      {
         const int BUFFER_SIZE = 2048;
         byte[] buffer1 = new byte[BUFFER_SIZE]; //buffer size
         byte[] buffer2 = new byte[BUFFER_SIZE];

         while (true)
         {
            int count1 = stream1.Read(buffer1, 0, BUFFER_SIZE);
            int count2 = stream2.Read(buffer2, 0, BUFFER_SIZE);

            if (count1 != count2)
            {
               return false;
            }

            if (count1 == 0)
            {
               return true;
            }

            // You might replace the following with an efficient "memcmp"
            if (!buffer1.Take(count1).SequenceEqual(buffer2.Take(count2)))
            {
               return false;
            }
         }
      }

      /// <summary>
      /// Get a timestamp in string form.
      /// </summary>
      /// <returns>The timestamp string.</returns>
      public static string GetTimeString()
      {
         DateTime nowTime = DateTime.Now;
         string time = nowTime.Year.ToString("0000") + "_" + nowTime.Month.ToString("00") + "_" + nowTime.Day.ToString("00") + "_" + nowTime.Hour.ToString("00") + "_" + nowTime.Minute.ToString("00") + "_" + nowTime.Second.ToString("00") + "_" + nowTime.Millisecond.ToString("000");
         return time;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="address"></param>
      /// <param name="pingTimeOutInMs"></param>
      /// <returns></returns>
      public static bool IsIpAddressReachable(string address, int pingTimeOutInMs = 1000)
      {
         bool isReachable = false;

         System.Net.NetworkInformation.Ping p = new System.Net.NetworkInformation.Ping();

         System.Net.NetworkInformation.PingReply reply = p.Send(address, pingTimeOutInMs);

         if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
         {
            isReachable = true;
         }

         return isReachable;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="registerContents"></param>
      /// <param name="startBit"></param>
      /// <param name="numOfBitsToMask"></param>
      /// <returns></returns>
      public static uint ParseRegisterItem(uint registerContents, int startBit, int numOfBitsToMask)
      {
         uint mask = 1;
         for (int i = 0; i < numOfBitsToMask - 1; i++)
         {
            mask = mask << 1;
            mask = mask + 1;
         }

         //left shift to startbit position
         mask = mask << startBit;

         uint dataToReturn = (registerContents & mask);

         dataToReturn = dataToReturn >> startBit;

         //Apply Mask and return
         return dataToReturn;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="data"></param>
      /// <returns></returns>
      public static ushort ReverseBits16(ushort data)
      {
         ushort reversedData = 0;

         for (int i = 0; i < 16; ++i)
         {
            reversedData <<= 1;
            reversedData |= (ushort)(data & 1);
            data >>= 1;
         }

         return reversedData;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="input"></param>
      /// <returns></returns>
      public static byte[] Swap(byte[] input)
      {
         Array.Reverse(input);
         return input;
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="input"></param>
      /// <returns></returns>
      public static double Swap(double input)
      {
         byte[] byteArray = BitConverter.GetBytes(input);
         Array.Reverse(byteArray);
         return BitConverter.ToDouble(byteArray, 0);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="input"></param>
      /// <returns></returns>
      public static ulong Swap(ulong input)
      {
         byte[] byteArray = BitConverter.GetBytes(input);
         Array.Reverse(byteArray);
         return BitConverter.ToUInt64(byteArray, 0);
      }

      /// <summary>
      /// Reverse the byte order of the input parameter (16bit).
      /// </summary>
      /// <param name="input">16bit data.</param>
      /// <returns>Reversed 16bit data.</returns>
      public static ushort Swap(ushort input)
      {
         byte[] byteArray = BitConverter.GetBytes(input);
         Array.Reverse(byteArray);
         return BitConverter.ToUInt16(byteArray, 0);
      }

      /// <summary>
      /// Reverse the byte order of the input parameter (32bit).
      /// </summary>
      /// <param name="input">32bit data.</param>
      /// <returns>Reversed 32bit data.</returns>
      public static uint Swap(uint input)
      {
         byte[] byteArray = BitConverter.GetBytes(input);
         Array.Reverse(byteArray);
         return BitConverter.ToUInt32(byteArray, 0);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="input"></param>
      /// <returns></returns>
      public static float Swap(float input)
      {
         byte[] byteArray = BitConverter.GetBytes(input);
         Array.Reverse(byteArray);
         return BitConverter.ToSingle(byteArray, 0);
      }

      /// <summary>
      /// 
      /// </summary>
      /// <param name="dataBeingSwapped"></param>
      /// <returns></returns>
      public static uint SwapHighAndLowBytes(uint dataBeingSwapped)
      {
         //Using a 16-bit shift to move around four hex values at a time
         dataBeingSwapped = ((dataBeingSwapped & 0xFFFF) << 16) | ((uint)(dataBeingSwapped & 0xFFFF0000) >> 16);
         return dataBeingSwapped;
      }

      #endregion
   }
}
