/**
   * Merges to user lists. New users will be added and old users will be updated.
   *
   * @param aOriginal Original {@link List} of {@link User}.
   * @param aChanges New {@link List} of {@link User} to merge into original list.
   * @return Merges {@link List} of {@link User}.
   */
  public static List<User> merge(List<User> aOriginal, List<User> aChanges) {
    List<User> vUsers = new ArrayList<User>();

    // delete original users not in changes, not manually added and without letters
    for (Iterator<User> vIterator = aOriginal.iterator(); vIterator.hasNext(); ) {
      User vUser = vIterator.next();
      List<User> vUsersSelected =
          UserListUtils.selectUnique(aChanges, vUser.getName(), vUser.getBirthdate());

      if (vUsersSelected.size() == 0 && !vUser.isManualAdded()) {
        User vUserDeleted = new User(vUser);
        vUsers.add(vUserDeleted.setName(null));
        vIterator.remove();
      }
    }

    // update and add user data
    List<User> vUnassignedUsers = new ArrayList<User>(); // list of users with more unique
    // users in original list
    for (Iterator<User> vIterator = aChanges.iterator(); vIterator.hasNext(); ) {
      User vUser = vIterator.next();
      List<User> vUsersSelected =
          UserListUtils.selectUnique(aOriginal, vUser.getName(), vUser.getBirthdate());

      if (vUsersSelected.size() == 1) {
        // one unique user found > update user
        User vUserSelected = new User(vUsersSelected.get(0)).update(vUser);
        vUsers.add(vUserSelected);
        vIterator.remove();

      } else if (vUsersSelected.size() == 0) {
        // no user found > check if to find without birthdate
        List<User> vUsersByName = UserListUtils.selectUnique(aOriginal, vUser.getName(), "");
        if (vUsersByName.size() == 1) {

          // found user > update
          User vUserSelected = new User(vUsersByName.get(0)).update(vUser);
          vUsers.add(vUserSelected);

        } else {

          // not found by name > add user
          User vNewUser = new User(vUser);
          vUsers.add(vNewUser);
          vIterator.remove();
        }

      } else if (vUnassignedUsers.size() > 1) {
        // more than one unique user found > add to list for further processing
        vUnassignedUsers.add(vUser);
        vIterator.remove();
      }
    }

    // process unassigned users
    // TODO
    logger.warn("Unassigned users " + vUnassignedUsers.size());

    return vUsers;
  }
  /**
   * Gets userdata from row.
   *
   * @param aRow {@link HSSFRow} to get userdata from.
   * @param aColumns Array of {@link Integer} that specify the userdata columns. See {@code
   *     getUserdataColumnsFromSheet()}.
   * @param aHouse {@link String} of users house name.
   * @return {@link User} if data found, {@code null} otherwise.
   */
  private User getUser(HSSFRow aRow, int[] aColumns, String aHouse) {
    if (aRow != null && aColumns.length > 3) {
      // convert cell types to string
      convertCellTypes(aRow, Arrays.copyOf(aColumns, 3), HSSFCell.CELL_TYPE_STRING);
      // convertCellTypes( aRow, new int[]{aColumns[3]}, HSSFCell.CELL_TYPE_NUMERIC );

      // get cells
      HSSFCell vCellName = aRow.getCell(aColumns[0]);
      HSSFCell vCellFloor = aRow.getCell(aColumns[1]);
      HSSFCell vCellRoom = aRow.getCell(aColumns[2]);
      HSSFCell vCellBirthdate = aRow.getCell(aColumns[3]);

      // check cells and create user
      if (vCellName != null && vCellFloor != null && vCellRoom != null && vCellBirthdate != null) {
        try {

          String vName = vCellName.getStringCellValue().trim();
          String vFloor = vCellFloor.getStringCellValue().trim();
          String vRoom = vCellRoom.getStringCellValue().trim();
          Date vBirthdate = (new SimpleDateFormat("dd.MM.yyyy")).parse("00.00.0000");
          if (vCellBirthdate.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {
            vBirthdate = vCellBirthdate.getDateCellValue();
          }

          if (vName.length() > 0) {
            User vUser = new User(-1);
            vUser.setName(vName);
            vUser.setHouse(aHouse);
            vUser.setFloor(vFloor);
            vUser.setRoom(vRoom);
            if (vBirthdate != null) {
              SimpleDateFormat vDateFormat = new SimpleDateFormat("dd.MM.yyyy");
              String vDateString = vDateFormat.format(vBirthdate);
              vUser.setBirthdate(vDateString);
            }

            return vUser;
          }
        } catch (Exception e) {
          e.printStackTrace();
          logger.error("Error importing data from row " + aRow.getRowNum());
          System.exit(0);
        }
      }
    }

    return null;
  }
  /**
   * Imports user data from csv file.
   *
   * @param aCsvFile {@link File} csv file to import.
   * @return {@link List} of {@link User} from csv file.
   */
  private List<User> getUsers(File aCsvFile) {
    List<User> vUsers = new ArrayList<User>();

    if (aCsvFile != null & aCsvFile.exists() && aCsvFile.isFile()) {
      try {

        BufferedReader vReader =
            new BufferedReader(new InputStreamReader(new FileInputStream(aCsvFile), "UTF-16"));

        // get header
        String vLine;
        if ((vLine = vReader.readLine()) != null) {
          int[] vColumns = getUserdataColumnsFromSheet(vLine.toLowerCase().split("\t"));
          logger.debug("Extracted file columns: ");
          for (int i : vColumns) logger.debug("\t" + i);

          if (validateColumnsData(vColumns)) {
            // read user data
            while ((vLine = vReader.readLine()) != null) {

              String[] vUserData = vLine.split("\t");
              if (getMaxColumnNum(vColumns) < vUserData.length) {

                String vPreName = vUserData[vColumns[0]].trim();
                String vLastName = vUserData[vColumns[1]].trim();
                String vHouse = vUserData[vColumns[2]].replace("Haus", "").trim();
                String vRoom = vUserData[vColumns[3]].trim();
                String vBirthdate = vUserData[vColumns[4]].trim();

                if (vLastName.length() > 0) {
                  User vUser = new User(-1);
                  vUser.setName(vLastName + ", " + vPreName);
                  vUser.setHouse(vHouse);
                  vUser.setRoom(vRoom);
                  vUser.setBirthdate(vBirthdate);

                  vUsers.add(vUser);
                }

              } else {
                logger.warn("Line doesn't contain number of required information:\n\t" + vLine);
              }
            }
          }
        } else {
          logger.warn("Cannot read header from file " + aCsvFile.getPath());
        }

        vReader.close();

      } catch (FileNotFoundException e) {
        e.printStackTrace();
        logger.error("Cannot find file for import: " + aCsvFile.getPath());
      } catch (IOException e) {
        e.printStackTrace();
        logger.error(e.getMessage());
      }
    }

    return vUsers;
  }