/** Loads the mail configuration. */
  public static void loadMailConfiguration() {
    try {
      DATFileReader reader = new DATFileReader(MAIL_PATH);
      reader.setCommentChar('#');
      reader.ignoreWhitespaces(true);

      // Read file line by line
      while (reader.loadNextLine()) {
        // Read data from current line
        String systemName = reader.nextField();
        String userName = reader.nextField();
        String sender = reader.nextField();
        String date = reader.nextField();
        String subject = reader.nextField();
        String resourceName = reader.nextField();

        Server system = Terminal.getServer(systemName);
        UserAccount user = system.getUser(userName);
        Mail m = new Mail(sender, date, subject, resourceName);
        user.getMailbox().addMail(m);

        Logger.info(
            "Loaded mail: subject: %s (user: %s, system: %s%s)\n",
            subject.isEmpty() ? "<no subject>" : subject,
            userName,
            systemName,
            resourceName.isEmpty() ? "" : ", resource: " + resourceName);
      }
    } catch (IOException ex) {
      Logger.stackTrace(ex);
      showConfigFileErrorMessage(ex);
    }
  }
  /**
   * Loads user configuration
   *
   * @return the last loaded user account
   */
  public static UserAccount loadUserConfiguration() {
    UserAccount u = null;

    try {
      DATFileReader reader = new DATFileReader(USERS_PATH);
      reader.setCommentChar('#');
      reader.ignoreWhitespaces(true);

      // Read file line by line
      while (reader.loadNextLine()) {
        // Read data from current line
        String systemName = reader.nextField();
        String username = reader.nextField();
        String password = reader.nextField();
        int homeDirID = Integer.parseInt(reader.nextField());
        boolean isUnlisted = Boolean.parseBoolean(reader.nextField());

        Server system = Terminal.getServer(systemName);
        HomeDirectory homeDir =
            (HomeDirectory) system.getFileSystem().getFileSystemObject(homeDirID);
        homeDir.setUnlisted(isUnlisted);
        u = new UserAccount(username, password, homeDir);
        system.addUser(u);

        Logger.info("Loaded user: %s (system: %s)\n", username, systemName);
      }
    } catch (IOException ex) {
      Logger.stackTrace(ex);
      showConfigFileErrorMessage(ex);
    }

    return u;
  }
  /**
   * Loads server configuration.
   *
   * @return the last loaded server
   */
  public static Server loadServerConfiguration() {
    Server s = null;

    try {
      DATFileReader reader = new DATFileReader(SERVERS_PATH);
      reader.setCommentChar('#');
      reader.ignoreWhitespaces(true);

      // Read file line by line
      while (reader.loadNextLine()) {
        // Read data from current line
        String serverName = reader.nextField();
        String loginMessage = reader.nextField();
        s = new Server(serverName, loginMessage);
        Terminal.addServer(s);

        Logger.info("Loaded server: %s\n", serverName);
      }

    } catch (IOException ex) {
      Logger.stackTrace(ex);
      showConfigFileErrorMessage(ex);
    }

    return s;
  }
  /*
   * Loads the list of files.
   */
  private static FileSystem loadFiles(Map<String, Class<? extends ExecutableFile>> exes) {
    FileSystem tempFileSystem = new FileSystem(new Directory(0, ""));
    try {
      DATFileReader reader = new DATFileReader(FILES_PATH);
      reader.setCommentChar('#');
      reader.ignoreWhitespaces(true);

      // Read file line by line
      while (reader.loadNextLine()) {
        // Read data from current line
        int id = Integer.parseInt(reader.nextField());
        String fileName = reader.nextField();
        boolean isHidden = Boolean.parseBoolean(reader.nextField());
        String resourceName = reader.nextField();
        String aliasIDStr = "";
        if (reader.hasNextField()) {
          aliasIDStr = reader.nextField();
        }

        File f;
        if (!aliasIDStr.isEmpty()) {
          // Resolve alias target if the file is an alias
          f = new File(id, fileName);
          int aliasID = Integer.parseInt(aliasIDStr);
          File aliasTarget = (File) tempFileSystem.getFileSystemObject(aliasID);
          f.markAsAlias(aliasTarget);
        } else if (id > 100 && id < 200) {
          // Match file to an ExecurableFile subclass
          Class<? extends ExecutableFile> clazz = exes.get(fileName);
          if (clazz == null) {
            Logger.error("Unresolved executable: %s (id: %d)\n", fileName, id);
            continue;
          }

          // Create instance of subclass
          Constructor<? extends ExecutableFile> constructor = clazz.getConstructor(int.class);
          f = constructor.newInstance(id);
        } else if (id > 300 && id < 400) {
          // Load file as a text file
          f = new TextFile(id, fileName, resourceName);
        } else if (id > 400 && id < 500) {
          // Load file as an image file
          f = new ImageFile(id, fileName, resourceName);
        } else if (id > 500 && id < 600) {
          // Load file as a sound file
          f = new SoundFile(id, fileName, resourceName);
        } else {
          // Load generic file
          f = new File(id, fileName);
        }

        f.setHidden(isHidden);

        // Add file to temporay filesystem
        tempFileSystem.getRoot().addChild(f);

        Logger.info(
            "Loaded file: %s (id: %d%s)\n",
            fileName, id, resourceName.isEmpty() ? "" : ", resource: " + resourceName);
      }
    } catch (IOException ex) {
      Logger.stackTrace(ex);
      showConfigFileErrorMessage(ex);
    } catch (InstantiationException | IllegalAccessException ex) {
      throw new RuntimeException(ex);
    } catch (NoSuchMethodException | SecurityException ex) {
      throw new RuntimeException(ex);
    } catch (IllegalArgumentException | InvocationTargetException ex) {
      throw new RuntimeException(ex);
    }

    return tempFileSystem;
  }
  /**
   * Loads the filesystem configuration.
   *
   * @param exes a Map containing classes representing executable files and their corresponding
   *     names
   */
  public static void loadFileSystemConfiguration(
      Map<String, Class<? extends ExecutableFile>> exes) {
    /* Load files into temporary filesystem. The temporary filesytem is used
       to access files while the actual filesystem is being built.
    */
    FileSystem tempFileSystem = loadFiles(exes);

    try {
      DATFileReader reader = new DATFileReader(FILESYSTEM_PATH);
      reader.setCommentChar('#');
      reader.ignoreWhitespaces(true);

      // Read file line by line
      while (reader.loadNextLine()) {
        // Read data from current line
        int id = Integer.parseInt(reader.nextField());
        String systemName = reader.nextField();
        String dirName = reader.nextField();
        int parentID = Integer.parseInt(reader.nextField());
        String files = "";
        if (reader.hasNextField()) {
          files = reader.nextField();
        }

        Server system = Terminal.getServer(systemName);

        Directory dir;
        if (parentID == -1 || (id > 600 && id < 700)) {
          // Load directory, set it as root on the current system
          dir = new Directory(id, dirName);
          system.setFileSystem(new FileSystem(dir));
        } else if (id > 800 && id < 900) {
          // Load directory as as user's home directory
          dir = new HomeDirectory(id, dirName);
        } else {
          // Load generic directory
          dir = new Directory(id, dirName);
        }

        /* Place the directory in the correct location in the filesystem
           tree
        */
        FileSystemObject parent = system.getFileSystem().getFileSystemObject(parentID);
        if (parent != null) {
          parent.addChild(dir);
        }

        // Populate directory with files (if it has any)
        String[] fileIDList = files.split(" ");
        for (String fileIDStr : fileIDList) {
          if (fileIDStr.isEmpty()) {
            continue;
          }
          int fileID = Integer.parseInt(fileIDStr);

          // Get file from temporary filesystem based on file ID
          File file = (File) tempFileSystem.getFileSystemObject(fileID);
          if (file == null) {
            Logger.error(
                "Unresolved file ID: %d " + "(dir: %s, system: %s)\n", fileID, dirName, systemName);
            continue;
          }

          // Place the file in the directory
          dir.addChild(file);
        }

        Logger.info(
            "Loaded directory: %s (system: %s, id: ID)\n",
            dirName.isEmpty() ? "<no name, root?>" : dirName, systemName, id);
      }
    } catch (IOException ex) {
      Logger.stackTrace(ex);
      showConfigFileErrorMessage(ex);
    }
  }