using System.Text;
using System.Xml.Linq;

namespace Inspectron.Epson.SOAP
{
    public class SoapEpsonPrinter
    {
        private readonly string url;
        private static readonly HttpClient httpClient = new HttpClient(new HttpClientHandler
        {
            ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
        });

        private const string EPSON_XML_HEADER =
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
            "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
            "<s:Header>" +
            "<parameter xmlns=\"http://www.epson-pos.com/schemas/2011/03/epos-print\" />" +
            "</s:Header>" +
            "<s:Body>";

        public SoapEpsonPrinter(string ip)
        {
            url = $"https://{ip}/cgi-bin/epos/service.cgi?devid=local_printer&timeout=10000";
        }

        public async Task<XElement> Send(SoapEpsonPrint print)
        {
            var data = print.ToString();
            Console.WriteLine(data.ToString());
            var xml = await Request(data);
            Console.WriteLine(xml.ToString());
            var response = xml.Descendants(XName.Get("response", "http://www.epson-pos.com/schemas/2011/03/epos-print"))
                              .FirstOrDefault();

            if (response == null)
            {
                throw new Exception("INVALID_RESPONSE");
            }

            var successAttr = response.Attribute("success");
            var success = successAttr?.Value == "true";

            var codeAttr = response.Attribute("code");
            var code = codeAttr?.Value ?? "";

            if (!success)
            {
                throw new Exception(code);
            }
            
            return response;
        }

        public async Task<PrinterStatus> GetStatus(SoapEpsonPrint print)
        {
            var data = print.ToString();
            var xml = await Request(data);

            Console.WriteLine(xml.ToString());

            var response = xml.Descendants(XName.Get("response", "http://www.epson-pos.com/schemas/2011/03/epos-print"))
                              .FirstOrDefault();

            if (response == null)
            {
                throw new Exception("INVALID_RESPONSE");
            }

            var statusAttr = response.Attribute("status");
            var status = int.Parse(statusAttr?.Value ?? "0");

            var statuses = new PrinterStatus
            {
                IsResponsive = true,
                DrawerIsOpen = true,
                CoverIsOpen = false,
                IsOffline = false,
                PaperNearEmpty = false,
                PaperEmpty = false
            };

            if ((status & 0x00000001) != 0)
            {
                statuses.IsResponsive = false;
            }

            if ((status & 0x00000004) != 0)
            {
                statuses.DrawerIsOpen = false;
            }

            if ((status & 0x00000008) != 0)
            {
                statuses.IsOffline = true;
            }

            if ((status & 0x00000020) != 0)
            {
                statuses.CoverIsOpen = true;
            }

            if ((status & 0x00020000) != 0)
            {
                statuses.PaperNearEmpty = true;
            }

            if ((status & 0x00080000) != 0)
            {
                statuses.PaperEmpty = true;
            }

            return statuses;
        }

        private async Task<XDocument> Request(string data)
        {
            var body = $"{EPSON_XML_HEADER}{data}</s:Body></s:Envelope>";

            var content = new StringContent(body, Encoding.UTF8, "text/xml");

            using (var request = new HttpRequestMessage(HttpMethod.Post, url))
            {
                request.Content = content;
                //request.Headers.Add("If-Modified-Since", "Thu, 01 Jun 1970 00:00:00 GMT");
                //request.Headers.Add("SOAPAction", "\"\"");

                var response = await httpClient.SendAsync(request);
                response.EnsureSuccessStatusCode();

                var responseText = await response.Content.ReadAsStringAsync();
                return XDocument.Parse(responseText);
            }
        }
    }

    public class PrinterStatus
    {
        public bool IsResponsive { get; set; }
        public bool DrawerIsOpen { get; set; }
        public bool CoverIsOpen { get; set; }
        public bool IsOffline { get; set; }
        public bool PaperNearEmpty { get; set; }
        public bool PaperEmpty { get; set; }

        public override string ToString()
        {
            return $"IsResponsive: {IsResponsive}, DrawerIsOpen: {DrawerIsOpen}, CoverIsOpen: {CoverIsOpen}, IsOffline: {IsOffline}, PaperNearEmpty: {PaperNearEmpty}, PaperEmpty: {PaperEmpty}";

        }
    }
}
