using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ScaBox30.Controller
{
    /// <summary>
    /// Reemplazo del ReaderAccessor del SDK de Keyence
    /// Implementa conexión TCP/IP directa para SR-X300W
    /// </summary>
    public class TcpReaderAccessor : IDisposable
    {
        private TcpClient client;
        private NetworkStream stream;
        private bool isConnected = false;
        private CancellableBackgroundWorker receiverWorker;
        private Action<byte[]> onDataReceived;

        // Propiedades compatibles con el SDK original
        public string IpAddress { get; set; }
        public int Port { get; set; } = 9004; // Puerto TCP de comandos SR-X300W
        public ErrorCode LastErrorInfo { get; private set; } = ErrorCode.None;
        public int ReceiveTimeout { get; set; } = 30000; // 5 segundos

        public TcpReaderAccessor()
        {
            receiverWorker = new CancellableBackgroundWorker();
        }

        /// <summary>
        /// Conecta al sensor SR-X300W y registra callback para datos recibidos
        /// Compatible con: m_reader.Connect((data) => { ... })
        /// </summary>
        public bool Connect(Action<byte[]> dataReceivedCallback)
        {
            if (string.IsNullOrEmpty(IpAddress))
            {
                LastErrorInfo = ErrorCode.InvalidParameter;
                Console.WriteLine("ERROR: IpAddress no configurada");
                return false;
            }

            try
            {
                Console.WriteLine($"[TCP] Conectando a {IpAddress}:{Port}...");

                // Crear y conectar TcpClient
                client = new TcpClient();
                client.Connect(IpAddress, Port);
                stream = client.GetStream();
                stream.ReadTimeout = ReceiveTimeout;

                isConnected = true;
                onDataReceived = dataReceivedCallback;
                LastErrorInfo = ErrorCode.None;

                Console.WriteLine($"[TCP] Conectado exitosamente a {IpAddress}:{Port}");

                // Iniciar listener asíncrono (similar al SDK)
                //StartAsyncReceiver();

                return true;
            }
            catch (SocketException ex)
            {
                Console.WriteLine($"[TCP] Error de conexión: {ex.Message}");
                LastErrorInfo = ErrorCode.OpenFailed;
                isConnected = false;
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[TCP] Error inesperado: {ex.Message}");
                LastErrorInfo = ErrorCode.OpenFailed;
                isConnected = false;
                return false;
            }
        }

        /// <summary>
        /// Ejecuta un comando y espera respuesta COMPLETA usando async/await
        /// BASADO EN LA APP DE PRUEBA QUE FUNCIONA CORRECTAMENTE
        /// </summary>
        public string ExecCommand(string command)
        {
            if (!isConnected || stream == null)
            {
                LastErrorInfo = ErrorCode.Closed;
                Console.WriteLine($"[TCP] ERROR: No conectado al ejecutar '{command}'");
                return string.Empty;
            }

            try
            {
                // Agregar terminador de línea si no lo tiene
                string fullCommand = command.EndsWith("\r") ? command : command + "\r";
                byte[] cmdBytes = Encoding.ASCII.GetBytes(fullCommand);

                Console.WriteLine($"[TCP] Enviando comando: '{command}' ({cmdBytes.Length} bytes)");
                Console.WriteLine($"[TCP] Bytes a enviar: [{BitConverter.ToString(cmdBytes)}]");

                // Enviar comando (SÍNCRONO como en tu app original)
                stream.Write(cmdBytes, 0, cmdBytes.Length);
                stream.Flush();
                Console.WriteLine("[TCP] Bytes escritos ✓");

                // **CAMBIO CRÍTICO**: Usar el patrón de la app de prueba
                Console.WriteLine($"[TCP] Leyendo respuesta (timeout {ReceiveTimeout}ms)...");

                byte[] buffer = new byte[8192];
                StringBuilder responseBuilder = new StringBuilder();

                // Crear task de lectura asíncrona
                Task<int> readTask = stream.ReadAsync(buffer, 0, buffer.Length);

                // Esperar con timeout (igual que la app de prueba)
                if (readTask.Wait(ReceiveTimeout))
                {
                    int bytesRead = readTask.Result;
                    Console.WriteLine($"[TCP] Primera lectura: {bytesRead} bytes");

                    if (bytesRead > 0)
                    {
                        string chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                        responseBuilder.Append(chunk);

                        // Si contiene \r, ya terminó
                        if (chunk.Contains("\r"))
                        {
                            string response = responseBuilder.ToString();
                            Console.WriteLine($"[TCP] Respuesta completa en primer paquete: {response.Length} bytes");
                            Console.WriteLine($"[TCP] Respuesta HEX: [{BitConverter.ToString(buffer, 0, bytesRead)}]");

                            string preview = response.Length > 100
                                ? response.Substring(0, 50) + "..." + response.Substring(response.Length - 50)
                                : response.TrimEnd('\r', '\n');
                            Console.WriteLine($"[TCP] Preview: '{preview}'");

                            LastErrorInfo = ErrorCode.None;
                            return response;
                        }

                        // Si NO terminó, leer más paquetes
                        Console.WriteLine("[TCP] Respuesta incompleta, leyendo más paquetes...");
                        DateTime startTime = DateTime.Now;
                        int packetsRead = 1;

                        while ((DateTime.Now - startTime).TotalMilliseconds < ReceiveTimeout)
                        {
                            if (stream.DataAvailable)
                            {
                                readTask = stream.ReadAsync(buffer, 0, buffer.Length);
                                if (readTask.Wait(5000)) // 5s para paquetes adicionales
                                {
                                    bytesRead = readTask.Result;
                                    if (bytesRead > 0)
                                    {
                                        packetsRead++;
                                        chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                                        responseBuilder.Append(chunk);
                                        Console.WriteLine($"[TCP] Paquete {packetsRead}: {bytesRead} bytes");

                                        if (chunk.Contains("\r"))
                                        {
                                            break; // Terminador encontrado
                                        }
                                    }
                                }
                            }
                            else
                            {
                                System.Threading.Thread.Sleep(50); // Esperar más datos
                            }
                        }

                        string finalResponse = responseBuilder.ToString();
                        Console.WriteLine($"[TCP] Respuesta COMPLETA: {finalResponse.Length} bytes en {packetsRead} paquetes");

                        string finalPreview = finalResponse.Length > 100
                            ? finalResponse.Substring(0, 50) + "..." + finalResponse.Substring(finalResponse.Length - 50)
                            : finalResponse.TrimEnd('\r', '\n');
                        Console.WriteLine($"[TCP] Preview final: '{finalPreview}'");

                        LastErrorInfo = ErrorCode.None;
                        return finalResponse;
                    }
                    else
                    {
                        Console.WriteLine("[TCP] Primera lectura devolvió 0 bytes");
                        LastErrorInfo = ErrorCode.None;
                        return string.Empty;
                    }
                }
                else
                {
                    Console.WriteLine($"[TCP] TIMEOUT esperando respuesta de '{command}'");
                    LastErrorInfo = ErrorCode.Timeout;
                    return string.Empty;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[TCP] Error en ExecCommand '{command}': {ex.Message}");
                Console.WriteLine($"[TCP] StackTrace: {ex.StackTrace}");
                LastErrorInfo = ErrorCode.SendFailed;
                return string.Empty;
            }
        }

        /// <summary>
        /// Inicia un worker en background para recibir datos asíncronos
        /// Esto simula el comportamiento del SDK cuando hay lecturas continuas
        /// </summary>
        private void StartAsyncReceiver()
        {
            receiverWorker.DoWork += (sender, e) =>
            {
                byte[] buffer = new byte[8192];

                while (!receiverWorker.CancellationPending && isConnected)
                {
                    try
                    {
                        if (stream != null && stream.DataAvailable)
                        {
                            int bytesRead = stream.Read(buffer, 0, buffer.Length);
                            if (bytesRead > 0)
                            {
                                byte[] data = new byte[bytesRead];
                                Array.Copy(buffer, data, bytesRead);

                                // Llamar al callback registrado
                                onDataReceived?.Invoke(data);
                            }
                        }
                        else
                        {
                            Thread.Sleep(50); // Pequeña pausa para no saturar CPU
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"[TCP] Error en receiver: {ex.Message}");
                        LastErrorInfo = ErrorCode.BeginReceiveFailed;
                        break;
                    }
                }
            };

            receiverWorker.RunWorkerAsync();
        }

        /// <summary>
        /// Cierra la conexión TCP
        /// </summary>
        public void Disconnect()
        {
            try
            {
                Console.WriteLine("[TCP] Desconectando...");

                isConnected = false;

                // Detener receiver
                if (receiverWorker != null && receiverWorker.IsBusy)
                {
                    receiverWorker.CancelAsync();
                    Thread.Sleep(100); // Dar tiempo para que termine
                }

                stream?.Close();
                client?.Close();

                LastErrorInfo = ErrorCode.None;
                Console.WriteLine("[TCP] Desconectado correctamente");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[TCP] Error al desconectar: {ex.Message}");
            }
        }

        public void Dispose()
        {
            Disconnect();
            stream?.Dispose();
            client?.Dispose();
            receiverWorker?.Dispose();
        }
    }

    /// <summary>
    /// Enumeración de códigos de error compatible con el SDK
    /// </summary>
    public enum ErrorCode
    {
        None = 0,
        AlreadyOpen = 1,
        Closed = 2,
        OpenFailed = 3,
        Timeout = 4,
        SendFailed = 5,
        BeginReceiveFailed = 6,
        InvalidParameter = 7
    }

    /// <summary>
    /// BackgroundWorker con soporte para cancelación
    /// </summary>
    public class CancellableBackgroundWorker : System.ComponentModel.BackgroundWorker
    {
        public CancellableBackgroundWorker()
        {
            WorkerSupportsCancellation = true;
        }
    }
}