using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;

namespace WindowsApplication1
{
    class device
    {

        SerialPort port;
        logger log;
        public bool connected=false;

        // konstruktor, otevreni portu
        public device(string portname, logger alog)
        {
            log = alog;
            connected = false;
            try
            {
                port = new SerialPort(portname);
                port.Open();
            }
            catch
            {
                log.add(1, "Can't open port " + portname);
                port = null;
                connected = false;
            }
            finally
            {
                if (port != null && port.IsOpen)
                {
                    port.BaudRate = 9600;
                    port.StopBits = StopBits.One;
                    port.Parity = Parity.None;
                    port.DataBits = 8;
                    port.NewLine = "\n\r";
                    connected = true;
                }
            }

        }

        // uzavreni portu
        public void close()
        {
            if (port != null)
            {
                if (port.IsOpen) port.Close();
            }
        }
        
        // posle radek na port
        public void sendstr(string astr)
        {
            if (connected) port.Write(astr+"\n");
        }

        // precte odpoved z portu a vrati retezec
        private string ReadFromPort(int timeout)
        {
            string sOut;
            port.ReadTimeout = timeout;
            try
            {
                sOut = port.ReadLine();
            }
            catch
            {
                sOut = null;
            }
            return sOut;
        }
        
        // test dostupnosti pripojeneho zarizeni
        public bool IsPresent(bool verbose)
        {
            if (connected) this.sendstr("P"); else return false;
            if (ReadFromPort(1500) == "OK")
            {
                if (verbose) log.add(0, "Device found");
                return true;
            }
            else
            {
                log.add(1, "Device not connected");
                return false;
            }
        }

        // precte adresu z pripojeneho zarizeni
        public string ReadAddr()
        {
            if (!IsPresent(false)) return null; 
            if (connected) this.sendstr("A"); else return null;
            string s = ReadFromPort(1500);
            if (IsInt(s))
            {
                log.add(0, "Read address " + s);
                return s;
            }
            else
            {
                log.add(1, "Read address failed");
                return null;
            }
        }

        // precte delay konstanty z pripojeneho zarizeni
        public int[] ReadDelays()
        {
            int i;
            if (!IsPresent(false)) return null; 
            if (connected) this.sendstr("I"); else return null;
            string s = ReadFromPort(1500);
            if ((s==null) || (s.Length!=19)) 
            {
                log.add(1, "Read delays failed. ");
                return null;
            }
            int[] aReturnValues = new int[5];
            for (i = 0; i < 3; i++)
            {
                if (!IsInt(s.Substring(4 * i, 3)))
                {
                    log.add(1, "Read delays failed. ");
                    return null;
                }
                aReturnValues[i] = (int.Parse(s.Substring(4 * i, 3)))*150;
            }
            for (i = 3; i < 5; i++)
            {
                if (!IsInt(s.Substring(4 * i, 3)))
                {
                    log.add(1, "Read times failed. ");
                    return null;
                }
                aReturnValues[i] = (int.Parse(s.Substring(4 * i, 3))) * 10;
            }
            string[] spole = Array.ConvertAll<int, string>(aReturnValues, Convert.ToString);
            log.add(0, "Read delays " + String.Join(", ",spole) + " [ms]");
            return aReturnValues;
        }

        // precte tabulku kodu
        public string[] ReadCodes(System.Windows.Forms.ProgressBar apg)
        {
            int i;
            string s;
            bool readokflag = true;
            if (!IsPresent(false)) return null; 
            if (!connected) return null;
            string[] aReturnValues = new string[200];
            for (i = 1; i <= 200; i++)
            {
                this.sendstr("R" + (i.ToString()).PadLeft(3, '0'));
                s = ReadFromPort(1500);
                if (s == null)
                {
                    log.add(1, "Codes read failed");
                    apg.Value = 0;
                    return null;
                }
                apg.Value = i;
                if (!IsInt(s))
                {
                    aReturnValues[i - 1] = null;
                    readokflag = false;
                    log.add(1, "Code " + i.ToString() + " is null or failed");
                }
                else
                {
                    aReturnValues[i - 1] = s;
                }
            }
            if (readokflag) log.add(0, "Codes read OK"); else log.add(1, "Some codes read failed");
            apg.Value = 0;
            return aReturnValues;
        }

        // zapise adresu do pripojene jednotky
        public void WriteAddress(string aArg)
        {
            if (!IsPresent(false) || (aArg==null)) return;
            if (connected) this.sendstr("N" + aArg); else return;
            log.add(2, "Write and verify new address "+aArg);
            ReadFromPort(1500);
            string s = ReadAddr();
            if (s != aArg) log.add(1, "Write address error"); else log.add(0, "Write address OK");

        }

        // zapise nastaveni delayu do pripojene jednotky
        public void WriteDelays(int[] aArg)
        {
            int i;
            string s = null;
            if (!IsPresent(false) || (aArg == null) || (!connected) || (aArg.Length!=5)) return;
            for (i = 0; i < 3; i++)  s += ((aArg[i] / 150).ToString()).PadLeft(3,'0');
            for (i = 3; i < 5; i++) s += ((aArg[i] / 10).ToString()).PadLeft(3, '0');
            sendstr("K" + s);
            log.add(2, "Write and verify delays");
            ReadFromPort(1500);
            int[] rpole = ReadDelays();
            for (i = 0; i < 5; i++)
            {
                if (rpole[i] != aArg[i])
                {
                    log.add(1, "Write delays error");
                    return;
                }
            }
            log.add(0, "Write delays OK");
        }

