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

namespace V2GDecoderNet
{
    class Program
    {
        private const int BUFFER_SIZE = 4096;
        
        // Network protocol patterns and definitions
        private const ushort ETH_TYPE_IPV6 = 0x86DD;           // Ethernet Type: IPv6
        private const byte IPV6_NEXT_HEADER_TCP = 0x06;        // IPv6 Next Header: TCP
        private const ushort TCP_V2G_PORT = 15118;             // V2G communication port
        
        // V2G Transfer Protocol patterns and definitions
        private const byte V2G_PROTOCOL_VERSION = 0x01;        // Protocol Version
        private const byte V2G_INV_PROTOCOL_VERSION = 0xFE;    // Inverse Protocol Version
        private const ushort V2G_PAYLOAD_ISO_DIN_SAP = 0x8001; // ISO 15118-2/DIN/SAP payload type
        private const ushort V2G_PAYLOAD_ISO2 = 0x8002;        // ISO 15118-20 payload type
        private const ushort EXI_START_PATTERN = 0x8098;       // EXI document start pattern


        static int Main(string[] args)
        {
            bool xmlMode = false;
            bool encodeMode = false;
            string filename = null;
            
            if (args.Length == 1)
            {
                filename = args[0];
            }
            else if (args.Length == 2 && args[0] == "-decode")
            {
                xmlMode = true;
                filename = args[1];
            }
            else if (args.Length == 2 && args[0] == "-encode")
            {
                encodeMode = true;
                filename = args[1];
            }
            else if (args.Length == 1 && args[0] == "-decode")
            {
                // stdin에서 EXI 읽어서 XML로 변환
                return HandleStdinDecode();
            }
            else if (args.Length == 1 && args[0] == "-encode")
            {
                // stdin에서 XML 읽어서 EXI로 변환
                return HandleStdinEncode();
            }
            else
            {             
                Console.Error.WriteLine("Usage: V2GDecoderNet [-decode|-encode] input_file");
                Console.Error.WriteLine("       V2GDecoderNet -encode          (read XML from stdin)");
                Console.Error.WriteLine("       V2GDecoderNet -decode          (read hex string from stdin)");
                Console.Error.WriteLine("Enhanced EXI viewer with XML conversion capabilities");
                Console.Error.WriteLine("  -decode      Convert EXI to Wireshark-style XML format");
                Console.Error.WriteLine("  -decode      Read hex string from stdin (echo hex | V2GDecoderNet -decode)");
                Console.Error.WriteLine("  -encode      Convert XML to EXI format");
                Console.Error.WriteLine("  -encode      Read XML from stdin (type file.xml | V2GDecoderNet -encode)");
                Console.Error.WriteLine("  (default)    Analyze EXI with detailed output");
                Console.Error.WriteLine("");
                Console.Error.WriteLine("Contact: tindevil82@gmail.com");
                return -1;
            }
                        
            if (!File.Exists(filename))
            {
                Console.Error.WriteLine($"Error reading file: {filename}");
                return -1;
            }

            try
            {
                if (encodeMode)
                {
                    return HandleEncodeMode(filename);
                }
                else
                {
                    return HandleDecodeOrAnalyzeMode(filename, xmlMode);
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error processing file: {ex.Message}");
                return -1;
            }
        }

        private static int HandleEncodeMode(string filename)
        {
            try
            {
                // Read XML file
                string xmlContent = File.ReadAllText(filename, Encoding.UTF8);
                
                // Parse and encode XML to EXI
                var exiData = V2GMessageProcessor.EncodeXmlToExi(xmlContent);
                
                if (exiData == null || exiData.Length == 0)
                {
                    Console.Error.WriteLine("Error encoding XML to EXI");
                    return -1;
                }
                
                // Check if output is redirected
                bool isRedirected = Console.IsOutputRedirected;
                
                if (isRedirected)
                {
                    // Binary output for redirection (file output)
                    using (var stdout = Console.OpenStandardOutput())
                    {
                        stdout.Write(exiData, 0, exiData.Length);
                        stdout.Flush();
                    }
                }
                else
                {
                    // Hex string output for console display
                    Console.Write(BitConverter.ToString(exiData).Replace("-", ""));
                }
                
                return 0;
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error encoding to EXI: {ex.Message}");
                return -1;
            }
        }

        private static int HandleDecodeOrAnalyzeMode(string filename, bool xmlMode)
        {
            try
            {
                // Read EXI file
                byte[] buffer = File.ReadAllBytes(filename);
                
                if (!xmlMode)
                {
                    // Analysis mode - show detailed information like C version
                    Console.WriteLine($"File: {filename} ({buffer.Length} bytes)");
                    Console.Write("Raw hex data: ");
                    
                    int displayLength = Math.Min(buffer.Length, 32);
                    for (int i = 0; i < displayLength; i++)
                    {
                        Console.Write($"{buffer[i]:X2} ");
                    }
                    if (buffer.Length > 32) Console.Write("...");
                    Console.WriteLine("\n");
                    
                    // Analyze data structure
                    AnalyzeDataStructure(buffer);
                }
                
                // Extract EXI body from V2G Transfer Protocol data
                byte[] exiBuffer = ExtractExiBody(buffer);
                
                if (exiBuffer.Length != buffer.Length && !xmlMode)
                {
                    Console.WriteLine($"\n=== V2G Transfer Protocol Analysis ===");
                    Console.WriteLine($"Original size: {buffer.Length} bytes");
                    Console.WriteLine($"EXI body size: {exiBuffer.Length} bytes");
                    Console.WriteLine($"Stripped V2GTP header: {buffer.Length - exiBuffer.Length} bytes");
                }
                
                // Decode EXI message
                DecodeResult result;
                if (xmlMode)
                {
                    // Suppress debug output for XML-only mode
                    using (var sw = new StringWriter())
                    {
                        var originalOut = Console.Out;
                        Console.SetOut(sw);
                        try
                        {
                            result = V2GMessageProcessor.DecodeExiMessage(exiBuffer);
                        }
                        finally
                        {
                            Console.SetOut(originalOut);
                        }
                    }
                }
                else
                {
                    result = V2GMessageProcessor.DecodeExiMessage(exiBuffer);
                }
                
                if (result.Success)
                {
                    if (xmlMode)
                    {
                        // XML decode mode - output clean XML only
                        Console.Write(result.XmlOutput);
                    }
                    else
                    {
                        // Analysis mode - show detailed analysis
                        Console.WriteLine(result.AnalysisOutput);
                        Console.WriteLine(result.XmlOutput); // Also show XML in analysis mode
                    }
                    return 0;
                }
                else
                {
                    Console.Error.WriteLine($"Error decoding EXI: {result.ErrorMessage}");
                    return -1;
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error processing file: {ex.Message}");
                return -1;
            }
        }

        private static void AnalyzeDataStructure(byte[] buffer)
        {
            Console.WriteLine("=== Data Structure Analysis ===");
            Console.WriteLine($"Total size: {buffer.Length} bytes");
            
            if (buffer.Length >= 4)
            {
                uint firstFourBytes = (uint)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]);
                Console.WriteLine($"First 4 bytes: 0x{firstFourBytes:X8}");
            }
            
            // Check for EXI start pattern
            for (int i = 0; i <= buffer.Length - 2; i++)
            {
                ushort pattern = (ushort)((buffer[i] << 8) | buffer[i + 1]);
                if (pattern == EXI_START_PATTERN)
                {
                    Console.WriteLine($"EXI start pattern (0x{EXI_START_PATTERN:X4}) found at offset: {i}");
                    Console.WriteLine($"EXI payload size: {buffer.Length - i} bytes");
                    break;
                }
            }
            
            // Determine protocol type
            if (buffer.Length >= 8 && buffer[0] == V2G_PROTOCOL_VERSION && buffer[1] == V2G_INV_PROTOCOL_VERSION)
            {
                Console.WriteLine("Protocol: V2G Transfer Protocol detected");
            }
            else if (buffer.Length >= 2 && ((buffer[0] << 8) | buffer[1]) == EXI_START_PATTERN)
            {
                Console.WriteLine("Protocol: Direct EXI format");
            }
            else
            {
                Console.WriteLine("Protocol: Unknown or Direct EXI");
            }
            
            Console.WriteLine();
        }

