/// <summary>
 /// Performs a shallow copy of the specified TFMS_Data object
 /// </summary>
 /// <param name="data">the TFMS_Data object that you want to duplicate</param>
 private void clone(TFMS_Data data)
 {
     if (data == null) return;
     this.acknowledged = data.acknowledged;
     this.cmdCommand = data.cmdCommand;
     this.strMessage = data.strMessage;
     this.strName = data.strName;
     this.timeStamp = data.timeStamp;
 }
        /// <summary>
        /// Create a TFMS_Data object containing the name of all clients currently connected to the server
        /// </summary>
        /// <returns>TFMS_Data object with message field filled</returns>
        private TFMS_Data GetClientListMessage()
        {
            TFMS_Data msgToSend = new TFMS_Data(TFMS_Command.List, null, null);
            foreach (TFMS_ClientInfo c in clientList)
            {
                // use a * as delimiter
                msgToSend.strMessage += c.strName + "*";
            }

            return msgToSend;
        }
        /// <summary>
        /// When the server receives a message from a client, it rebroadcasts the message to all other clients
        /// This method sends the message to a single client
        /// </summary>
        /// <param name="data">TFMS_Data object to be sent</param>
        /// <param name="socket">Socket belonging to the target client</param>
        /// <param name="send">function to be called when the message is done sending (should be "onSend")</param>
        /// <returns>the result of sending the message to the target client</returns>
        private IAsyncResult sendTFMSmsg(TFMS_Data data, Socket socket, AsyncCallback send)
        {
            // get the byte array of the TFMS_Data object
            byte[] message = data.ToByte();

            Console.WriteLine("Sending length: {0}", message.Length);

            // syncronously send the length of the actual message and block until the message has been sent.
            socket.Send(new TFMS_Data(TFMS_Command.MsgLen, string.Format("{0}", message.Length), data.strName).ToByte());

            Console.WriteLine("BeginSend: {0} msg", data.cmdCommand);

            // the async call to send the message
            // returns an IAsyncResult object
            return socket.BeginSend(message, 0, message.Length, SocketFlags.None, send, socket);
        }
        /// <summary>
        /// this hadles the actual message forwarding ect.
        /// you should never call this function directly
        /// </summary>
        /// <param name="result">this encapsulates the Socket of the client</param>
        public void OnReceive(IAsyncResult result)
        {
            // get the socket that received data
            Socket clientSocket = (Socket)result.AsyncState;

            // get the ClientInfo from the connected client
            TFMS_ClientInfo CI = null;
            if (findIndexFromClient(clientList, clientSocket) >= 0)
                CI = clientList[findIndexFromClient(clientList, clientSocket)];

            Console.WriteLine("\n****************************************");
            Console.WriteLine("Data Received from {0}", getNamefromSocket(clientSocket));

            // attempt to get the data from the client and convert it into TFMS_Data object
            TFMS_Data msgReceived = extractData(clientSocket, CI, result);

            TFMS_Data msgToSend = new TFMS_Data();
            msgToSend.cmdCommand = msgReceived.cmdCommand;
            msgToSend.strName = msgReceived.strName;

            #region Interpret incoming message command

            // set the message to send, client info buffer size, and client info based on the incoming command
            switch (msgReceived.cmdCommand)
            {
                case TFMS_Command.Login:    // new client logging in
                    #region Login command

                    Console.WriteLine("Received login from {0}", getNamefromSocket(clientSocket));

                    // tell the server a logon was requested
                    logonRequested(msgReceived);

                    // add the new client to the current list of clients
                    TFMS_ClientInfo clientInfo = new TFMS_ClientInfo(clientSocket, msgReceived.strName);
                    clientList.Add(clientInfo);

                    // pass the incoming client's name to other clients
                    msgToSend.strMessage = msgReceived.strName;
                    clientInfo.buffer = new byte[TFMS_Constants.BUFFER_SIZE]; // dimension the clients buffer.
                    CI = clientInfo; // CI would not have been found before since its not in the clientList so just add it now

                    #endregion
                    break;
                case TFMS_Command.Logout:   // client logging out
                    #region Logout command

                    Console.WriteLine("Received Logout from {0}", getNamefromSocket(clientSocket));

                    // tell the server a logout was requested
                    logoffRequested(msgReceived);

                    // remove the client from the list of connected clients.
                    clientList.RemoveAt(findIndexFromClient(clientList, clientSocket));
                    clientSocket.Close();

                    // pass the client's name to other clients
                    msgToSend.strMessage = msgReceived.strName;

                    #endregion
                    break;
                case TFMS_Command.List:     // client requesting a list of all clients
                    #region List command

                    Console.WriteLine("Received List request from {0}", getNamefromSocket(clientSocket));

                    // tell the server a list of clients was requested
                    listRequested(msgReceived);

                    // get the list of all currently connected clients
                    msgToSend = GetClientListMessage();

                    // send the message to a single client
                    sendTFMSmsg(msgToSend, clientSocket, new AsyncCallback(OnSend));

                    #endregion
                    break;
                case TFMS_Command.Message:  // incoming message to be broadcast
                    #region Message command

                    Console.WriteLine("Received TFM from {0} (size={1})", getNamefromSocket(clientSocket), CI.buffer.Length);

                    // tell the server a message broadcast was requested
                    relayRequested(msgReceived);

                    // copy the message to the server's outgoing message field
                    msgToSend.strMessage = msgReceived.strMessage;

                    #endregion
                    break;
                case TFMS_Command.MsgLen:   // incoming message is greater than the current buffer size
                    #region Long Message command

                    Console.WriteLine("Received long TFM from {0} (size={1})", getNamefromSocket(clientSocket), CI.buffer.Length);

                    // resize the buffer for the incoming large message
                    CI.buffer = new byte[int.Parse(msgReceived.strMessage) + TFMS_Constants.BUFFER_SIZE];

                    #endregion
                    break;
                default:                    // unrecognizable command
                    #region Unknown command

                    // we gracefully fail and gtfoh
                    Console.WriteLine("Cant interpret command! Aborting this relay.");
                    Console.WriteLine("BeginReceive from {0}", getNamefromSocket(clientSocket));

                    // restart the receive command
                    clientSocket.BeginReceive(CI.buffer, 0, CI.buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), clientSocket);

                    return;
                    #endregion
            }
            #endregion

            // send the message to every client logged in to the server
            broadcastToClients(msgToSend, clientList);

            // after a message is broadcast, begin listening for more incoming messages
            // NOTE: if many users are logged in at once, the broadcast method could block other incoming messages
            // for future iterations of TFMS, we want the receiving command to run on a separate thread from the sending command
            if (msgReceived.cmdCommand != TFMS_Command.Logout)
            {
                Console.WriteLine("Waiting for data from {0}", getNamefromSocket(clientSocket));
                clientSocket.BeginReceive(CI.buffer, 0, CI.buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), clientSocket);
            }
        }
 /// <summary>
 /// Step through the list of clients, sending the message to everybody currently logged in to the server
 /// This is the "meat and potatoes" of TFMS
 /// </summary>
 /// <param name="data">message to be broadcast</param>
 /// <param name="list">list of clients logged in to the server</param>
 private void broadcastToClients(TFMS_Data data, List<TFMS_ClientInfo> list)
 {
     // List commands are only sent to one recipient
     // MsgLen commands are also not resent to anyone
     if (data.cmdCommand != TFMS_Command.List && data.cmdCommand != TFMS_Command.MsgLen)
     {
         // loop through the client list, sending the message to every client currently logged in
         foreach (TFMS_ClientInfo c in list)
         {
             // send a message to a single client
             Console.WriteLine("Relaying {0} from {1} to {2}", data.cmdCommand, data.strName, c.strName);
             sendTFMSmsg(data, c.socket, new AsyncCallback(OnSend));
         }
     }
 }
        /// <summary>
        /// OnReceive will interpret messages received from the TFMS_Server object and handle the possible messages
        /// </summary>
        /// <param name="result">the result of the response from the server</param>
        public void OnReceive(IAsyncResult result)
        {
            // assume default buffer size
            int buffSize = TFMS_Constants.BUFFER_SIZE;

            try
            {
                int numBytesReceived = 0;
                TFMS_Data msgReceived;

                try
                {
                    // attempt to get the number of bytes received from the server
                    numBytesReceived = clientSocket.EndReceive(result);
                }
                catch (Exception)
                {
                    disconnectDetected(null);
                    return;
                }

                // using the received number of bytes, create a new TFMS_Data object to handle incoming data
                if (numBytesReceived == 0)
                {
                    msgReceived = new TFMS_Data(TFMS_Command.Null, "", "");
                }
                else
                {
                    byte[] temp = new byte[numBytesReceived];
                    Array.Copy(byteData, temp, numBytesReceived);
                    msgReceived = new TFMS_Data(temp);
                }

                #region Interpret incoming message command

                // handlccordingly process the message received
                switch (msgReceived.cmdCommand)
                {
                    case TFMS_Command.Login:
                        loginReceived(msgReceived);
                        break;

                    case TFMS_Command.Logout:
                        logoffReceived(msgReceived);
                        break;

                    case TFMS_Command.Message:
                        dataReceived(msgReceived);
                        break;

                    case TFMS_Command.MsgLen:
                        // this is the only place that a long message has to do work in the client class
                        // everything else is passed on to the client
                        buffSize = int.Parse(msgReceived.strMessage);
                        break;

                    case TFMS_Command.List:
                        listReceived(msgReceived);
                        break;

                    case TFMS_Command.Null:
                        break;
                }

                #endregion

                byteData = new byte[buffSize];

                // wait for next message from server.
                clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);

            }
            catch (ObjectDisposedException)
            {
                Console.WriteLine("This object has been disposed");
            }
            catch (SocketException ex)
            {
                // if there is a Socket exception, clear out this client from the socket and disconnect
                Console.WriteLine(ex.Message);
                if (ex.SocketErrorCode == SocketError.ConnectionReset)
                    this.clientSocket = null;
            }
        }
        /// <summary>
        /// Send a message to the server that needs to be broadcast to all other clients
        /// </summary>
        /// <param name="path">the path drawn on the client's DrawingBox that will be relayed to other clients</param>
        public void sendMessage(string path)
        {
            // prepare the data by converting the message informatino into a byte array
            byte[] data = (new TFMS_Data(TFMS_Command.Message, path, strName)).ToByte();
            TFMS_Data lenToSend = new TFMS_Data(TFMS_Command.MsgLen, string.Format("{0}", data.Length), strName);

            // send the message to the socket
            clientSocket.Send(lenToSend.ToByte());
            Thread.Sleep(TFMS_Constants.DELAY_TIME);

            // begin sending the message to the server
            clientSocket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
        }
        /// <summary>
        /// If the current TFMS_Data objects contains the Relay command, echo the status (and perform the command elsewhere)
        /// When a message is sent to be broadcast to other uses, attempt to log it in the SQL database
        /// </summary>
        /// <param name="data">TFMS_Data object containing the Relay command</param>
        private static void handleRelayRequest(TFMS_Data data)
        {
            Console.WriteLine("{0} has sent a message", data.strName);

            // log the message in the SQL database
            //logMessage(data);
        }
        /// <summary>
        /// Attempt to connect to the server at the specified IP address
        /// </summary>
        /// <param name="IP_address">the IP address of the target server ("a.b.c.d" format)</param>
        /// <returns>true if the connection was succesful, otherwise false</returns>
        public bool connect(string IP_address)
        {
            try
            {
                // create the socket
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // get the IP addresses, then connect them with the socket
                IPAddress serverip = IPAddress.Parse(IP_address);
                IPEndPoint ipEnd = new IPEndPoint(serverip, serverPort);
                clientSocket.Connect(ipEnd);

                // try to login after connecting to the server
                TFMS_Data msgToSend = new TFMS_Data(TFMS_Command.Login, null, strName);
                clientSocket.Send(msgToSend.ToByte());

                // setup buffer to receive data from the server
                byteData = new byte[TFMS_Constants.BUFFER_SIZE];

                // start listening to the data asynchronously
                clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);

                // the client successfully connected
                Console.WriteLine("{0} successfully connected to the server!", msgToSend.strName);
                return true;
            }
            catch (Exception)
            {
                // the client failed to connect
                Console.WriteLine("Client at '{0}' failed to connect to the server!", IP_address);
                return false;
            }
        }
 /// <summary>
 /// If the current TFMS_Data objects contains the List command, echo the status (and perform the command elsewhere)
 /// </summary>
 /// <param name="data">TFMS_Data object containing the List command</param>
 private static void handleListRequest(TFMS_Data data)
 {
     Console.WriteLine("{0} has requested a list of clients", data.strName);
 }
 /// <summary>
 /// If the current TFMS_Data objects contains the Logoff command, echo the status (and perform the command elsewhere)
 /// </summary>
 /// <param name="data">TFMS_Data object containing the Logoff command</param>
 private static void handleLogoffRequest(TFMS_Data data)
 {
     Console.WriteLine("{0} is logging off", data.strName);
 }
        /// <summary>
        /// Attempt to log the current message in the SQL database
        /// </summary>
        /// <param name="data">TFMS_Data object containing the current message</param>
        private static void logMessage(TFMS_Data data)
        {
            Console.WriteLine("Logging {0}'s message to database", data.strName);

            // Attmpt to log the message to the SQL database
            try
            {
                // open a new SQL connection
                SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DBConnect"].ToString());
                conn.Open();

                // add the message to the database
                SqlCommand cmd = new SqlCommand("INSERT INTO TFMessage (sender, date, message) VALUES (@sent_by, @date, @msg)", conn);
                cmd.Parameters.AddWithValue("@sent_by", data.strName);
                cmd.Parameters.AddWithValue("@date", data.timeStamp);
                cmd.Parameters.AddWithValue("@msg", data.strMessage);
                cmd.ExecuteNonQuery();

                Console.WriteLine("Logged {0}'s message.", data.strName);

                // close the connection
                conn.Close();
            }
            catch (Exception e)
            {
                // print to the console if the logging failed
                Console.WriteLine("Logging of {0}'s message failed. {1}", data.strName, e.Message);
            }
        }
 /// <summary>
 /// If a client is running and the server has disappeared, alert the user
 /// </summary>
 /// <param name="dataReceived">the TFMS_Data object indicating the server was lost</param>
 private void myClient_disconnectDetected(TFMS_Data msg)
 {
     myClient = null;
     MessageBox.Show("The server has been closed or has crashed. Please restart the server.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     Application.Exit();
 }
        /// <summary>
        /// If the server indicates that a message was received, alert the current user
        /// </summary>
        /// <param name="msg">the TFMS_Data object indicating a message was received</param>
        private void handleMessage(TFMS_Data msg)
        {
            notifyIcon1.Visible = true;
            notifyIcon1.BalloonTipText = string.Format("You have a TFM");
            notifyIcon1.ShowBalloonTip(10 * POPUP_TIME);

            // make the call to add an item thread safe because chances are that this will be called from another thread
            if (lstMessages.InvokeRequired)
                lstMessages.Invoke(new Action<TFMS_Data>(delegate(TFMS_Data a) { lstMessages.Items.Add(a); }), msg);
            else
                lstMessages.Items.Add(msg);
        }
 /// <summary>
 /// If the server indicates that another client has logged in, alert the current user
 /// </summary>
 /// <param name="msg">the TFMS_Data object indicating another user logged in</param>
 private void handleLogon(TFMS_Data msg)
 {
     notifyIcon1.BalloonTipText = string.Format("{0} has joined", msg.strName);
     notifyIcon1.ShowBalloonTip(POPUP_TIME);
 }
 /// <summary>
 /// When the server indicates that a client has logged in, send the updated list to the current user
 /// </summary>
 /// <param name="msg">the TFMS_Data object indicating the peer list was updated</param>
 private void handleList(TFMS_Data msg)
 {
     notifyIcon1.ShowBalloonTip(POPUP_TIME, "List", "You got the list of peers", ToolTipIcon.Info);
 }