        // zapise tabulku kodu do pripojene jednotky
        public void WriteCodes(string[] aArg, System.Windows.Forms.ProgressBar apg)
        {
            int i;
            string s;
            if (!IsPresent(false) || (aArg == null) || (!connected) || (aArg.Length != 200)) return;
            for (i = 0; i < 200; i++)
            {
                sendstr("W"+(i+1).ToString().PadLeft(3,'0')+aArg[i]);
                ReadFromPort(1500);
                sendstr("R"+(i+1).ToString().PadLeft(3,'0'));
                s=ReadFromPort(1500);
                if(s!=aArg[i]) 
                {
                    log.add(1,"Code table write error on line "+(i+1).ToString()+" ("+aArg[i]+"->"+s+")");
                    apg.Value = 0;
                    return;
                }
                apg.Value = i;
            }
            apg.Value = 0;
            log.add(0,"Code table write OK");
        }

        // provede reset na pripojene jednotce.
        public void ResetDirect(int aTime, int aOut)
        {
            if (!connected) return;
            int aTimeDiv = aTime / 10;
            sendstr("X"+aOut.ToString()+aTimeDiv.ToString());
            string s = ReadFromPort(1500);
            log.add(3, s);
            if (s.Substring(0,6) == "Direct")
            {
                log.add(0, "Direct output " + aOut.ToString() + " was restarted. Please wait " + aTime.ToString() + " second.");
            }
            else
            {
                log.add(1, "Direct reset failed");
            }
        }

        // provede reset na vzdalene jednotce
        public void ResetRemote(int aTime, int aOut, string aRmtAddr)
        {
            string s;
            bool konec=false;
            int timeout = 15000;
            if (!connected) return;
            if (IsInt(aRmtAddr)) {
                aRmtAddr=aRmtAddr.Substring(0, 3).PadLeft(3, '0');
            }
            else
            {
                log.add(1,"Address must be numeric value");
                return;
            }
            int aTimeDiv = aTime / 10;
            log.add(2, "Remote reset to address "+aRmtAddr+" executed");
            sendstr("C"+ aRmtAddr + aOut.ToString() + aTimeDiv.ToString());
            while(!konec) 
            {
                s = ReadFromPort(timeout);
                if (s == null)
                {
                    log.add(1, "Remote reset failed");
                    konec = true;
                }
                else log.add(3, s);
                if ((s!=null) && (s.Substring(0, 1) == "E"))
                {
                    log.add(1, "Remote reset failed");
                    konec = true;
                }
                if ((s != null) && (s.Substring(0, 1) == "G"))
                {
                    log.add(1, "Remote reset OK. Device "+aRmtAddr+" will be aprox. "+aTime.ToString()+" sec busy");
                    konec = true;
                }
            }
        }

        // provede reset na vzdalene jednotce pomoci opakovace
        public void ResetRepeater(int aTime, int aOut, string aRmtAddr, string aRptrAddr)
        {
            string s;
            bool konec = false;
            int timeout = 15000;
            if (!connected) return;
            if (IsInt(aRmtAddr) || IsInt(aRptrAddr)) {
                aRmtAddr=aRmtAddr.Substring(0, 3).PadLeft(3, '0');
                aRptrAddr=aRptrAddr.Substring(0, 3).PadLeft(3, '0');
            }
            else
            {
                log.add(1,"Address must be numeric value");
                return;
            }
            int aTimeDiv = aTime / 10;
            log.add(2, "Remote reset to address " + aRmtAddr + " via repeater "+aRptrAddr+" executed");
            sendstr("D" + aRmtAddr + aRptrAddr + aOut.ToString() + aTimeDiv.ToString());
            while (!konec)
            {
                s = ReadFromPort(timeout);
                if (s == null)
                {
                    log.add(1, "Remote reset failed");
                    konec = true;
                }
                else log.add(3, s);
                if ((s != null) && (s.Substring(0, 1) == "E"))
                {
                    log.add(1, "Remote reset failed");
                    konec = true;
                }
                if ((s != null) && (s.Substring(0, 1) == "G"))
                {
                    log.add(1, "Remote reset OK. Device " + aRmtAddr + " will be aprox. " + aTime.ToString() + " sec busy");
                    konec = true;
                }
                if ((s != null) && (s.Substring(0, 4) == "IM-5")) timeout = 40000;
            }
        }

        // provede live test na vzdalenou jednotku
        public void LiveTest(string aRmtAddr)
        {
            if (!connected) return;
            if (IsInt(aRmtAddr)) {
                aRmtAddr=aRmtAddr.Substring(0, 3).PadLeft(3, '0');
            }
            else
            {
                log.add(1,"Address must be numeric value");
                return;
            }
            log.add(2, "Live test to " + aRmtAddr + " executed");
            sendstr("L"+ aRmtAddr);
            ReadFromPort(1000);
            string s=ReadFromPort(30000);
            log.add(3, s);
            if((s!=null) && (s.Length>6) && (s.Substring(0,6)=="L: Rem")) 
            {
                log.add(0,"Remote device alive");
            } else 
            {
                log.add(1,"Remote device not alive");
            }
        }

        // otestuje string jestli je v nem integer
        public bool IsInt(string s)
        {
            try
            {
                int.Parse(s);
                return true;
            }
            catch
            {
                return false;
            }
        }

    }
}
