using System.Xml;
using EvaPosSrvDTO;

namespace EvaPosSrvResp
{
    /// <summary>
    /// Interface que define adaptadores para conversión de 
    /// mensaje XML en DTO.
    /// </summary>
    public interface IAdaptadorDTO
    {
        /// <summary>
        /// Identifica el mensaje al cual el adaptador genera DTO.
        /// Le referencia se usa por el servidor de mensajes para interpretar 
        /// el mensaje en un DTO a pasar a un comando.
        /// La referencia corresponde a un string contenido en el mensaje
        /// que permite identificar su tipo.
        /// </summary>
        public string Referencia { get; set; }

        /// <summary>
        /// Nombre del elemento xml con los datos para el DTO.
        /// El nombre del elemento se presenta en formato xpath para ubicar el nodo desde el 
        /// elemento raiz del mensaje.
        /// </summary>
        public string NombreElementoXMLDatos { get; set; }

        /// <summary>
        /// Inicializar DTO adecuado al mensaje.
        /// </summary>
        public DTOBase ObtieneDTO(int idSesion, TipoMensaje tipoMensaje, XmlElement docXml);

        /// <summary>
        /// Retorna una "shallow copy" del objeto.
        /// </summary>
        public IAdaptadorDTO CreaCopia();
    }

    /// <summary>
    /// Directorio de adaptadores DTO.
    /// Almacena instancia de adaptadores indexadas por su Referencia.
    /// Usado para identificar el adaptador que procesa un mensaje particular
    /// asociado por la propiedad Referencia del adaptador.
    /// </summary>
    public class DirectorioAdaptadoresDTO
    {
        private Dictionary<string, IAdaptadorDTO> dir = new Dictionary<string, IAdaptadorDTO>();
        
        /// <summary>
        /// Agrega un adaptador al directorio.
        /// </summary>
        public void AgregaAdaptador(IAdaptadorDTO adp)
        {
            dir.Add(adp.Referencia, adp);
        }

        /// <summary>
        /// Recupera una "shallow copy" del adaptador asociado al parámmetro.
        /// </summary>
        public IAdaptadorDTO ObtieneAdaptador(string referencia)
        {
            IAdaptadorDTO adp;
            try {
                adp = dir[referencia];
            } catch (KeyNotFoundException) 
            {
                throw new Exception($"No se encuentra adaptador con referencia '{referencia}'.");
            }
            return adp.CreaCopia();
        }
    }

    /// <summary>
    /// Trama, integra las partes que incluyen mensaje xml e información de control.
    /// </summary> 
    public struct TramaSCO
    {
        /// <summary>
        /// Longitud del mensaje. 
        /// Es la suma de la longitud de las cadenas de texto de la sección de encabezado 
        /// y de la sección te contenido xml.
        /// </summary>
        public UInt32 Longitud { get; set; }
        /// <summary>
        /// Id de la sesión.
        /// </summary>
        public Int32 IdSesion { get; set; }
        /// <summary>
        /// Tipo del mensaje, con valores predefinidos según el tipo de datos.
        /// </summary>
        public TipoMensaje TipoMensaje { get; set; }
        /// <summary>
        /// Texto del encabezado según los valores de tipo de mensaje e id de sesión.
        /// </summary>
        public string TextoEncabezado { get => $"soeps~Message-Type={TipoMensaje.Tipo}|Session-Id={IdSesion}|~"; }
        /// <summary>
        /// Texto del contenido xml del mensaje, con sus valores incluidos. 
        /// Debe ser inicializado por el que crea el objeto.
        /// </summary>
        public string TextoXML { get; set; }
        /// <summary>
        /// Objeto xml del mensaje, construido a partir del texto xml.
        /// </summary>
        public XmlDocument ContenidoXML
        {
            get
            {
                var doc = new XmlDocument();
                doc.LoadXml(TextoXML);
                return doc;
            }
        }

        /// <summary>
        /// Constructor genérico.
        /// </summary>
        public TramaSCO(UInt32 longitud, Int32 idSesion, TipoMensaje tipo, string textoXML)
        {
            Longitud = longitud;
            IdSesion = idSesion;
            TipoMensaje = tipo;
            TextoXML = textoXML;
        }

        /// <summary>
        /// Constructor usado para calcular Longitud del mensaje (encabezado + texto xml).
        /// </summary>
        public TramaSCO(Int32 idSesion, TipoMensaje tipo, string textoXML)
        {
            IdSesion = idSesion;
            TipoMensaje = tipo;
            TextoXML = textoXML;
            Longitud = Convert.ToUInt32(TextoEncabezado.Length + TextoXML.Length);
        }
    }

    /// <summary>
    /// Definición mensajes respuesta para el SCO.
    /// </summary>
    public abstract class Respuesta
    {
        /// <summary>
        /// String con el encabezado y contenido xml de mensajes de respuesta.
        /// </summary>
        public Int32 SessionId { get; private set; }
        public TipoMensaje MessageType { get; private set; }
        public abstract string TextoXML { get; }
        public TramaSCO TramaSCO
        {
            get => new TramaSCO(SessionId, MessageType, TextoXML);
        }

        public Respuesta(int sessionId, TipoMensaje tipo)
        {
            SessionId = sessionId;
            MessageType = tipo;
        }
    }

    /// <summary>
    /// Respuesta en modo texto genérico. Usada para pruebas básicas de comunicación.
    /// </summary>
    public class RespuestaGenerica : Respuesta
    {
        private string _mensaje;

        /// <summary>
        /// Constructor mensaje básico.
        /// </summary>
        public RespuestaGenerica(string mensaje) : base(1, TipoMensaje.Resp)
        {
            _mensaje = mensaje;
        }

        /// <summary>
        /// Retorna mensaje.
        /// </summary>
        public override string TextoXML { get => _mensaje; }
    }

    /// <summary>
    /// Colección de respuestas.
    /// </summary>
    public class Respuestas : List<Respuesta> { }
}