using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using DevExpress.Xpo.Exceptions;
using DevExpress.CodeParser;
//using System.Windows.Forms;

namespace DxBlazorApplication2.AS400Kommunikation
{
    //DB2Umstellung

    public class AS400SocketHandler
    {
        private TcpClient tcpClient = null;     //Client..was sonst
        protected NetworkStream inOut = null;     //Stream zu lesen ODER schreiben
        protected String buffer = "";             //Puffer zum Lesen
        private const String crlf = "\r\n";     //Linefeed                 
        private bool ladeJetztDS = false;       //Schalter -> Ab jetztr werden DS geladen und keine Felder mehr
        private Encoding encoding = Encoding.GetEncoding(1252);   //ANSI
        protected String lambertzPortalSessionID = null;
        private bool activateProtocol = false;
        private StreamWriter logWriter;
        public bool Connected { get; private set; }

        /// <summary>
        /// Der Konstruktor besorgt sich den Stream aus dem übergebenen Client!
        /// </summary>
        /// <param name="client"></param>
        public AS400SocketHandler(TcpClient client)
        {
            Connected = true;
            this.tcpClient = client;
            this.inOut = client.GetStream();
        }

        /// <summary>
        /// Der Konstruktor besorgt sich den Stream aus dem übergebenen Client!
        /// </summary>
        /// <param name="client"></param>
        public AS400SocketHandler(string sessionID)
            : this(new TcpClient("172.16.4.1", 5000))
        {
            this.lambertzPortalSessionID = sessionID;
        }

        /// <summary>
        /// Liest eine durch \r\n gekennzeichnete Zeile aus dem Stream und gibt diese zurück.
        /// Zuviel gelesene zeichen werden gepuffert ..
        /// </summary>
        /// <returns></returns>
        public virtual String ReadLine()
        {
            String result = "";
            try
            {
                Byte[] readBuffer = new Byte[1026];
                int len = 0;
                while (buffer.IndexOf(crlf) == -1)
                {
                    //this.tcpClient.Client.Poll(-1, SelectMode.SelectRead)) //bringt nix
                    len = this.inOut.Read(readBuffer, 0, 1026);
                    if(len == 0)
                    {
                        Connected = false;
                        throw new Exception("Verbindung verloren");
                    }
                    buffer = buffer + encoding.GetString(readBuffer).Substring(0, len);
                }
                if (ladeJetztDS && inOut.ReadTimeout == -1)
                {
                    inOut.ReadTimeout = 10000;
                }
                result = buffer.Substring(0, buffer.IndexOf(crlf));
                buffer = buffer.Substring(result.Length + 2);
                if (activateProtocol)
                {
                    logWriter.WriteLine("<-" + result);
                }
            }
            catch (Exception ex)
            {
                result = "FE" + ex.Message;
            }
            return result;
        }

        /// <summary>
        /// Schreibt einen Text an den Server und hängt \r\n an.
        /// </summary>
        /// <param name="text"></param>
        public void WriteLine(String text)
        {
            text = text.Trim() + crlf;
            Byte[] bytes = encoding.GetBytes(text);
            try
            {
                if (activateProtocol)
                {
                    logWriter.Write("->" + text);
                }
                this.inOut.Write(bytes, 0, bytes.Length);
            }
            catch (Exception)
            {
                //Do nothing ...
            }
        }

        /// <summary>
        /// Löscht den Buffer (Warum auch immer)
        /// </summary>
        public void ClearBuffer()
        {
            this.buffer = "";
        }

        public Encoding Encoding
        {
            get { return encoding; }
            set { encoding = value; }
        }