        private static byte[] ExtractExiBody(byte[] inputData)
        {
            if (inputData.Length < 8)
            {
                // Too small for V2G TP header, assume it's pure EXI
                return inputData;
            }
            
            // Check for V2GTP header: Version(1) + Inv.Version(1) + PayloadType(2) + PayloadLength(4)
            if (inputData[0] == V2G_PROTOCOL_VERSION && inputData[1] == V2G_INV_PROTOCOL_VERSION)
            {
                // Extract payload type and length
                ushort payloadType = (ushort)((inputData[2] << 8) | inputData[3]);
                uint payloadLength = (uint)((inputData[4] << 24) | (inputData[5] << 16) | (inputData[6] << 8) | inputData[7]);
                
                if (payloadType == V2G_PAYLOAD_ISO_DIN_SAP || payloadType == V2G_PAYLOAD_ISO2)
                {
                    if (8 + payloadLength <= inputData.Length)
                    {
                        byte[] result = new byte[payloadLength];
                        Array.Copy(inputData, 8, result, 0, (int)payloadLength);
                        return result;
                    }
                }
            }
            
            // Not V2GTP format, return as-is
            return inputData;
        }

        private static int HandleStdinDecode()
        {
            try
            {
                // Read hex string from stdin (like VC2022)
                string hexInput = Console.In.ReadToEnd().Trim();
                
                // Remove spaces and convert hex to bytes
                hexInput = hexInput.Replace(" ", "").Replace("\n", "").Replace("\r", "");
                if (hexInput.Length % 2 != 0)
                {
                    Console.Error.WriteLine("Error: Invalid hex string length");
                    return -1;
                }
                
                byte[] exiData = new byte[hexInput.Length / 2];
                for (int i = 0; i < exiData.Length; i++)
                {
                    exiData[i] = Convert.ToByte(hexInput.Substring(i * 2, 2), 16);
                }
                
                // Decode and output XML
                var result = V2GMessageProcessor.DecodeExiMessage(exiData);
                if (result.Success)
                {
                    Console.Write(result.XmlOutput);
                    return 0;
                }
                else
                {
                    Console.Error.WriteLine($"Error: {result.ErrorMessage}");
                    return -1;
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error reading from stdin: {ex.Message}");
                return -1;
            }
        }

        private static int HandleStdinEncode()
        {
            try
            {
                // Read XML from stdin (like VC2022)
                string xmlInput = Console.In.ReadToEnd();
                
                // Encode XML to EXI
                var exiData = V2GMessageProcessor.EncodeXmlToExi(xmlInput);
                
                if (exiData == null || exiData.Length == 0)
                {
                    Console.Error.WriteLine("Error encoding XML to EXI");
                    return -1;
                }
                
                // Check if output is redirected
                bool isRedirected = Console.IsOutputRedirected;
                
                if (isRedirected)
                {
                    // Binary output for redirection
                    using (var stdout = Console.OpenStandardOutput())
                    {
                        stdout.Write(exiData, 0, exiData.Length);
                        stdout.Flush();
                    }
                }
                else
                {
                    // Hex string output for console display
                    Console.Write(BitConverter.ToString(exiData).Replace("-", ""));
                }
                
                return 0;
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"Error reading from stdin: {ex.Message}");
                return -1;
            }
        }
    }
}