/**
   * Fetch participants of TaskUpdate. Is called on creation of a new instance of TaskUpdate.
   *
   * @param repoUid the specified repository's identifier.
   * @return new Object[] with length of 2. The first item is a vector of Links that content is
   *     containing ComputerRemotes that are online and participating at the specified repository.
   *     <p>The second entry contains a link between the local computer and the specified
   *     repository.
   */
  private Object[] fetchTaskupdateParticipants(String repoUid) {

    Vector<Link> vec_parti = new Vector<Link>();
    Link ownRepoLink = null;

    for (int j = 0; j < combination.size(); j++) {

      final Link secondLnk = getCombination().get(j);

      if (secondLnk.getRepository().getUid().equals(repoUid)) {

        // inform the specified computer about the
        // changes
        final Computer currentComputer = secondLnk.getComputer();
        if (!currentComputer.matches(ComputerLocal.this)
            && currentComputer instanceof ComputerRemote) {

          vec_parti.addElement(secondLnk);
        } else if (currentComputer.getUid().equals(getUid())) {
          ownRepoLink = secondLnk;
        }
      }
    }

    return new Object[] {vec_parti, ownRepoLink};
  }
  /**
   * @param xlink
   * @return
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  private boolean addLink(final Link xlink) {

    if (xlink == null || combination == null) {
      View.print(getClass() + "Fehler.");
      return false;
    }

    for (int i = 0; i < combination.size(); i++) {
      if (xlink.matches(combination.get(i))) {
        return false;
      }
    }
    combination.add(xlink);
    setChanged();
    notifyObservers(new Object[] {View.ID_UPDATE_LINK, combination});
    save(Constants.getConfigLocal());
    return true;
  }
    /**
     * @author Julius Huelsmann
     * @version %I%, %U%
     * @since 1.0
     */
    public void run() {

      while (!isInterrupted()) {

        if (taskList.isEmpty() && getCombination().isEmpty()) {
          try {
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            interrupt();
          }
        }

        //
        //
        // Check if the connection is established to all the RemoteComputers
        // that are listed as online
        final double currentTime = System.currentTimeMillis();
        for (int i = 0; i < computer.size(); i++) {
          final double dueTime = computer.get(i).getDueContact();
          final double connectionTime = computer.get(i).getContactionThreshold();
          if (computer.get(i).isConnected()) {
            if (currentTime > connectionTime) {
              computer.get(i).setConnected(false);
              Staticio.unskipPort(computer.get(i).getPort());

              setChanged();
              notifyObservers(new Object[] {View.ID_UPDATE_COMPUTER, computer});
            } else if (currentTime > dueTime) {
              computer.get(i).sayHi(ComputerLocal.this);
              System.out.println("sayin hi");

            } else {
              System.out.println("hier" + (currentTime - computer.get(i).getLastContact()));
            }
          }
        }

        final int taskListSize = taskList.size();
        final int totalNumber = taskList.size() + getCombination().size();

        final String identifier =
            "GitThreadRun" + new Random().nextDouble() + " " + System.currentTimeMillis();

        taskLock.add(identifier);
        if (!taskLock.get(0).equals(identifier)) {

          View.print(
              "DEBUG: taskLock.get(0) equalt nicht identifier!"
                  + taskLock.get(0)
                  + ".."
                  + identifier);
          try {
            Thread.sleep(200);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          continue;
        }

        for (int i = 0; i < taskList.size(); i++) {
          setPercentage(1.0 * i / totalNumber);
          if (taskList.get(i).isReadyPerformOperation()) {
            removeTask(i);
            i--;
          }
        }

        setChanged();
        notifyObservers(new Object[] {View.ID_UPDATE_TASKS, taskList, taskListfinished});

        for (int i = 0; i < getCombination().size(); i++) {

          setPercentage(1.0 * (taskListSize + i) / totalNumber);
          //
          // if the current combination contains a repository which
          // is contained by the local Computer check whether changes
          // occurred. If that is the case, send information message
          // to all participants of the repository.
          final Link currentLink = getCombination().get(i);
          final Repository repo = currentLink.getRepository();
          final double timestamp = System.currentTimeMillis();
          if (currentLink.getComputer().getUid().equals(getUid())) {

            final String repoPath = currentLink.getPath();
            final String commitMsg = "auto" + timestamp;
            if (Utils.callScript(
                Constants.UNIX_SRC_GIT_COMMIT,
                Constants.UNIX_DEST_GIT_COMMIT.getAbsolutePath(),
                repoPath + " " + commitMsg,
                true)) {

              //
              // Inform the user about the changes
              View.print("git changes occurred at " + repoPath);

            } else {

              continue;
            }

            // Find all the computers connected ot the
            final Vector<Link> vec_parti =
                (Vector<Link>) fetchTaskupdateParticipants(repo.getUid())[0];

            // important: initialize TaskUpdate before sending
            // signal.
            TaskUpdate tu = new TaskUpdate(vec_parti, currentLink, ComputerLocal.this);
            if (!tu.add(
                TaskUpdate.generateInformationString(
                    repo.getUid(), ComputerLocal.this.getUid(), "" + timestamp))) {

              View.print(
                  "Error: new created task does not accept "
                      + "information on the local computer.");
            }
            taskList.add(tu);

            //
            // Inform every client that is online to update the repository.
            for (int j = 0; j < vec_parti.size(); j++) {

              ((ComputerRemote) vec_parti.get(j).getComputer())
                  .repositoryUpdated(getUid(), repo.getUid(), timestamp);
            }
          }
        }

        removeTasklock(identifier);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          View.print("interrupted");
          interrupt();
        }
      }
    }
  /**
   * @param xrepository
   * @param xpath
   * @return
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public String becomeRepositorymember(final Repository xrepository, final String xpath) {

    if (combination.size() == 0) {
      return "ERROR: no combination found.";
    }

    Link linkSelected = null;
    int index = 0;
    do {
      linkSelected = combination.get(index);
      index++;
    } while ((!linkSelected.getRepository().matches(xrepository)
            || linkSelected.getComputer() instanceof ComputerLocal
            || !((ComputerRemote) linkSelected.getComputer()).isConnected())
        && index < combination.size());

    if (linkSelected == null
        || !linkSelected.getRepository().matches(xrepository)
        || linkSelected.getComputer() instanceof ComputerLocal
        || !((ComputerRemote) linkSelected.getComputer()).isConnected()) {
      return "ERROR: No link between a computer that is online and the "
          + "specified repository found. Try again later.";
    }

    /*
     *  $1  the repository's root directory,
     *  $2  the repository's name.
     *  $3  the remote repository's url and path
     */
    final String computerUrl = ((ComputerRemote) linkSelected.getComputer()).getAddress();
    // TODO: hard-coded git user. change to git eitherway
    final String parameter =
        xpath
            + " "
            + linkSelected.getRepository().getName()
            + " juli@"
            + computerUrl
            + ":"
            + linkSelected.getPath();

    //
    // create directory to which is cloned
    new File(xpath).mkdirs();

    //
    //
    final boolean b =
        Utils.callScript(
            Constants.UNIX_SRC_GIT_CLONE,
            Constants.UNIX_DEST_GIT_CLONE.getAbsolutePath(),
            parameter,
            true);

    if (b) {

      //
      // Add new link to the vector of links and inform the remote computers
      // the changes.
      combination.add(new Link(xpath, this, xrepository));
      broadcastInformation();

      return "Successfully cloned repository from remote computer\n"
          + "remote ID  :\t"
          + linkSelected.getComputer().getUid()
          + "\n"
          + "remote IP  :\t"
          + computerUrl
          + "\n"
          + "remote path:\t"
          + linkSelected.getPath()
          + "\n"
          + "Destination:\t"
          + xpath
          + "."
          + "\n"
          + "Callparam. :\t"
          + parameter;

    } else {
      return "Failed to clone repository from remote computer\n"
          + "remote ID  :\t"
          + linkSelected.getComputer().getUid()
          + "\n"
          + "remote IP  :\t"
          + computerUrl
          + "\n"
          + "remote path:\t"
          + linkSelected.getPath()
          + "\n"
          + "Destination:\t"
          + xpath
          + "."
          + "\n"
          + "Callparam. :\t"
          + parameter;
    }
  }
  /**
   * Generate information string on the local computer configuration (includes {@link #computer},
   * {@link #repositories}, {@link #combination}).
   *
   * @return whether new changes have occurred due to the update.
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public boolean convertInformationString(final String xstring, final String xadr) {

    // go through all the identifiers and generate Vector of Computers,
    // Repositories and Link-Combinations
    boolean changesOccurred = false;

    //
    // Fetch information from string
    final int occurr1 = xstring.indexOf(Utils.invalidCharLevel1);
    final String remoteUid = xstring.substring(0, occurr1);
    final String rest00 = xstring.substring(occurr1 + 1, xstring.length());
    final String port = rest00.substring(0, rest00.indexOf(Utils.invalidCharLevel1));
    final int iport = Integer.parseInt(port);

    boolean found = false;
    for (int i = 0; i < computer.size(); i++) {
      if (computer.get(i).getUid().equals(remoteUid)) {
        found = true;

        if (computer.get(i).getPort() != iport
            || computer.get(i).getAddress().equals(xadr)
            || computer.get(i).isConnected() == false) {
          changesOccurred = true;
        }
        computer.get(i).setPort(iport);
        computer.get(i).setAddress(xadr);
        computer.get(i).setConnected(true);
        computer.get(i).setContacted();
      }
    }

    // if the computer has not been found inside the list of computers, add
    // it.
    if (!found) {
      ComputerRemote cr = new ComputerRemote(remoteUid, xadr, iport);
      cr.setConnected(true);
      addComputer(cr);
      changesOccurred = true;
      cr.setContacted();
    }

    // TODO: Check whether the computer with the UID has already been added
    // to the list of computers.
    String remainingString =
        xstring.substring(remoteUid.length() + port.length() + 2, xstring.length());

    String strgComputer =
        remainingString.substring(0, remainingString.indexOf(Utils.invalidCharLevel1));
    remainingString =
        remainingString.substring(strgComputer.length() + 1, remainingString.length());
    String strgRepositories =
        remainingString.substring(0, remainingString.indexOf(Utils.invalidCharLevel1));
    remainingString =
        remainingString.substring(strgRepositories.length() + 1, remainingString.length());
    String strgCombination = remainingString;

    //
    // ComputerRemote
    for (int i = strgComputer.indexOf(Utils.invalidCharLevel2);
        i != -1;
        i = strgComputer.indexOf(Utils.invalidCharLevel2)) {
      final String strgComputerSingle = strgComputer.substring(0, i);
      strgComputer = strgComputer.substring(i + 1, strgComputer.length());

      ComputerRemote comp = ComputerRemote.convertInformationString(strgComputerSingle);
      changesOccurred = addComputer(comp) || changesOccurred;
    }

    //
    // Repository
    for (int i = strgRepositories.indexOf(Utils.invalidCharLevel2);
        i != -1;
        i = strgRepositories.indexOf(Utils.invalidCharLevel2)) {
      final String strgSingle = strgRepositories.substring(0, i);
      strgRepositories = strgRepositories.substring(i + 1, strgRepositories.length());

      Repository repo = Repository.convertInformationString(strgSingle);

      changesOccurred = addRepository(repo) || changesOccurred;
    }

    //
    // Links
    // Important: the Links can ony be added to the vector of
    for (int i = strgCombination.indexOf(Utils.invalidCharLevel2);
        i != -1;
        i = strgCombination.indexOf(Utils.invalidCharLevel2)) {
      final String strgSingle = strgCombination.substring(0, i);
      strgCombination = strgCombination.substring(i + 1, strgCombination.length());

      Link clink = Link.convertInformationString(strgSingle, repositories, computer);
      changesOccurred = addLink(clink) || changesOccurred;
    }

    View.print("Something changed: " + changesOccurred);
    if (changesOccurred) {
      changed();
    }

    // Return whether the above-created computers, combinations and
    // repositories have been already added to list.
    return changesOccurred;
  }
  /**
   * Create new instance of TaskUpdate from received message
   *
   * @param xmessageRest
   * @return
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public final synchronized boolean newTaskUpdate(final String xmessageRest) {

    //
    // Enable task-lock.
    final String identifier =
        "newTaskUpdate" + new Random().nextDouble() + " " + System.currentTimeMillis();
    taskLock.add(identifier);

    //
    // In case the task is not locked, continue. Otherwise go to else clause
    // that waits for a few milliseconds and recalls the function afterwards.
    if (taskLock.get(0).equals(identifier)) {

      //
      // Check if task already exists that is working with the same repository
      // and add string to the task. May occur here due to task-lock-delay.
      if (forewardTaskMessage(xmessageRest)) {
        return true;
      }

      //
      // Here it is clear that the task does not exist and that a new task has
      // to be created. Thus save the current time as an identifier of the
      // update time.
      double timestamp = System.currentTimeMillis();
      final String[] extractedInfo = TaskUpdate.fetchInformation(xmessageRest);
      final String repoUid = extractedInfo[0];

      //
      // Fetch participants of the new task for creating the task.
      Object[] o = fetchTaskupdateParticipants(repoUid);
      final Vector<Link> vec_parti = (Vector<Link>) o[0];
      final Link ownRepoLink = (Link) o[1];

      if (ownRepoLink != null && !vec_parti.isEmpty()) {

        final String repoPath = ownRepoLink.getPath();
        final String commitMsg = "auto" + timestamp;
        if (!Utils.callScript(
            Constants.UNIX_SRC_GIT_COMMIT,
            Constants.UNIX_DEST_GIT_COMMIT.getAbsolutePath(),
            repoPath + " " + commitMsg,
            true)) {
          timestamp = TaskUpdate.VALUE_NO_UPDATE;
        } else {
          View.print(
              getClass().getSimpleName()
                  + " Error. local computer "
                  + "out of sync?"
                  + ownRepoLink
                  + vec_parti.isEmpty());
        }
        final TaskUpdate tu = new TaskUpdate(vec_parti, ownRepoLink, this);
        taskList.add(tu);
        if (!tu.add(xmessageRest)) {
          View.print("ERROR in COMPUTERLOCAL COULD NOT ADD " + xmessageRest);

          final String newId = taskLock.remove(0);
          if (!newId.equals(identifier)) {
            View.print("Fatal error in newTaksupdate 1");
          }
          return false;
        }
        final String myself =
            Task.COMMUNIICATION_CHANGES
                + ownRepoLink.getRepository().getUid()
                + Utils.invalidCharLevel1
                + getUid()
                + Utils.invalidCharLevel1
                + timestamp;
        if (!tu.add(myself)) {

          View.print("ERROR in COMPUTERLOCAL COULD NOT ADD " + xmessageRest);

          final String newId = taskLock.remove(0);
          if (!newId.equals(identifier)) {
            View.print("Fatal error in newTaksupdate 2");
          }
          return false;
        }

        final String newId = taskLock.remove(0);
        if (!newId.equals(identifier)) {
          View.print("Fatal error in newTaksupdate 3");
        }
        return true;
      }

      // update participants

      final String newId = taskLock.remove(0);
      if (!newId.equals(identifier)) {
        View.print("Fatal error in newTaksupdate 4");
      }
      return false;
    } else {
      try {
        Thread.sleep(200);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      return newTaskUpdate(xmessageRest);
    }
  }