        /// <summary>
        /// Zerlegt eine gelesene Zeile anhand der field Informationen in Einzelfelder
        /// Es sind immer Strings, nur die Formatierung ändert sich.
        /// </summary>
        /// <param name="line"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public AS400PreviewContainer ToPreviewContainer(String line, List<AS400SocketField> fields)
        {
            AS400PreviewContainer result = new AS400PreviewContainer();
            String stringValue = null;
            foreach (AS400SocketField field in fields)
            {
                if (line.Length <= field.Start)
                {
                    result.AddNextField("");
                }
                else
                {
                    if (line.Length < (field.Start + field.Length))
                    {
                        stringValue = (line.Substring(field.Start)); //Geändert von trim()
                    }
                    else
                    {
                        stringValue = (line.Substring(field.Start, field.Length)); // Geändert von trim()
                    }

                    ///Entscheidung über die Formatierung
                    switch (field.Type)
                    {
                        case "S":
                            if (!stringValue.Trim().Equals(""))
                            {
                                stringValue = negativeCheck(stringValue);
                                if (field.InternalDecimalPlaces != 0)
                                {
                                    //Double
                                    double d = Convert.ToDouble(stringValue);
                                    double devider = Math.Pow(10, field.InternalDecimalPlaces);
                                    d = d / devider;
                                    StringBuilder format = new StringBuilder("#,###.");
                                    for (int i = 0; i < field.InternalDecimalPlaces; i++)
                                    {
                                        format.Append("0");
                                    }
                                    result.AddNextField(d.ToString(format.ToString()));
                                }
                                else
                                {
                                    //Integer -> geändert zu long wegen sehr großer Zahlen!!!
                                    long i = Convert.ToInt64(stringValue);
                                    result.AddNextField(i.ToString());
                                }
                            }
                            else
                            {
                                //Leere Felder
                                result.AddNextField("0");
                            }
                            break;
                        case "T":
                            //Zeit
                            if (!stringValue.Trim().Equals(""))
                            {
                                int hour = Convert.ToInt32(stringValue.Substring(0, 2));
                                int minute = Convert.ToInt32(stringValue.Substring(3, 2));
                                int second = Convert.ToInt32(stringValue.Substring(6, 2));

                                result.AddNextField(hour.ToString("00") + ":" + minute.ToString("00") + ":" + second.ToString("00"));
                            }
                            else
                            {
                                //Leere Felder
                                result.AddNextField("");
                            }
                            break;
                        case "L":
                            //Datum
                            String temp = "";
                            if (!stringValue.Trim().Equals(""))
                            {
                                int day = Convert.ToInt32(stringValue.Substring(0, 2));
                                int month = Convert.ToInt32(stringValue.Substring(3, 2));
                                int year = Convert.ToInt32(stringValue.Substring(6, 4));

                                temp = day.ToString("00") + "." + month.ToString("00") + "." + year.ToString("0000");
                                //01.01.0001 ist ein default Wert und wird auf "" geändert!!
                                if (temp.Equals("01.01.0001"))
                                {
                                    temp = "";
                                }
                            }
                            result.AddNextField(temp);

                            break;
                        default:
                            //result.AddNextField(stringValue.TrimEnd()); //Neu: nur hier wird getrimmt
                            result.AddNextField(stringValue); //Neu: nur hier wird getrimmt
                            break;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Macht aus den lustigen negativen Zahlen der AS400 wieder
        /// Vernünftige. Wenn mit der Falschen Codepage gearbeitet wird
        /// hat man hier ein Problem weil -0 -> ü ist und dann nur als ?
        /// geliefert wird.
        /// </summary>
        /// <param name="zahl"></param>
        /// <returns></returns>
        public String negativeCheck(String zahl)
        {
            String result = zahl;
            String last = null;
            bool build = false;
            //Nachsehen nach letzter "Ziffer"

            switch (zahl.Substring(zahl.Length - 1))
            {
                case "ü":
                    last = "0";
                    build = true;
                    break;
                case "J":
                    last = "1";
                    build = true;
                    break;
                case "K":
                    last = "2";
                    build = true;
                    break;
                case "L":
                    last = "3";
                    build = true;
                    break;
                case "M":
                    last = "4";
                    build = true;
                    break;
                case "N":
                    last = "5";
                    build = true;
                    break;
                case "O":
                    last = "6";
                    build = true;
                    break;
                case "P":
                    last = "7";
                    build = true;
                    break;
                case "Q":
                    last = "8";
                    build = true;
                    break;
                case "R":
                    last = "9";
                    build = true;
                    break;
            }

            if (build)
            {
                result = "-" + zahl.Substring(0, zahl.Length - 1) + last;
            }

            return result;
        }

        public bool LadeJetztDS
        {
            get
            {
                return ladeJetztDS;
            }
            set
            {
                ladeJetztDS = value;
            }
        }

        public TcpClient TcpClient
        {
            get
            {
                return tcpClient;
            }
            set
            {
                tcpClient = value;
            }
        }

        public String LambertzPortalSessionID
        {
            get { return lambertzPortalSessionID; }
            set { lambertzPortalSessionID = value; }
        }

        public bool ActivateProtocol
        {
            get { return activateProtocol; }
            set
            {
                if (activateProtocol)
                {
                    this.logWriter.Close();
                }
                activateProtocol = value;
                if (activateProtocol)
                {
                    String datei = "log.txt";
                    FileStream destFile = new FileStream(datei, FileMode.Create, FileAccess.Write);
                    this.logWriter = new StreamWriter(destFile, System.Text.Encoding.Default);
                }
            }
        }
    }
}
