//  
//  ConsoleCmd.cs
//  
//  Author:
//       Robert BRACCAGNI alias Gai-Luron <lfsgailuron@free.fr>
// 
//  Copyright (c) 2010 Gai-Luron
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace LFSLapper
{
    static class ConsoleCmd
    {
        enum TypePack
        {
            LP_NONE,		//  0					: not used
            LP_EOP,   		//  1 - instruction		: End of packets
            LP_LST, 		//  2 - instruction		: Server list
            LP_MSG, 		//  3 - instruction		: Server Message
        }

        public static void readCmd( string lapperServerPass, string ip, int port, string strQuit )
        {
            Encoder myEncoder = new Encoder();

            TcpClient client = new TcpClient();

            IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            byte[] recvPacket = new byte[4096];

            try
            {
                client.Connect(serverEndPoint);
            }
            catch
            {
                System.Threading.Thread.Sleep(2000);
                return;
            }

            NetworkStream clientStream = client.GetStream();

            sendToStream(clientStream, myEncoder.LP_CNT(lapperServerPass));
            try
            {
                recvPacket = GetPackServer(clientStream);
            }
            catch
            {
                Console.WriteLine("Cannot connect to a server, please quit Lapper!");
                System.Threading.Thread.Sleep(4000);
                return;
            }

			string lineRead;
            while (true)
            {

                Console.Write(">");
                while( true )
                { // Just for monodevelop who catch console in a strange way
					lineRead = System.Console.ReadLine();
                	if (lineRead == null){
						System.Threading.Thread.Sleep( 100 );
                    	continue;
					}
					break;
				}
                //After typing 'quit' in the console.
                if (lineRead == "quit")
                {
                    Console.WriteLine(strQuit);
                    clientStream.Close();
                    clientStream.Dispose();
                    client.Close();
                    break;
                }
                //If command is empty
                if (lineRead == "")
                {
                    continue;
                }

                byte[] buffer = myEncoder.LP_CMD( lineRead );
                try
                {
                    clientStream.Write(buffer, 0, buffer.Length);
                    clientStream.Flush();
                }
                catch{
                    Console.WriteLine( "Error on TCP connection, Lost" );
                    return;
                }
                bool exitWhile = false;
                bool flagFirst = true;
                while ( true )
                {
                    try
                    {
                        recvPacket = GetPackServer(clientStream);
                    }
                    catch( Exception e )
                    {
                        Console.WriteLine(e.Message);
                        Console.WriteLine("No server connection, quit Application!");
                        System.Threading.Thread.Sleep(4000);
                        return;
                    }
                    string PH = packetHead(recvPacket);
                    switch (PH)
                    {
                        case "EOP":
                            exitWhile = true;
                            break;
                        case "LST":
                            if (flagFirst)
                            {
                                Console.WriteLine("List of servers:");
                                Console.WriteLine("RemotePort :" + port );
                                flagFirst = false;
                            }
                            Decoder.LST lst = new Decoder.LST(recvPacket);
							string sta = Enum.GetName(typeof(LapperInstances.status), lst.State).Replace( "_"," " );
							
                            Console.WriteLine("\t" + lst.HName + " ( " + sta + " )" + ", " + lst.Ip + ", " + lst.Port + ", " + lst.Pass + ", " + lst.WorkingDir + ", " + lst.ConfigFile);
                            break;
                        case "MSG":
                            Decoder.MSG msg = new Decoder.MSG(recvPacket);
                            Console.WriteLine(msg.Msg);
                            break;
                    }
                    if (exitWhile)
                        break;
                }
            }
        }
        private static void sendToStream(NetworkStream clientStream, string str)
        {
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer = encoder.GetBytes(str);
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
        }
        private static void sendToStream(NetworkStream clientStream, byte[] buffer)
        {
            clientStream.Write(buffer, 0, buffer.Length);
//            clientStream.Write(buffer, 0, 1);
            clientStream.Flush();
        }

        public static string packetHead(byte[] packet)
        {
            string packetHead;
            packetHead = Enum.GetName(typeof(TypePack), packet[1]);
            if (packetHead != null)
                packetHead = packetHead.Remove(0, 3);
            else
                packetHead = "";
            return packetHead;
        }
        public static byte[] GetPackServer(NetworkStream clientStream)
        {
            try
            {

                int len = clientStream.ReadByte();
                if (len != -1)
                {
                    byte[] buff = new byte[len];
                    clientStream.Read(buff, 1, (int)(len - 1));
                    buff[0] = (byte)len;
                    return buff;
                }
                else
                {
                    throw new Exception("Error on GetPackServer, not receive byte");
                }
            }
            catch
            {
                throw new Exception("Error on GetPackServer, no connection");
//                return null;
            }

        }
    }
    public class Encoder
    {
        enum TypePack
        {
            LP_NONE,		//  0					: not used
            LP_CNT,   		//  1 - instruction		: Connection
            LP_CMD, 		//  2 - instruction		: Server list
        }

        /// <summary>
        /// Encoder for messages sent to LFS console
        /// </summary>
        /// <param name="msg">Message to send</param>
        /// <returns></returns>
        /// 
        public byte[] LP_CNT( string pass )
        {
            int len;

            byte packLen = 32;
            byte[] packet = new byte[packLen];

            packet[0] = packLen;
            packet[1] = (int)TypePack.LP_CNT;
            len = Math.Min(pass.Length, 30);
            System.Text.Encoding.ASCII.GetBytes(pass, 0, len, packet, 2);

            return packet;
        }
        public byte[] LP_CMD(string cmd)
        {
            byte packLen = 82;
            byte[] packet = new byte[packLen];

            packet[0] = packLen;
            packet[1] = (int)TypePack.LP_CMD;
            int len = Math.Min(cmd.Length, 80);
            System.Text.Encoding.ASCII.GetBytes(cmd, 0, len, packet, 2);
            return packet;
        }
    }
    public class Decoder
    {
        enum TypePack
        {
            LP_NONE,		//  0					: not used
            LP_EOP,   		//  1 - instruction		: End of packets
            LP_LST, 		//  2 - instruction		: Server list
        }

        /// <summary>
        /// Encoder for messages sent to LFS console
        /// </summary>
        /// <param name="msg">Message to send</param>
        /// <returns></returns>
        /// 

        static int pakGetByte(byte[] pak, int first)
        {
            return (int)pak[first];
        }
        static string pakGetString(byte[] pak, int first, int len) ///n'importe oik
        {
            return InSim.LfsEncoding.Current.GetString(pak, first, len);
        }
        static int pakGetWord(byte[] pak, int first)
        {
            return (int)System.BitConverter.ToUInt16(pak, first);
        }
        static int pakGetShort(byte[] pak, int first)
        {
            return (int)System.BitConverter.ToInt16(pak, first);
        }
        static long pakGetUnsigned(byte[] pak, int first)
        {
            return (long)System.BitConverter.ToUInt32(pak, first);
        }
        static int pakGetInt(byte[] pak, int first)
        {
            return (int)System.BitConverter.ToInt32(pak, first);
        }
        static float pakGetFloat(byte[] pak, int first)
        {
            return (float)System.BitConverter.ToSingle(pak, first);
        }
        public class LST
        {
            public readonly string HName;
            public readonly string Ip;
            public readonly int Port;
            public readonly string Pass;
            public readonly string WorkingDir;
            public readonly string ConfigFile;
            public readonly int State;

            public LST(byte[] packet)
            {
                HName = pakGetString(packet, 2, 30);
                Ip = pakGetString(packet, 32, 30);
                Port = pakGetShort(packet, 62);
                Pass = pakGetString(packet, 64, 20);
                WorkingDir = pakGetString(packet, 84, 60);
                ConfigFile = pakGetString(packet, 144, 30);
                State = pakGetByte(packet, 174);
            }
        }
        public class MSG
        {
            public readonly string Msg;

            public MSG(byte[] packet)
            {
                Msg = pakGetString(packet, 2, 80);
            }
        }

    }

    


}
