/*
 * Copyright (C) 2007-2024 C# Port
 * Original Copyright (C) 2007-2018 Siemens AG
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */
using System;
using System.IO;
using System.Linq;
namespace V2GDecoderNetFx.EXI
{
    /// 
    /// Byte Stream utilities for file operations
    /// 
    public static class ByteStream
    {
        /// 
        /// Write bytes to file
        /// 
        /// byte array
        /// File name
        /// Error-Code != 0 on failure
        public static int WriteBytesToFile(byte[] data, string filename)
        {
            try
            {
                if (data == null)
                    return EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER;
                    
                if (string.IsNullOrEmpty(filename))
                    return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
                File.WriteAllBytes(filename, data);
                return 0; // Success
            }
            catch (UnauthorizedAccessException)
            {
                return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
            }
            catch (DirectoryNotFoundException)
            {
                return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
            }
            catch (IOException)
            {
                return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
            }
            catch
            {
                return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
            }
        }
        /// 
        /// Read bytes from file
        /// 
        /// File name
        /// Output byte array
        /// Number of bytes actually read
        /// Error-Code != 0 on failure
        public static int ReadBytesFromFile(string filename, out byte[] data, out int bytesRead)
        {
            data = new byte[0];
            bytesRead = 0;
            try
            {
                if (string.IsNullOrEmpty(filename))
                    return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
                if (!File.Exists(filename))
                    return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
                data = File.ReadAllBytes(filename);
                bytesRead = data.Length;
                return 0; // Success
            }
            catch (UnauthorizedAccessException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch (DirectoryNotFoundException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch (FileNotFoundException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch (IOException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
        }
        /// 
        /// Read bytes from file with buffer size limit
        /// 
        /// File name
        /// Maximum buffer size
        /// Output byte array
        /// Number of bytes actually read
        /// Error-Code != 0 on failure
        public static int ReadBytesFromFile(string filename, int maxSize, out byte[] data, out int bytesRead)
        {
            data = new byte[0];
            bytesRead = 0;
            try
            {
                if (string.IsNullOrEmpty(filename))
                    return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
                if (!File.Exists(filename))
                    return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
                using (var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
                {
                    var fileSize = (int)fileStream.Length;
                    
                    if (fileSize > maxSize)
                        return EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER;
                    data = new byte[fileSize];
                    bytesRead = fileStream.Read(data, 0, fileSize);
                    
                    return 0; // Success
                }
            }
            catch (UnauthorizedAccessException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch (DirectoryNotFoundException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch (FileNotFoundException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch (IOException)
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
            catch
            {
                return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
            }
        }
        /// 
        /// Convert hex string to byte array
        /// 
        /// Hex string
        /// Byte array
        public static byte[] HexStringToByteArray(string hex)
        {
            if (string.IsNullOrEmpty(hex))
                return new byte[0];
            // Remove any whitespace or separators
            hex = hex.Replace(" ", "").Replace("-", "").Replace(":", "");
            
            if (hex.Length % 2 != 0)
                throw new ArgumentException("Hex string must have even number of characters");
            var result = new byte[hex.Length / 2];
            for (int i = 0; i < result.Length; i++)
            {
                if (!byte.TryParse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, null, out result[i]))
                    throw new ArgumentException($"Invalid hex characters at position {i * 2}");
            }
            
            return result;
        }
        /// 
        /// Convert byte array to hex string
        /// 
        /// Byte array
        /// Use uppercase hex digits
        /// Hex string
        public static string ByteArrayToHexString(byte[] data, bool uppercase = true)
        {
            if (data == null || data.Length == 0)
                return string.Empty;
            var format = uppercase ? "X2" : "x2";
            return string.Concat(data.Select(b => b.ToString(format)));
        }
    }
}