/// <summary>
        /// Constructor
        /// </summary>
        public TcpAppServer() : base()
        {
            MessageDelimiter         = Convert.ToByte(Convert.ToChar(TcpAppCommon.Delimiter));
            base.ClientConnected    += TcpAppServer_ClientConnected;
            base.ClientDisconnected += TcpAppServer_ClientDisconnected;
            base.ServerStopped      += TcpAppServer_ServerStopped;
            CommandQueueThread       = new Thread(ExecuteQueuedCommandsAsync);
            AbortCommandQueueThread  = false;
            CommandQueueThread.Start();

            //TcpAppServer Format:
            // TX: TCP: <Command> [-Param0] [-Param1] ... [-ParamN]
            // RX: TCP: <Status> [Return Message]
            // Source - [email protected]:23
            // Command - Registered Command.

            //Register System Commands
            //--- INIT (Commands used by TcpAppClient) ---
            RegisterSystemCommand("Help", "Show help screen. Include plugin type or object alias name to show commands for selected plugin.", ShowHelp,
                                  TcpAppParameter.CreateOptionalParameter("Plugin", "Plugin type or Alias", "-"));
            RegisterSystemCommand("SignIn", "Sign in to TcpAppServer. Server will verify connection id and return unique ID.", delegate(TcpAppInputCommand sender)
            {
                //Assign Name
                string machineName = sender.Command.Parameter("ConnectionID").Value?.Replace(" ", "_");
                if (string.IsNullOrEmpty(machineName))
                {
                    machineName = sender.AppClient.Connection.ClientIPAddress.ToString();
                }

                if (sender.AppClient.SignedIn)
                {
                    //Client already signed in, verify connection ID.
                    if (sender.AppClient.Name.Equals(machineName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        sender.OutputMessage = sender.AppClient.Name;
                        sender.Status        = TcpAppCommandStatus.OK;
                        return;
                    }
                    else
                    {
                        sender.AppClient.SignedIn = false;
                    }
                }

                TcpAppServerExEventArgs signInArg = new TcpAppServerExEventArgs(sender.AppClient)
                {
                    Value = machineName
                };
                ClientSigningIn?.Invoke(this, signInArg);
                if (signInArg.Cancel == true)
                {
                    sender.OutputMessage = signInArg.Reason;
                    if (string.IsNullOrEmpty(sender.OutputMessage))
                    {
                        sender.OutputMessage = "Access Denied!";
                    }
                    sender.Status = TcpAppCommandStatus.ERR;
                    return;
                }

                string uniqueName = machineName;
                lock (AppClients)
                {
                    //Cleanup instance with same name but already disconnected without signout
                    if (AppClients.FirstOrDefault(x => x.Name == uniqueName) != null)
                    {
                        for (int x = 0; x < AppClients.Count;)
                        {
                            if (!AppClients[x].Connection.Connected && AppClients[x].Name.StartsWith(uniqueName))
                            {
                                AppClients[x].Dispose();
                                AppClients.RemoveAt(x);
                            }
                            else
                            {
                                x++;
                            }
                        }
                    }
                    while (AppClients.FirstOrDefault(x => x.Name == uniqueName) != null)
                    {
                        uniqueName = machineName + "_" + (++Counter).ToString();
                    }
                    sender.AppClient.Name     = uniqueName;
                    sender.OutputMessage      = uniqueName;
                    sender.Status             = TcpAppCommandStatus.OK;
                    sender.AppClient.SignedIn = true;
                    ClientSignedIn?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient));     //Event
                }
            },
                                  TcpAppParameter.CreateParameter("ConnectionID", "Connection ID. If already exist, server will return an updated unique ID."));
            RegisterSystemCommand("SignOut", "Signout TcpAppClient.", delegate(TcpAppInputCommand sender)
            {
                ClientSigningOut?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient));
                sender.AppClient.Dispose();
                lock (AppClients) { AppClients.Remove(sender.AppClient); }
                sender.AppClient.SignedIn = false;
                sender.Status             = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("Version?", "Get TcpAppServer Library Version", delegate(TcpAppInputCommand sender)
            {
                sender.OutputMessage = Version.ToString();
                sender.Status        = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("Terminate", "Terminate Application. Command only valid after client Signin. Default Exit Code = -99", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                int exitCode = -99;
                try
                {
                    exitCode = Convert.ToInt32(sender.Command.Parameter("ExitCode").Value);
                }
                catch { }
                sender.Status = TcpAppCommandStatus.OK;

                Environment.ExitCode = exitCode;
                Thread ptrThread     = new Thread(TerminateApplication);
                ptrThread.Start();
            },
                                  TcpAppParameter.CreateOptionalParameter("ExitCode", "Assign Exit Code for application termination.", "-99"));

            //--- Execution ---
            RegisterSystemCommand("FunctionList?", "Get list of registered functions.", delegate(TcpAppInputCommand sender)
            {
                foreach (TcpAppCommand x in Commands)
                {
                    sender.OutputMessage += x.Keyword;
                    sender.OutputMessage += " ";
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("Execute", "Execute plugin's command. Command only valid after client Signin.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                ITcpAppServerPlugin plugin = _Plugins.FirstOrDefault(x => string.Compare(x.Alias, sender.Command.Parameter("Alias").Value, true) == 0);
                if (plugin == null)
                {
                    throw new ArgumentException("Plugin not exists!");
                }

                TcpAppInputCommand pluginCommand = plugin.GetPluginCommand(sender.Arguments.Skip(1).ToArray());
                TcpAppServerExEventArgs pluginExecuteEventArgs = new TcpAppServerExEventArgs(sender.AppClient)
                {
                    Plugin = plugin
                };
                BeforeExecutePluginCommand?.Invoke(this, pluginExecuteEventArgs);
                if (pluginExecuteEventArgs.Cancel)
                {
                    //Command execution cancelled by server, return error with reason.
                    sender.Status        = TcpAppCommandStatus.ERR;
                    sender.OutputMessage = pluginExecuteEventArgs.Reason;
                }
                else
                {
                    //Proceed with execution.
                    pluginCommand.ExecuteCallback();
                    sender.Status        = pluginCommand.Status;
                    sender.OutputMessage = pluginCommand.OutputMessage;
                }
            },
                                  TcpAppParameter.CreateParameter("Alias", "Plugin Alias Name."));
            RegisterSystemCommand("CheckStatus", "Check execution status for queued command. RETURN: Command status if executed, else BUSY. ERR if no queued message.",
                                  delegate(TcpAppInputCommand sender)
            {
                int queueID = Convert.ToInt32(sender.Command.Parameter("QueueID").Value);
                if (queueID == 0)
                {
                    //ID not specified, get status for next queued command.
                    if (sender.AppClient.NextQueuedCommand == null)
                    {
                        sender.Status        = TcpAppCommandStatus.ERR;
                        sender.OutputMessage = "No queued message!";
                        return;
                    }

                    sender.Status = sender.AppClient.NextQueuedCommand.Status;
                    switch (sender.AppClient.NextQueuedCommand.Status)
                    {
                    case TcpAppCommandStatus.BUSY:
                    case TcpAppCommandStatus.QUEUED:
                        sender.OutputMessage = string.Empty;
                        break;

                    default:
                        lock (CommandQueue)
                        {
                            //Return result for all queued message except executing one.
                            TcpAppInputCommand[] results = ResultQueue.Where(x => x.AppClient == sender.AppClient).ToArray();
                            TcpAppInputCommand nextQueue = null;
                            foreach (TcpAppInputCommand cmd in results)
                            {
                                if (cmd.Status == TcpAppCommandStatus.BUSY || cmd.Status == TcpAppCommandStatus.QUEUED)
                                {
                                    nextQueue = cmd;
                                    break;
                                }
                                if (cmd.Status == TcpAppCommandStatus.ERR)
                                {
                                    cmd.Status         = sender.Status;
                                    cmd.OutputMessage += "! ";         //Prefix for command with error status.
                                }
                                ResultQueue.Remove(cmd);
                                sender.OutputMessage += cmd.OutputMessage + "\n";
                            }

                            //Return number of remaining queued commands
                            sender.OutputMessage += CommandQueue.Where(x => x.AppClient == sender.AppClient).Count().ToString();
                            sender.AppClient.NextQueuedCommand = nextQueue;
                        }
                        break;
                    }
                }
                else
                {
                    //Return status of specific message.
                    TcpAppInputCommand ptrCmd = CommandQueue.FirstOrDefault(x => x.AppClient == sender.AppClient && x.ID == queueID);
                    if (ptrCmd == null)
                    {
                        ptrCmd = ResultQueue.FirstOrDefault(x => x.AppClient == sender.AppClient);
                        if (ptrCmd != null)
                        {
                            ResultQueue.Remove(ptrCmd);
                        }
                        else
                        {
                            sender.Status        = TcpAppCommandStatus.ERR;
                            sender.OutputMessage = "Invalid ID: " + queueID.ToString();
                            return;
                        }
                    }

                    sender.OutputMessage = ptrCmd.OutputMessage;
                    sender.Status        = ptrCmd.Status;
                }
            },
                                  TcpAppParameter.CreateOptionalParameter("QueueID", "Get status of specific message.", "0"));
            RegisterSystemCommand("Abort", "Abort last queued command.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                if (sender.AppClient.NextQueuedCommand != null)
                {
                    lock (CommandQueue)
                    {
                        CommandQueue.Remove(sender.AppClient.NextQueuedCommand);
                        ResultQueue.Remove(sender.AppClient.NextQueuedCommand);
                        sender.AppClient.NextQueuedCommand = null;
                    }
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("FlushQueue", "Flush message queue for calling client.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                lock (CommandQueue)
                {
                    CommandQueue.RemoveAll(x => x.AppClient == sender.AppClient);
                    ResultQueue.RemoveAll(x => x.AppClient == sender.AppClient);
                    sender.AppClient.NextQueuedCommand = null;
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("FlushAllQueue", "Flush message queue for all clients.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                lock (CommandQueue)
                {
                    CommandQueue.Clear();
                    ResultQueue.Clear();
                    lock (AppClients)
                    {
                        foreach (TcpAppServerConnection client in AppClients)
                        {
                            client.NextQueuedCommand = null;
                        }
                    }
                }
                sender.Status = TcpAppCommandStatus.OK;
            });

            //--- PLUGIN ---
            RegisterSystemCommand("PluginTypes?", "Get list of plugin class type. Use CreatePlugins command to instantiate type.", delegate(TcpAppInputCommand sender)
            {
                if (PluginTypes.Count == 0)
                {
                    sender.OutputMessage = "-NONE-";
                }
                else
                {
                    sender.OutputMessage = string.Join(" ", PluginTypes.Select(x => x.Name).ToArray());
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("CreatePlugin", "Create an plugin object from listed plugin types.  Command only valid after client Signin.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                string typeName = sender.Command.Parameter("TypeName").Value;

                //Sanity Check - Type Name
                if (PluginTypes.FirstOrDefault(x => x.Name.Equals(typeName, StringComparison.InvariantCultureIgnoreCase)) == null)
                {
                    throw new ArgumentException("Unknown plugin type: " + typeName);
                }

                Type pluginType = PluginTypes.FirstOrDefault(x => x.Name.Equals(typeName, StringComparison.InvariantCultureIgnoreCase)).Type;

                string aliasName;
                sender.OutputMessage = "Plugin created:";
                foreach (string value in sender.Command.Parameter("Alias").Values)
                {
                    aliasName = value;
                    if (Commands.FirstOrDefault(x => string.Compare(x.Keyword, aliasName, true) == 0) != null)
                    {
                        throw new ArgumentException("Unable to create plugin with alias '" + aliasName + "'. Name already registered as command!");
                    }

                    //Sanity Check - Verify alias name is not plugin type name
                    if (PluginTypes.FirstOrDefault(x => string.Compare(x.Name, aliasName, true) == 0) != null)
                    {
                        throw new ArgumentException("Unable to create plugin with alias '" + aliasName + "'. Name already registered plugin type!");
                    }

                    //Sanity Check - Alias Name
                    if (_Plugins.FirstOrDefault(x => string.Compare(x.Alias, aliasName, true) == 0) != null)
                    {
                        throw new ArgumentException("Unable to create plugin with alias '" + aliasName + "'. Plugin already exists!");
                    }

                    ITcpAppServerPlugin pluginInstance = Activator.CreateInstance(pluginType) as ITcpAppServerPlugin;
                    pluginInstance.Alias = aliasName;
                    _Plugins.Add(pluginInstance);
                    PluginCreated?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient)
                    {
                        Plugin = pluginInstance
                    });
                    sender.OutputMessage += " " + aliasName;
                }
                sender.Status = TcpAppCommandStatus.OK;
            },
                                  TcpAppParameter.CreateParameter("TypeName", "Plugin type name."),
                                  TcpAppParameter.CreateParameterArray("Alias", "Plugin object name, case insensitive.", false));
            RegisterSystemCommand("Plugins?", "Return plugins list by alias name.", delegate(TcpAppInputCommand sender)
            {
                if (_Plugins.Count == 0)
                {
                    sender.OutputMessage = "-NONE-";
                }
                else
                {
                    sender.OutputMessage = string.Join(TcpAppCommon.NewLine, _Plugins.Select(x => x.Alias + "(" + PluginTypes.FirstOrDefault(n => n.Type == x.GetType())?.Name + ")").ToArray());
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("DisposePlugin", "Delete plugin by alias name. Command only valid after client Signin.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                string alias             = sender.Command.Parameter("Alias").Value;
                ITcpAppServerPlugin item = _Plugins.FirstOrDefault(x => string.Compare(x.Alias, alias, true) == 0);
                if (item == null)
                {
                    sender.OutputMessage = "Plugin [" + alias + "] not found / disposed.";
                    sender.Status        = TcpAppCommandStatus.OK; return;
                }
                else
                {
                    if (item.DisposeRequest() == true)
                    {
                        _Plugins.Remove(item);
                        PluginDisposed?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient)
                        {
                            Plugin = item
                        });
                        sender.OutputMessage = alias + " disposed.";
                        sender.Status        = TcpAppCommandStatus.OK;
                    }
                    else
                    {
                        sender.Status        = TcpAppCommandStatus.ERR;
                        sender.OutputMessage = "Unable to dispose " + alias;
                    }
                }
            },
                                  TcpAppParameter.CreateParameter("Alias", "Plugin alias name."));
        }