/*
 * Copyright (C) 2007-2024 C# Port
 * Original Copyright (C) 2007-2018 Siemens AG
 *
 * Exact EXI Header implementation - byte-compatible with OpenV2G
 * Matches EXIHeaderDecoder.c and EXIHeaderEncoder.c exactly
 */

using System;

namespace V2GDecoderNet.EXI
{
    /// <summary>
    /// EXI Error codes - exact match to C implementation
    /// </summary>
    public static class EXIErrorCodesExact
    {
        public const int EXI_OK = 0;
        public const int EXI_ERROR_UNEXPECTED_END_OF_STREAM = -1;
        public const int EXI_UNSUPPORTED_HEADER_COOKIE = -2;
        public const int EXI_UNSUPPORTED_HEADER_OPTIONS = -3;
        public const int EXI_ERROR_UNKNOWN_EVENT = -4;
        public const int EXI_ERROR_OUT_OF_BYTE_BUFFER = -5;
        public const int EXI_ERROR_OUT_OF_BOUNDS = -6;
        public const int EXI_ERROR_STRINGVALUES_NOT_SUPPORTED = -7;
        public const int EXI_ERROR_NOT_IMPLEMENTED_YET = -8;
    }

    /// <summary>
    /// EXI Header decoder - exact implementation of EXIHeaderDecoder.c
    /// </summary>
    public static class EXIHeaderDecoderExact
    {
        /// <summary>
        /// Decode EXI header - exact implementation of decodeEXIHeader()
        /// </summary>
        public static int DecodeHeader(BitInputStreamExact stream, EXIHeaderExact header)
        {
            if (stream == null) throw new ArgumentNullException(nameof(stream));
            if (header == null) throw new ArgumentNullException(nameof(header));

            // Read the header byte
            int headerByte = stream.ReadBits(8);
            if (headerByte < 0)
                return EXIErrorCodesExact.EXI_ERROR_UNEXPECTED_END_OF_STREAM;

            byte header_b = (byte)headerByte;

            // Check for EXI Cookie - not supported in this implementation
            if (header_b == 0x24) // '$' character
            {
                return EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_COOKIE;
            }

            // Check presence bit for EXI Options (bit 5, value 0x20)
            if ((header_b & 0x20) != 0)
            {
                return EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_OPTIONS;
            }

            // Parse simple header format (distinguishing bits = "1")
            // Bit pattern: 1 | Version[4] | Presence[1] | Format[2]
            
            // Extract format version (bits 6-3, mask 0x1E, shift right 1)
            header.FormatVersion = (byte)((header_b & 0x1E) >> 1);
            
            // Extract format field (bits 1-0, mask 0x03)
            byte format = (byte)(header_b & 0x03);
            
            // Set preservation options based on format field
            switch (format)
            {
                case 0: // Format 00: No preservation
                    header.PreserveComments = false;
                    header.PreservePIs = false;
                    header.PreserveDTD = false;
                    header.PreservePrefixes = false;
                    break;
                case 1: // Format 01: Preserve comments and PIs
                    header.PreserveComments = true;
                    header.PreservePIs = true;
                    header.PreserveDTD = false;
                    header.PreservePrefixes = false;
                    break;
                case 2: // Format 10: Preserve DTD and prefixes
                    header.PreserveComments = false;
                    header.PreservePIs = false;
                    header.PreserveDTD = true;
                    header.PreservePrefixes = true;
                    break;
                case 3: // Format 11: Preserve all
                    header.PreserveComments = true;
                    header.PreservePIs = true;
                    header.PreserveDTD = true;
                    header.PreservePrefixes = true;
                    break;
            }

            // Header always has no cookie in this implementation
            header.HasCookie = false;

            return EXIErrorCodesExact.EXI_OK;
        }
    }

    /// <summary>
    /// EXI Header encoder - exact implementation of EXIHeaderEncoder.c
    /// </summary>
    public static class EXIHeaderEncoderExact
    {
        /// <summary>
        /// Encode EXI header - exact implementation of encodeEXIHeader()
        /// Always writes simple header format (0x80 = 128)
        /// </summary>
        public static int EncodeHeader(BitOutputStreamExact stream, EXIHeaderExact header)
        {
            if (stream == null) throw new ArgumentNullException(nameof(stream));
            if (header == null) throw new ArgumentNullException(nameof(header));

            try
            {
                // Simple header format: always write 128 (0x80)
                // Bit pattern: 1 0000 0 00 = 10000000 = 0x80 = 128
                // - Distinguishing bit: 1
                // - Version: 0000 (format version 0)
                // - Presence bit: 0 (no options)
                // - Format: 00 (no preservation)
                stream.WriteBits(8, EXIConstantsExact.EXI_HEADER_SIMPLE);
                
                return EXIErrorCodesExact.EXI_OK;
            }
            catch
            {
                return EXIErrorCodesExact.EXI_ERROR_OUT_OF_BYTE_BUFFER;
            }
        }
    }

    /// <summary>
    /// EXI Exception for exact error handling
    /// </summary>
    public class EXIExceptionExact : Exception
    {
        public int ErrorCode { get; }
        
        public EXIExceptionExact(int errorCode, string message) : base(message)
        {
            ErrorCode = errorCode;
        }
        
        public EXIExceptionExact(int errorCode, string message, Exception innerException) 
            : base(message, innerException)
        {
            ErrorCode = errorCode;
        }
        
        public static string GetErrorMessage(int errorCode)
        {
            return errorCode switch
            {
                EXIErrorCodesExact.EXI_OK => "No error",
                EXIErrorCodesExact.EXI_ERROR_UNEXPECTED_END_OF_STREAM => "Unexpected end of stream",
                EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_COOKIE => "EXI header cookie not supported",
                EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_OPTIONS => "EXI header options not supported",
                EXIErrorCodesExact.EXI_ERROR_UNKNOWN_EVENT => "Unknown EXI event",
                EXIErrorCodesExact.EXI_ERROR_OUT_OF_BYTE_BUFFER => "Output buffer overflow",
                EXIErrorCodesExact.EXI_ERROR_OUT_OF_BOUNDS => "Index out of bounds",
                EXIErrorCodesExact.EXI_ERROR_STRINGVALUES_NOT_SUPPORTED => "String values not supported",
                EXIErrorCodesExact.EXI_ERROR_NOT_IMPLEMENTED_YET => "Feature not implemented",
                _ => $"Unknown error code: {errorCode}"
            };
        }
    }
}