/**
   * @param xcomputer
   * @return
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  private boolean addComputer(final ComputerRemote xcomputer) {

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

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

      //
      // In case the computer has already been added or the computer to add
      // is the local computer, return false.
      if (xcomputer.matches(computer.get(i))) {
        return false;
      } else if (xcomputer.getUid().equals(getUid())) {
        return false;
      }
    }
    Staticio.skipPort(xcomputer.getPort());
    computer.add(xcomputer);

    setChanged();
    notifyObservers(new Object[] {View.ID_UPDATE_COMPUTER, computer});
    save(Constants.getConfigLocal());
    return true;
  }
    private void removeTasklock(final String xoldid) {

      final String newId = taskLock.remove(0);

      if (!newId.equals(xoldid)) {
        View.print("Fatal error in removing task lock" + xoldid + ". removed " + newId);
      }
    }
  /**
   * Initialize a new (local) repository which will be provided to remote computers afterwards.
   *
   * @param xpath the path to the repository at local computer
   * @param xdescription Description of the repository (is stored inside the repository's
   *     configuration file)
   * @param xname the repository's name.
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public boolean initializeRepository(
      final String xpath, final String xdescription, final String xname) {

    //
    // Create folder and call the initialize - script.
    final boolean success = new File(xpath).mkdirs();
    final boolean success3 =
        Utils.callScript(
            Constants.UNIX_SRC_GIT_INIT,
            Constants.UNIX_DEST_GIT_INIT.getAbsolutePath(),
            xpath + " " + xname,
            true);
    if (!success3) {

      // Inform the user on the failure
      View.print(
          "Failed to create repository "
              + xname
              + " at "
              + xpath
              + ":\n"
              + "\tSuccess creating folder: "
              + success
              + "\tSuccess executing script: "
              + success3);
    } else {

      //
      // Inform the user on the success, add new repository and link to the
      // suitable vectors
      View.print("Successfully created repository " + xname + " at " + xpath + ".");
      final Repository rep = new Repository(xname, new Random().nextDouble(), xdescription);

      addRepository(rep);
      addLink(new Link(xpath, this, rep));

      //
      // inform the remote computers on the new computer
      broadcastInformation();
    }

    return success3;
  }
  /**
   * Handle HI message.
   *
   * @param xaddr
   */
  public final void handleHi(final String xaddr, final String xuid) {
    for (int i = 0; i < computer.size(); i++) {
      if (computer.get(i).getAddress().equals(xaddr) && computer.get(i).getUid().equals(xuid)) {

        computer.get(i).setContacted();
      } else if (computer.get(i).getAddress().equals(xaddr)
          || computer.get(i).getUid().equals(xuid)) {
        View.print("Wrong computer UID or address.");
        computer.get(i).setConnected(false);
      }
    }
  }
  /**
   * Save the instance of {@link #ComputerLocal(String)} to the specified location.
   *
   * @param xconfigLocal the specified location
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public void save(final File xconfigLocal) {
    final GitThread t = gitThread;
    gitThread = null;
    ObjectOutputStream oos;
    try {
      FileOutputStream fos = new FileOutputStream(xconfigLocal);
      oos = new ObjectOutputStream(fos);
      oos.writeObject(this);
      oos.flush();
      oos.close();
    } catch (IOException e) {
      View.print(
          "The configuration could not be saved because the " + "folder does not exist: " + e);
    }

    gitThread = t;
  }
  /**
   * @param xrepository
   * @return
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  private boolean addRepository(final Repository xrepository) {

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

    for (int i = 0; i < repositories.size(); i++) {
      if (xrepository.matches(repositories.get(i))) {
        return false;
      }
    }
    repositories.add(xrepository);
    setChanged();
    notifyObservers(new Object[] {View.ID_UPDATE_REPO, repositories});
    save(Constants.getConfigLocal());
    return true;
  }
  /**
   * @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;
  }
  /**
   * This function is called once for starting to scan for other copies of the program inside the
   * local network. It generates {@value #amountOfThreads} threads by calling the private method
   * {@link #getNewClient(int, int, int, ComputerLocal)}.
   *
   * @param xcl instance of the Local Computer class that handles the received information -
   *     strings.
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public static void startScanning(final ComputerLocal xcl) {

    //
    // In case local testing is enabled, do not scan for remote computers
    // but use the local IP and given port.
    if (Constants.isLocalTesting()) {

      requestLocalServerInformation(xcl);
    } else {

      //
      // Initialize the Vector which contains information on the current
      // process of each XTreaead that is created in the following.
      currentProgress = new Vector<Integer>();

      //
      // Print information message and compute the range of one thread.
      // Afterwards initialize #amountOfThreads threads by calling the method
      // #getNewClient.
      View.print("Start scanning for clients in " + amountOfThreads + " Threads.");
      final int threadRange =
          (Constants.globalPortEnd - Constants.globalPortStart) / amountOfThreads;
      int sum = Constants.globalPortStart;
      for (int i = 0; i < amountOfThreads; i++) {

        // Add the lower range of scanning of the client to the progress vector
        // and generate upper range by adding the threadRange.
        currentProgress.add(sum);
        int sump = sum + threadRange;

        // Create new client and set the sum - integer to the upper range of
        // the current process.
        getNewClient(i, sum, sump, xcl);
        sum = sump;
      }
    }
  }
  /**
   * @param xia
   * @param xport
   * @author Julius Huelsmann
   * @version %I%, %U%
   * @since 1.0
   */
  public final void generateAddclientTask(final InetAddress xia, final int xport) {

    // If the address - port combination is unknown, generate new task to add
    // the client
    boolean found = false;
    for (int i = 0; i < computer.size(); i++) {
      if (computer.get(i).getAddress().equals(xia.getHostAddress())) {
        found = true;

        if (xport != computer.get(i).getPort()) {
          computer.get(i).setPort(xport);
          View.print(
              "Stored wrong port identifier for client with ip " + computer.get(i).getAddress());
        }
      }
    }

    //
    // In case the remote computer has not already been added, create new
    // instance of TaskAddclient.
    if (!found) {
      taskList.add(new TaskAddclient(this, xia, xport));
    }
  }
    /**
     * @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();
        }
      }
    }
  /**
   * 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);
    }
  }