/*
 * Copyright (C) 2007-2024 C# Port
 * Original Copyright (C) 2007-2018 Siemens AG
 *
 * Exact EXI Types - Byte-compatible port of OpenV2G EXI implementation
 */

using System;

namespace V2GDecoderNet.EXI
{
    /// <summary>
    /// Exact EXI constants matching OpenV2G C implementation
    /// </summary>
    public static class EXIConstantsExact
    {
        // Core EXI constants from EXITypes.h
        public const int BITS_IN_BYTE = 8;
        public const int EXI_ELEMENT_STACK_SIZE = 24;
        public const int UINT_MAX_VALUE = 65535;
        
        // EXI Date-Time constants
        public const int DATETIME_YEAR_OFFSET = 2000;
        public const int DATETIME_NUMBER_BITS_MONTHDAY = 9;
        public const int DATETIME_NUMBER_BITS_TIME = 17;
        public const int DATETIME_NUMBER_BITS_TIMEZONE = 11;
        public const int DATETIME_MONTH_MULTIPLICATOR = 32;
        public const int DATETIME_TIMEZONE_OFFSET_IN_MINUTES = 896;
        
        // EXI Float special values
        public const int FLOAT_EXPONENT_SPECIAL_VALUES = -16384;
        public const long FLOAT_MANTISSA_INFINITY = 1;
        public const long FLOAT_MANTISSA_MINUS_INFINITY = -1;
        public const long FLOAT_MANTISSA_NOT_A_NUMBER = 0;
        
        // Buffer and stream configuration
        public const int BUFFER_SIZE = 4096;
        
        // EXI Header byte - always 0x80 for simple headers
        public const byte EXI_HEADER_SIMPLE = 0x80;
        
        // Stream type configuration
        public const int EXI_STREAM_BYTE_ARRAY = 0;
        public const int EXI_STREAM_FILE = 1;
    }

    /// <summary>
    /// EXI Events enumeration - exact match to C implementation
    /// </summary>
    public enum EXIEventExact
    {
        START_DOCUMENT = 0,
        END_DOCUMENT = 1,
        START_ELEMENT = 2,
        START_ELEMENT_NS = 3,
        START_ELEMENT_GENERIC = 4,
        START_ELEMENT_GENERIC_UNDECLARED = 5,
        END_ELEMENT = 6,
        END_ELEMENT_UNDECLARED = 7,
        CHARACTERS = 8,
        CHARACTERS_GENERIC = 9,
        ATTRIBUTE = 10,
        ATTRIBUTE_NS = 11,
        ATTRIBUTE_GENERIC = 12,
        ATTRIBUTE_GENERIC_UNDECLARED = 13,
        ATTRIBUTE_XSI_TYPE = 14,
        ATTRIBUTE_XSI_NIL = 15,
        SELF_CONTAINED = 16,
        ENTITY_REFERENCE = 17,
        COMMENT = 18,
        PROCESSING_INSTRUCTION = 19,
        DOCTYPE_DECLARATION = 20,
        NAMESPACE_DECLARATION = 21
    }

    /// <summary>
    /// EXI Integer types - exact match to C implementation
    /// </summary>
    public enum EXIIntegerTypeExact
    {
        UNSIGNED_INTEGER_8 = 0,
        UNSIGNED_INTEGER_16 = 1,
        UNSIGNED_INTEGER_32 = 2,
        UNSIGNED_INTEGER_64 = 3,
        INTEGER_8 = 4,
        INTEGER_16 = 5,
        INTEGER_32 = 6,
        INTEGER_64 = 7,
        UNSIGNED_INTEGER_BIG = 8
    }

    /// <summary>
    /// EXI Stream configuration - exact match to C bitstream_t
    /// </summary>
    public class BitstreamExact
    {
        // Core buffer state
        public byte[] Data { get; set; }
        public int Size { get; set; }
        public int Position { get; set; }
        
        // Bit-level state - exact match to C implementation
        public byte Buffer { get; set; }    // Current bit buffer
        public byte Capacity { get; set; }  // Remaining bits in buffer
        
        public BitstreamExact(byte[] data)
        {
            if (data == null) throw new ArgumentNullException(nameof(data));
            Data = data;
            Size = data.Length;
            Position = 0;
            Buffer = 0;
            Capacity = 0; // 0 = empty for input, 8 = empty for output
        }
        
        public BitstreamExact(int size)
        {
            Data = new byte[size];
            Size = size;
            Position = 0;
            Buffer = 0;
            Capacity = 8; // Output stream starts with empty buffer (8 available bits)
        }
        
        public void Reset()
        {
            Position = 0;
            Buffer = 0;
            Capacity = 0;
        }
        
        public byte[] ToArray()
        {
            int resultSize = Position;
            if (Capacity < 8) resultSize++; // Include partial buffer
            
            var result = new byte[resultSize];
            Array.Copy(Data, result, Position);
            
            // Include partial buffer if any bits written
            if (Capacity < 8 && resultSize > Position)
            {
                result[Position] = (byte)(Buffer << Capacity);
            }
            
            return result;
        }
    }

    /// <summary>
    /// EXI Header structure - exact match to C exi_header_t
    /// </summary>
    public class EXIHeaderExact
    {
        public bool HasCookie { get; set; }
        public byte FormatVersion { get; set; }
        public bool PreserveComments { get; set; }
        public bool PreservePIs { get; set; }
        public bool PreserveDTD { get; set; }
        public bool PreservePrefixes { get; set; }
        
        public EXIHeaderExact()
        {
            HasCookie = false;
            FormatVersion = 0;
            PreserveComments = false;
            PreservePIs = false;
            PreserveDTD = false;
            PreservePrefixes = false;
        }
    }

    /// <summary>
    /// EXI Document structure - matching C implementation
    /// </summary>
    public class EXIDocumentExact
    {
        public EXIHeaderExact Header { get; set; }
        public BitstreamExact Body { get; set; }
        
        public EXIDocumentExact()
        {
            Header = new EXIHeaderExact();
        }
    }
    
    /// <summary>
    /// EXI Grammar state structure
    /// </summary>
    public class EXIGrammarState
    {
        public int GrammarID { get; set; }
        public int EventCode { get; set; }
        public int ElementStackSize { get; set; }
        
        public EXIGrammarState()
        {
            GrammarID = 0;
            EventCode = 0;
            ElementStackSize = 0;
        }
    }
}