<commit_before>﻿// -----------------------------------------------------------------------
// <copyright file="IrcChannelViewModel.cs" company="">
// Copyright (c) 2011 <NAME>, <NAME>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
// -----------------------------------------------------------------------

namespace Handle.WPF
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.IO;
  using System.Media;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Input;
  using System.Windows.Interop;
  using System.Windows.Threading;
  using Caliburn.Micro;
  using IrcDotNet;

  /// <summary>
  /// TODO: Update summary.
  /// </summary>
  public class IrcChannelViewModel : ViewModelBase
  {
    public delegate void JoinChannelClickedEventHandler();
    public event JoinChannelClickedEventHandler JoinChannelClicked;

    private IEventAggregator events;
    private List<string> personalHistory;
    private int historyIndex = -1;
    private Queue<string> tabCompletionQueue;
    private string tabCompletionName;
    private FilterService filterService;

    public IrcChannel Channel { get; set; }

    public BindableCollection<Message> Messages { get; set; }

    private Logger logger;
    private string message;
    public string Message
    {
      get
      {
        return this.message;
      }
      set
      {
        this.message = value;
        NotifyOfPropertyChange(() => this.Message);
        NotifyOfPropertyChange(() => this.CanSend);
      }
    }
    private string topic;

    public string Topic
    {
      get
      {
        return topic;
      }
      set
      {
        this.topic = value;
        NotifyOfPropertyChange(() => this.Topic);
      }
    }

    public bool CanSend
    {
      get
      {
        return !string.IsNullOrWhiteSpace(this.Message);
      }
    }

    public bool Closable { get; set; }

    /// <summary>
    /// Initializes a new instance of the IrcChannelViewModel class
    /// </summary>
    public IrcChannelViewModel(IrcChannel channel, string networkName, Settings settings)
    {
      this.Settings = settings;
      this.Messages = new BindableCollection<Message>();
      this.Closable = true;
      this.DisplayName = channel.Name;
      this.networkName = networkName;
      this.personalHistory = new List<string>();
      this.events = IoC.Get<IEventAggregator>();
      this.filterService = IoC.Get<FilterService>();
      this.Channel = channel;
      this.Channel.ModesChanged += this.channelModesChanged;
      this.Channel.UsersListReceived += this.channelUsersListReceived;
      this.Channel.MessageReceived += this.channelMessageReceived;
      this.Channel.UserJoined += this.channelUserJoined;
      this.Channel.UserLeft += this.channelUserLeft;
      this.Channel.NoticeReceived += this.channelNoticeReceived;
      this.Channel.TopicChanged += this.channelTopicChanged;

      DirectoryInfo di = new DirectoryInfo(Settings.PATH + "\\logs\\");
      if (!di.Exists)
        di.Create();
      if (this.Settings.CanLog)
        this.logger = new Logger(String.Format("{0}\\logs\\{1}.{2}.txt",
                                 Settings.PATH,
                                 channel.Name,
                                 networkName));

      this.Channel.GetTopic();
      this.Users = new List<IrcChannelUser>();
    }

    private void channelUsersListReceived(object sender, EventArgs e)
    {
      this.Users.Clear();
      foreach (var user in this.Channel.Users)
      {
        this.Users.Add(user);
      }
      this.Users.Sort(IrcChannelUserComparison.Compare);
    }

    private void channelModesChanged(object sender, EventArgs e)
    {
    }

    private List<IrcChannelUser> users;

    public List<IrcChannelUser> Users
    {
      get
      {
        return this.users;
      }
      set
      {
        this.users = value;
        NotifyOfPropertyChange(() => this.Users);
      }
    }

    private string displayName;
    private string networkName;

    public override string DisplayName
    {
      get
      {
        return this.displayName;
      }

      set
      {
        this.displayName = value;
      }
    }

    private void channelTopicChanged(object sender, EventArgs e)
    {
      this.Topic = this.Channel.Topic;
    }

    private void channelMessageReceived(object sender, IrcMessageEventArgs e)
    {
      Message m = new Message(e.Text,
                              DateTime.Now.ToString(this.Settings.TimestampFormat),
                              e.Source.Name, MessageLevels.Public);
      if (this.filterService.IsImportant(m.Text))
        m.Levels.Add(MessageLevels.Highlight);

      this.Messages.Add(m);
      if (this.Settings.CanLog && this.logger != null)
        logger.Append(String.Format("{0} {1}: {2}",
                                    m.Received,
                                    m.Sender,
                                    m.Text));
      this.events.Publish(new MessageFilterEventArgs(this.Channel.Name, this.networkName,
                                                     DateTime.Now.ToString("HH:mm"), m.Sender, m.Text));
    }

    private void channelNoticeReceived(object sender, IrcMessageEventArgs e)
    {
      this.Messages.Add(new Message(e.Text, DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=", MessageLevels.Notice));
    }

    private void channelUserJoined(object sender, IrcChannelUserEventArgs e)
    {
      if (this.Settings.AdvertiseJoins)
      {
        this.Messages.Add(new Message(String.Format("{0} [{1}] has joined {2}",
                                                    e.ChannelUser.User.NickName,
                                                    e.ChannelUser.User.HostName,
                                                    e.ChannelUser.Channel.Name),
                                      DateTime.Now.ToString(this.Settings.TimestampFormat),
                                      "=!=",
                                      MessageLevels.Join));
      }
      this.Users.Add(e.ChannelUser);
      this.Users.Sort(IrcChannelUserComparison.Compare);

    }

    private void channelUserLeft(object sender, IrcChannelUserEventArgs e)
    {
      if (this.Settings.AdvertiseParts)
      {
        this.Messages.Add(new Message(String.Format("{0} [{1}] has left {2} [{3}]",
                                                    e.ChannelUser.User.NickName,
                                                    e.ChannelUser.User.HostName,
                                                    e.ChannelUser.Channel.Name,
                                                    e.Comment),
                                      DateTime.Now.ToString(this.Settings.TimestampFormat),
                                      "=!=",
                                      MessageLevels.Part));
      }

      this.Users.Remove(e.ChannelUser);
    }

    public void Send()
    {
      // TODO Ignore adjacent duplicates
      this.personalHistory.Add(this.Message);
      this.historyIndex = -1;

      if (this.Message[0] != '/')
      {
        // Regular message
        this.Channel.Client.LocalUser.SendMessage(this.Channel, this.Message);
        Message m = new Message(this.Message.Trim(),
                                DateTime.Now.ToString(this.Settings.TimestampFormat),
                                this.Channel.Client.LocalUser.NickName,
                                MessageLevels.Public);
        this.Messages.Add(m);
        if (this.Settings.CanLog && this.logger != null)
          logger.Append(string.Format("{0} {1}: {2}", m.Received, m.Sender, m.Text));
        this.Message = string.Empty;
        return;
      }

      string command;
      string[] args;

      this.Message = this.Message.TrimEnd(' ');
      if (this.Message.Contains(" "))
      {
        command = this.Message.Substring(1, this.Message.IndexOf(' ') - 1).ToLower();
        args = this.Message.Substring(this.Message.IndexOf(' '), this.Message.Length - command.Length - 1)
                                  .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
      }
      else
      {
        command = this.Message.Substring(1, this.Message.Length - 1);
        args = new string[] { };
      }

      IrcNetworkViewModel invm = (IrcNetworkViewModel)this.Parent;
      IrcMainViewModel imvm = (IrcMainViewModel)invm.Parent;
      switch (command)
      {
        // TODO No such nick
        case "help":
          // TODO "/help command"
          this.Messages.Add(new Message("Helptext.",
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          break;
        case "join":
        case "j":
          if (args.Length == 1)
          {
            this.Channel.Client.Channels.Join(args[0]);
          }
          else if (args.Length == 2)
          {
            this.Channel.Client.Channels.Join(args[0], args[1]);
          }
          else
          {
            this.Messages.Add(new Message(string.Format("Invalid number of arguments for the command \"{0}\"!", command),
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          }
          break;
        case "whois":
          this.WhoIs(args[0]);
          break;
        case "clear":
          this.ClearMessages();
          break;
        case "complete":
          if (args[0] == "-d" || args[0] == "--delete")
          {
            this.Settings.Completions.Remove(args[1]);
          }
          else
          {
            var longString = new List<string>();
            for (int i = 1; i < args.Length; i++)
            {
              longString.Add(args[i]);
            }
            this.Settings.Completions[args[0]] = string.Join(" ", longString.ToArray());
          }
          this.Settings.Save();
          break;
        case "disconnect":
          imvm.CloseItem(invm);
          break;
        case "part":
          if (args.Length < 2)
          {
            invm.CloseItem(this);
          }
          else
          {
            for (int i = 0; i < args.Length; i++)
            {
              for (int y = 1; y < invm.Items.Count; y++)
              {
                IrcChannelViewModel icvm = invm.Items[y];
                if (icvm.DisplayName == args[i].ToString())
                  invm.CloseItem(icvm);
              }
            }
          }
          break;
        case "msg":
          IrcPrivateConversationViewModel ipcvm = null;
          IrcUser target = null;
          var longStr = new List<string>();
          for (int i = 1; i < args.Length; i++)
          {
            longStr.Add(args[i]);
          }

          var message = string.Join(" ", longStr.ToArray());

          foreach (var u in this.Channel.Users)
          {
            if (u.User.NickName == args[0])
            {
              target = u.User;
              break;
            }
          }

          if (target == null)
          {
            Console.WriteLine("Error, user does not exist!");
            return;
          }

          foreach (var item in (this.Parent as IrcNetworkViewModel).Items)
          {
            if (item.DisplayName == args[0])
            {
              ipcvm = item;
            }
          }

          if (ipcvm == null)
          {
            ipcvm = new IrcPrivateConversationViewModel(target, this.Channel.Client, this.Settings);
            (this.Parent as IrcNetworkViewModel).Items.Add(ipcvm);
          }

          (this.Parent as IrcNetworkViewModel).ActivateItem(ipcvm);

          this.Channel.Client.LocalUser.SendMessage(args[0], message);

          ipcvm.Messages.Add(new Message(message,
                              DateTime.Now.ToString(this.Settings.TimestampFormat),
                              target.NickName, MessageLevels.Private));

          break;
        case "nick":
          switch (args.Length)
          {
            case 0:
              this.Messages.Add(new Message("You are " + this.Channel.Client.LocalUser.NickName + ".",
                                            DateTime.Now.ToString(this.Settings.TimestampFormat),
                                            "=!=", MessageLevels.Clientside));
              break;
            case 1:
              this.Channel.Client.LocalUser.SetNickName(args[0]);
              this.Messages.Add(new Message("Changed nick to " + args[0] + ".",
                                            DateTime.Now.ToString(this.Settings.TimestampFormat),
                                            "=!=", MessageLevels.Clientside));
              break;
            default:
              this.Messages.Add(new Message("Invalid number of arguments for command '/nick'.",
                                            DateTime.Now.ToString(this.Settings.TimestampFormat),
                                            "=!=", MessageLevels.Clientside, MessageLevels.Error));
              break;
          }
          break;
        case "quit":
          for (int i = 0; i < invm.Items.Count; i++)
          {
            IrcNetworkViewModel net = invm.Items[i];
            imvm.CloseItem(net);
          }
          break;
        case "motd":
          this.Messages.Add(new Message(this.Channel.Client.MessageOfTheDay,
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          break;
        case "names":
          //TODO Nachrichten haben keinen Zeilenumbruch, wenn zu lange
          string users = "";
          foreach (IrcChannelUser iu in this.Users)
          {
            users += iu.User.NickName + ",";
          }
          this.Messages.Add(new Message(users,
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          break;
        default:
          this.Messages.Add(new Message(string.Format("Unknown command \"{0}\".", command),
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=", MessageLevels.Clientside));
          break;
      }
      this.Message = string.Empty;
    }

    public void LeaveChannel(string message)
    {
      // Unsubscribe from all events
      this.Channel.ModesChanged -= this.channelModesChanged;
      this.Channel.UsersListReceived -= this.channelUsersListReceived;
      this.Channel.MessageReceived -= this.channelMessageReceived;
      this.Channel.UserJoined -= this.channelUserJoined;
      this.Channel.UserLeft -= this.channelUserLeft;
      this.Channel.NoticeReceived -= this.channelNoticeReceived;
      this.Channel.TopicChanged -= this.channelTopicChanged;

      this.Channel.Leave(message);<commit_msg>improves a few things<commit_after>﻿// -----------------------------------------------------------------------
// <copyright file="IrcChannelViewModel.cs" company="">
// Copyright (c) 2011 <NAME>, <NAME>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
// -----------------------------------------------------------------------

namespace Handle.WPF
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.IO;
  using System.Media;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Input;
  using System.Windows.Interop;
  using System.Windows.Threading;
  using Caliburn.Micro;
  using IrcDotNet;

  /// <summary>
  /// TODO: Update summary.
  /// </summary>
  public class IrcChannelViewModel : ViewModelBase
  {
    public delegate void JoinChannelClickedEventHandler();
    public event JoinChannelClickedEventHandler JoinChannelClicked;

    private IEventAggregator events;
    private List<string> personalHistory;
    private int historyIndex = -1;
    private Queue<string> tabCompletionQueue;
    private string tabCompletionName;
    private FilterService filterService;

    public IrcChannel Channel { get; set; }

    public BindableCollection<Message> Messages { get; set; }

    private Logger logger;
    private string message;
    public string Message
    {
      get
      {
        return this.message;
      }
      set
      {
        this.message = value;
        NotifyOfPropertyChange(() => this.Message);
        NotifyOfPropertyChange(() => this.CanSend);
      }
    }
    private string topic;

    public string Topic
    {
      get
      {
        return topic;
      }
      set
      {
        this.topic = value;
        NotifyOfPropertyChange(() => this.Topic);
      }
    }

    public bool CanSend
    {
      get
      {
        return !string.IsNullOrWhiteSpace(this.Message);
      }
    }

    public bool Closable { get; set; }

    /// <summary>
    /// Initializes a new instance of the IrcChannelViewModel class
    /// </summary>
    public IrcChannelViewModel(IrcChannel channel, string networkName, Settings settings)
    {
      this.Settings = settings;
      this.Messages = new BindableCollection<Message>();
      this.Closable = true;
      this.DisplayName = channel.Name;
      this.networkName = networkName;
      this.personalHistory = new List<string>();
      this.events = IoC.Get<IEventAggregator>();
      this.filterService = IoC.Get<FilterService>();
      this.Channel = channel;
      this.Channel.ModesChanged += this.channelModesChanged;
      this.Channel.UsersListReceived += this.channelUsersListReceived;
      this.Channel.MessageReceived += this.channelMessageReceived;
      this.Channel.UserJoined += this.channelUserJoined;
      this.Channel.UserLeft += this.channelUserLeft;
      this.Channel.NoticeReceived += this.channelNoticeReceived;
      this.Channel.TopicChanged += this.channelTopicChanged;

      DirectoryInfo di = new DirectoryInfo(Settings.PATH + "\\logs\\");
      if (!di.Exists)
        di.Create();
      if (this.Settings.CanLog)
        this.logger = new Logger(String.Format("{0}\\logs\\{1}.{2}.txt",
                                 Settings.PATH,
                                 channel.Name,
                                 networkName));

      this.Channel.GetTopic();
      this.Users = new List<IrcChannelUser>();
    }

    private void channelUsersListReceived(object sender, EventArgs e)
    {
      this.Users.Clear();
      foreach (var user in this.Channel.Users)
      {
        this.Users.Add(user);
      }
      this.Users.Sort(IrcChannelUserComparison.Compare);
    }

    private void channelModesChanged(object sender, EventArgs e)
    {
    }

    private List<IrcChannelUser> users;

    public List<IrcChannelUser> Users
    {
      get
      {
        return this.users;
      }
      set
      {
        this.users = value;
        NotifyOfPropertyChange(() => this.Users);
      }
    }

    private string displayName;
    private string networkName;

    public override string DisplayName
    {
      get
      {
        return this.displayName;
      }

      set
      {
        this.displayName = value;
      }
    }

    private void channelTopicChanged(object sender, EventArgs e)
    {
      this.Topic = this.Channel.Topic;
    }

    private void channelMessageReceived(object sender, IrcMessageEventArgs e)
    {
      Message m = new Message(e.Text,
                              DateTime.Now.ToString(this.Settings.TimestampFormat),
                              e.Source.Name, MessageLevels.Public);
      if (this.filterService.IsImportant(m.Text))
        m.Levels.Add(MessageLevels.Highlight);

      this.Messages.Add(m);
      if (this.Settings.CanLog && this.logger != null)
        logger.Append(String.Format("{0} {1}: {2}",
                                    m.Received,
                                    m.Sender,
                                    m.Text));
      this.events.Publish(new MessageFilterEventArgs(this.Channel.Name, this.networkName,
                                                     DateTime.Now.ToString("HH:mm"), m.Sender, m.Text));
    }

    private void channelNoticeReceived(object sender, IrcMessageEventArgs e)
    {
      this.Messages.Add(new Message(e.Text, DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=", MessageLevels.Notice));
    }

    private void channelUserJoined(object sender, IrcChannelUserEventArgs e)
    {
      if (this.Settings.AdvertiseJoins)
      {
        this.Messages.Add(new Message(String.Format("{0} [{1}] has joined {2}",
                                                    e.ChannelUser.User.NickName,
                                                    e.ChannelUser.User.HostName,
                                                    e.ChannelUser.Channel.Name),
                                      DateTime.Now.ToString(this.Settings.TimestampFormat),
                                      "=!=",
                                      MessageLevels.Join));
      }
      this.Users.Add(e.ChannelUser);
      this.Users.Sort(IrcChannelUserComparison.Compare);

    }

    private void channelUserLeft(object sender, IrcChannelUserEventArgs e)
    {
      if (this.Settings.AdvertiseParts)
      {
        this.Messages.Add(new Message(String.Format("{0} [{1}] has left {2} [{3}]",
                                                    e.ChannelUser.User.NickName,
                                                    e.ChannelUser.User.HostName,
                                                    e.ChannelUser.Channel.Name,
                                                    e.Comment),
                                      DateTime.Now.ToString(this.Settings.TimestampFormat),
                                      "=!=",
                                      MessageLevels.Part));
      }

      this.Users.Remove(e.ChannelUser);
    }

    public void Send()
    {
      // TODO Ignore adjacent duplicates
      this.personalHistory.Add(this.Message);
      this.historyIndex = -1;

      if (this.Message[0] != '/')
      {
        // Regular message
        this.Channel.Client.LocalUser.SendMessage(this.Channel, this.Message);
        Message m = new Message(this.Message.Trim(),
                                DateTime.Now.ToString(this.Settings.TimestampFormat),
                                this.Channel.Client.LocalUser.NickName,
                                MessageLevels.Public);
        this.Messages.Add(m);
        if (this.Settings.CanLog && this.logger != null)
          logger.Append(string.Format("{0} {1}: {2}", m.Received, m.Sender, m.Text));
        this.Message = string.Empty;
        return;
      }

      string command;
      string[] args;

      this.Message = this.Message.TrimEnd(' ');
      if (this.Message.Contains(" "))
      {
        command = this.Message.Substring(1, this.Message.IndexOf(' ') - 1).ToLower();
        args = this.Message.Substring(this.Message.IndexOf(' '), this.Message.Length - command.Length - 1)
                                  .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
      }
      else
      {
        command = this.Message.Substring(1, this.Message.Length - 1);
        args = new string[] { };
      }

      IrcNetworkViewModel invm = (IrcNetworkViewModel)this.Parent;
      IrcMainViewModel imvm = (IrcMainViewModel)invm.Parent;
      switch (command)
      {
        // TODO No such nick
        case "help":
          // TODO "/help command"
          this.Messages.Add(new Message("Helptext.",
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          break;
        case "join":
        case "j":
          if (args.Length == 1)
          {
            this.Channel.Client.Channels.Join(args[0]);
          }
          else if (args.Length == 2)
          {
            this.Channel.Client.Channels.Join(args[0], args[1]);
          }
          else
          {
            this.Messages.Add(new Message(string.Format("Invalid number of arguments for the command \"{0}\"!", command),
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          }
          break;
        case "whois":
          this.WhoIs(args[0]);
          break;
        case "clear":
          this.ClearMessages();
          break;
        case "complete":
          if (args[0] == "-d" || args[0] == "--delete")
          {
            this.Settings.Completions.Remove(args[1]);
          }
          else
          {
            var longString = new List<string>();
            for (int i = 1; i < args.Length; i++)
            {
              longString.Add(args[i]);
            }
            this.Settings.Completions[args[0]] = string.Join(" ", longString.ToArray());
          }
          this.Settings.Save();
          break;
        case "disconnect":
          imvm.CloseItem(invm);
          break;
        case "part":
          if (args.Length < 2)
          {
            invm.CloseItem(this);
          }
          else
          {
            for (int i = 0; i < args.Length; i++)
            {
              for (int y = 1; y < invm.Items.Count; y++)
              {
                IrcChannelViewModel icvm = invm.Items[y];
                if (icvm.DisplayName == args[i].ToString())
                  invm.CloseItem(icvm);
              }
            }
          }
          break;
        case "msg":
          IrcPrivateConversationViewModel ipcvm = null;
          IrcUser target = null;
          var longStr = new List<string>();
          for (int i = 1; i < args.Length; i++)
          {
            longStr.Add(args[i]);
          }

          var message = string.Join(" ", longStr.ToArray());

          foreach (var u in this.Channel.Users)
          {
            if (u.User.NickName == args[0])
            {
              target = u.User;
              break;
            }
          }

          if (target == null)
          {
            this.Messages.Add(new Message("User does not exist!",
                                          DateTime.Now.ToString(this.Settings.TimestampFormat),
                                          "=!=",
                                          MessageLevels.Clientside, MessageLevels.Error));
            return;
          }

          foreach (var item in (this.Parent as IrcNetworkViewModel).Items)
          {
            if (item.DisplayName == args[0])
            {
              ipcvm = item;
            }
          }

          if (ipcvm == null)
          {
            ipcvm = new IrcPrivateConversationViewModel(target, this.Channel.Client, this.Settings);
            (this.Parent as IrcNetworkViewModel).Items.Add(ipcvm);
          }

          (this.Parent as IrcNetworkViewModel).ActivateItem(ipcvm);

          this.Channel.Client.LocalUser.SendMessage(args[0], message);

          ipcvm.Messages.Add(new Message(message,
                              DateTime.Now.ToString(this.Settings.TimestampFormat),
                              target.NickName, MessageLevels.Private));

          break;
        case "nick":
          switch (args.Length)
          {
            case 0:
              this.Messages.Add(new Message("You are " + this.Channel.Client.LocalUser.NickName + ".",
                                            DateTime.Now.ToString(this.Settings.TimestampFormat),
                                            "=!=", MessageLevels.Clientside));
              break;
            case 1:
              this.Channel.Client.LocalUser.SetNickName(args[0]);
              this.Messages.Add(new Message("Changed nick to " + args[0] + ".",
                                            DateTime.Now.ToString(this.Settings.TimestampFormat),
                                            "=!=", MessageLevels.Clientside));
              break;
            default:
              this.Messages.Add(new Message("Invalid number of arguments for command '/nick'.",
                                            DateTime.Now.ToString(this.Settings.TimestampFormat),
                                            "=!=", MessageLevels.Clientside, MessageLevels.Error));
              break;
          }
          break;
        case "quit":
          for (int i = 0; i < invm.Items.Count; i++)
          {
            IrcNetworkViewModel net = invm.Items[i];
            imvm.CloseItem(net);
          }
          break;
        case "motd":
          this.Messages.Add(new Message(this.Channel.Client.MessageOfTheDay,
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          break;
        case "names":
          //TODO Nachrichten haben keinen Zeilenumbruch, wenn zu lange
          string users = "";
          foreach (IrcChannelUser iu in this.Users)
          {
            users += iu.User.NickName + ",";
          }
          this.Messages.Add(new Message(users,
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), "=!=",
                                        MessageLevels.Clientside));
          break;
        default:
          this.Messages.Add(new Message(string.Format("Unknown command \"{0}\".", command),
                                        DateTime.Now.ToString(this.Settings.TimestampFormat), 
                                        "=!=", 
                                        MessageLevels.Clientside, MessageLevels.Error));
          break;
      }
      this.Message = string.Empty;
    }

    public void LeaveChannel(string message)
    {
      // Unsubscribe from all events
      this.Channel.ModesChanged -= this.channelModesChanged;
      this.Channel.UsersListReceived -= this.channelUsersListReceived;
      this.Channel.MessageReceived -= this.channelMessageReceived;
      this.Channel.UserJoined -= this.channelUserJoined;
      this.Channel.UserLeft -= this.channelUserLeft;
      this.Channel.NoticeReceived -= this.channelNoticeReceived;
      this.Channel.TopicChanged -= this.channelTopicChanged;

      this.Channel.Leave(message);
{'fraction_non_alphanumeric': 0.11394188076063325, 'fraction_numerical': 0.0032458584683222585, 'mean_word_length': 1.5721036465306766, 'pattern_counts': {'":': 26, '<': 55, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 108, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3951604', 'n_tokens_mistral': 8406, 'n_tokens_neox': 8152, 'n_words': 1778}