/**
  * Set the value of a property by its index
  *
  * @param index the index of the property
  * @param state the state of the property
  * @param value the value of the property
  */
 public void setValue(int index, State state, String value) {
   // only update if it shall override
   if (state.compareTo(states[index]) >= 0) {
     values[index] = value;
     states[index] = state;
   }
 }
 /**
  * Set the value for a specific property.
  *
  * @param property the property
  * @param state the state of the property
  * @param value the value of the property
  */
 public void setValue(Property property, State state, String value) {
   // only update if it shall override
   if (state.compareTo(states[property.ordinal()]) >= 0) {
     values[property.ordinal()] = value;
     states[property.ordinal()] = state;
   }
 }
  /**
   * Handles event that a submodule's out-port received a value.
   *
   * <p>This method starts copying the value to all ports that are connected via an outgoing
   * connection. Once all copy operations are completed (asynchronously), a {@link
   * SubmoduleOutPortNoLongerNeeded} message will be send to this actor.
   *
   * <p>This method does not finish any asynchronous actions. Instead, the asynchronous action
   * waiting for the submodule out-port is finished in {@link
   * #submoduleOutPortNoLongerNeeded(RuntimeOutPort)}.
   *
   * @param moduleId index of the module in {@link RuntimeParentModule#getModules()} that contains
   *     the out-port
   * @param outPortId index of the out-port in {@link RuntimeModule#getOutPorts()}
   */
  private void submoduleOutPortHasSignal(int moduleId, int outPortId) {
    assert state.compareTo(State.RUNNING) >= 0;

    final RuntimeOutPort outPort = module.getModules().get(moduleId).getOutPorts().get(outPortId);

    if (submoduleOutPortsReceivedMessage[moduleId].get(outPortId)) {
      log.warning(
          String.format(
              "Ignoring redundant message that %s of submodule '%s' provided a value.",
              outPort, outPort.getModule().getSimpleName()));
    } else if (isOutPortNeeded(outPort)) {
      submoduleOutPortsReceivedMessage[moduleId].set(outPortId);
      List<CompletableFuture<Void>> copyFutures =
          outPort
              .getOutConnections()
              .stream()
              .filter(this::isConnectionToRecomputedNode)
              .map(connection -> connection.accept(transmitFromSourcePortVisitor, outPort))
              .collect(Collectors.toList());
      CompletableFuture<Object> messageFuture =
          Futures.collect(copyFutures)
              .thenApply(ignored -> new SubmoduleOutPortNoLongerNeeded(outPort));
      pipeResultToSelf(
          messageFuture,
          "copying value of %s of submodule to out-connections",
          outPort,
          outPort.getSimpleName());
    } else {
      log.warning(
          String.format(
              "Ignoring unexpected message that %s of submodule '%s' provided a value because it is not needed.",
              outPort, outPort.getSimpleName()));
    }
  }
  /**
   * Handles event that the value of a submodule's out-port is no longer needed.
   *
   * <p>If the submodule of the given out-port no longer has any (other) out-port whose value is
   * required, this method starts (asynchronously) cleaning all intermediate results for this
   * module.
   *
   * <p>This method also finishes the asynchronous action (waiting for submodule out-port) started
   * previously in {@link #getChildExecutor}.
   *
   * @param outPort out-port whose value is no longer needed
   */
  private void submoduleOutPortNoLongerNeeded(RuntimeOutPort outPort) {
    assert state.compareTo(State.RUNNING) >= 0;

    RuntimeModule submodule = outPort.getModule();
    BitSet neededOutPorts = computeResumeState.getSubmodulesNeededOutPorts()[submodule.getIndex()];
    neededOutPorts.set(outPort.getOutIndex(), false);
    if (neededOutPorts.isEmpty() && getInterpreterProperties().isCleaningRequested()) {
      ExecutionTrace executionTrace =
          ExecutionTrace.empty().resolveContent().resolveModule(submodule.getSimpleName());
      CompletableFuture<Void> future = stagingArea.delete(executionTrace);
      awaitAsynchronousAction(
          future, "cleaning up intermediate output of submodule '%s'", submodule.getSimpleName());
    }

    endAsynchronousAction(outPort);
  }
 /**
  * Gets the client's refund transaction which they can spend to get the entire channel value back
  * if it reaches its lock time.
  */
 public synchronized long getRefundTransactionUnlockTime() {
   checkState(state.compareTo(State.WAITING_FOR_MULTISIG_CONTRACT) > 0 && state != State.ERROR);
   return refundTransactionUnlockTimeSecs;
 }
  /**
   * Closes this channel and broadcasts the highest value payment transaction on the network.
   *
   * <p>This will set the state to {@link State#CLOSED} if the transaction is successfully broadcast
   * on the network. If we fail to broadcast for some reason, the state is set to {@link
   * State#ERROR}.
   *
   * <p>If the current state is before {@link State#READY} (ie we have not finished initializing the
   * channel), we simply set the state to {@link State#CLOSED} and let the client handle getting its
   * refund transaction confirmed.
   *
   * @return a future which completes when the provided multisig contract successfully broadcasts,
   *     or throws if the broadcast fails for some reason. Note that if the network simply rejects
   *     the transaction, this future will never complete, a timeout should be used.
   * @throws InsufficientMoneyException If the payment tx would have cost more in fees to spend than
   *     it is worth.
   */
  public synchronized ListenableFuture<Transaction> close() throws InsufficientMoneyException {
    if (storedServerChannel != null) {
      StoredServerChannel temp = storedServerChannel;
      storedServerChannel = null;
      StoredPaymentChannelServerStates channels =
          (StoredPaymentChannelServerStates)
              wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID);
      channels.closeChannel(
          temp); // May call this method again for us (if it wasn't the original caller)
      if (state.compareTo(State.CLOSING) >= 0) return closedFuture;
    }

    if (state.ordinal() < State.READY.ordinal()) {
      log.error("Attempt to settle channel in state " + state);
      state = State.CLOSED;
      closedFuture.set(null);
      return closedFuture;
    }
    if (state != State.READY) {
      // TODO: What is this codepath for?
      log.warn("Failed attempt to settle a channel in state " + state);
      return closedFuture;
    }
    Transaction tx = null;
    try {
      Wallet.SendRequest req = makeUnsignedChannelContract(bestValueToMe);
      tx = req.tx;
      // Provide a throwaway signature so that completeTx won't complain out about unsigned inputs
      // it doesn't
      // know how to sign. Note that this signature does actually have to be valid, so we can't use
      // a dummy
      // signature to save time, because otherwise completeTx will try to re-sign it to make it
      // valid and then
      // die. We could probably add features to the SendRequest API to make this a bit more
      // efficient.
      signMultisigInput(tx, Transaction.SigHash.NONE, true);
      // Let wallet handle adding additional inputs/fee as necessary.
      req.shuffleOutputs = false;
      req.missingSigsMode = Wallet.MissingSigsMode.USE_DUMMY_SIG;
      wallet.completeTx(req); // TODO: Fix things so shuffling is usable.
      feePaidForPayment = req.tx.getFee();
      log.info("Calculated fee is {}", feePaidForPayment);
      if (feePaidForPayment.compareTo(bestValueToMe) > 0) {
        final String msg =
            String.format(
                Locale.US,
                "Had to pay more in fees (%s) than the channel was worth (%s)",
                feePaidForPayment,
                bestValueToMe);
        throw new InsufficientMoneyException(feePaidForPayment.subtract(bestValueToMe), msg);
      }
      // Now really sign the multisig input.
      signMultisigInput(tx, Transaction.SigHash.ALL, false);
      // Some checks that shouldn't be necessary but it can't hurt to check.
      tx.verify(); // Sanity check syntax.
      for (TransactionInput input : tx.getInputs())
        input.verify(); // Run scripts and ensure it is valid.
    } catch (InsufficientMoneyException e) {
      throw e; // Don't fall through.
    } catch (Exception e) {
      log.error(
          "Could not verify self-built tx\nMULTISIG {}\nCLOSE {}",
          multisigContract,
          tx != null ? tx : "");
      throw new RuntimeException(e); // Should never happen.
    }
    state = State.CLOSING;
    log.info("Closing channel, broadcasting tx {}", tx);
    // The act of broadcasting the transaction will add it to the wallet.
    ListenableFuture<Transaction> future = broadcaster.broadcastTransaction(tx).future();
    Futures.addCallback(
        future,
        new FutureCallback<Transaction>() {
          @Override
          public void onSuccess(Transaction transaction) {
            log.info("TX {} propagated, channel successfully closed.", transaction.getHash());
            state = State.CLOSED;
            closedFuture.set(transaction);
          }

          @Override
          public void onFailure(Throwable throwable) {
            log.error("Failed to settle channel, could not broadcast", throwable);
            state = State.ERROR;
            closedFuture.setException(throwable);
          }
        });
    return closedFuture;
  }
 private static boolean isFinal(State state) {
   return (state.compareTo(State.Id) >= 0);
 }
 /**
  * Once the servers signature over the refund transaction has been received and provided using
  * {@link PaymentChannelClientState#provideRefundSignature(byte[])} then this method can be called
  * to receive the now valid and broadcastable refund transaction.
  */
 public synchronized Transaction getCompletedRefundTransaction() {
   checkState(state.compareTo(State.WAITING_FOR_SIGNED_REFUND) > 0);
   return refundTx;
 }
 /**
  * Returns the fees that will be paid if the refund transaction has to be claimed because the
  * server failed to settle the channel properly. May only be called after {@link
  * PaymentChannelClientState#initiate()}
  */
 public synchronized Coin getRefundTxFees() {
   checkState(state.compareTo(State.NEW) > 0);
   return refundFees;
 }
  public static void parseDuration(
      final Object durationString,
      final int start,
      int length,
      IAObject mutableObject,
      ADurationParseOption parseOption)
      throws HyracksDataException {

    int offset = 0;
    int value = 0, hour = 0, minute = 0, second = 0, millisecond = 0, year = 0, month = 0, day = 0;
    State state = State.NOTHING_READ;

    IStringAccessor charAccessor;

    if (durationString instanceof char[]) {
      charAccessor =
          new IStringAccessor() {
            @Override
            public char getCharAt(int index) {
              return ((char[]) durationString)[start + index];
            }
          };
    } else if (durationString instanceof byte[]) {
      charAccessor =
          new IStringAccessor() {

            @Override
            public char getCharAt(int index) {
              return (char) (((byte[]) durationString)[start + index]);
            }
          };
    } else if (durationString instanceof String) {
      charAccessor =
          new IStringAccessor() {

            @Override
            public char getCharAt(int index) {
              return ((String) durationString).charAt(start + index);
            }
          };
    } else {
      throw new HyracksDataException(durationErrorMessage);
    }

    short sign = 1;
    if (charAccessor.getCharAt(offset) == '-') {
      offset++;
      sign = -1;
    }

    if (charAccessor.getCharAt(offset) != 'P') {
      throw new HyracksDataException(durationErrorMessage + ": Missing leading 'P'.");
    }

    offset++;

    for (; offset < length; offset++) {
      if (charAccessor.getCharAt(offset) >= '0' && charAccessor.getCharAt(offset) <= '9') {
        // accumulate the digit fields
        value = value * DECIMAL_UNIT + charAccessor.getCharAt(offset) - '0';
      } else {
        switch (charAccessor.getCharAt(offset)) {
          case 'Y':
            if (state.compareTo(State.YEAR) < 0) {
              if (parseOption == ADurationParseOption.DAY_TIME) {
                throw new HyracksDataException(onlyDayTimeErrorMessage);
              }
              year = value;
              state = State.YEAR;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong YEAR feild.");
            }
            break;
          case 'M':
            if (state.compareTo(State.TIME) < 0) {
              if (state.compareTo(State.MONTH) < 0) {
                if (parseOption == ADurationParseOption.DAY_TIME) {
                  throw new HyracksDataException(onlyDayTimeErrorMessage);
                }
                month = value;
                state = State.MONTH;
              } else {
                throw new HyracksDataException(durationErrorMessage + ": wrong MONTH field.");
              }
            } else if (state.compareTo(State.MIN) < 0) {
              if (parseOption == ADurationParseOption.YEAR_MONTH) {
                throw new HyracksDataException(onlyYearMonthErrorMessage);
              }
              minute = value;
              state = State.MIN;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong MIN field.");
            }
            break;
          case 'D':
            if (state.compareTo(State.DAY) < 0) {
              if (parseOption == ADurationParseOption.YEAR_MONTH) {
                throw new HyracksDataException(onlyYearMonthErrorMessage);
              }
              day = value;
              state = State.DAY;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong DAY field");
            }
            break;
          case 'T':
            if (state.compareTo(State.TIME) < 0) {
              if (parseOption == ADurationParseOption.YEAR_MONTH) {
                throw new HyracksDataException(onlyYearMonthErrorMessage);
              }
              state = State.TIME;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong TIME field.");
            }
            break;

          case 'H':
            if (state.compareTo(State.HOUR) < 0) {
              if (parseOption == ADurationParseOption.YEAR_MONTH) {
                throw new HyracksDataException(onlyYearMonthErrorMessage);
              }
              hour = value;
              state = State.HOUR;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong HOUR field.");
            }
            break;
          case '.':
            if (state.compareTo(State.MILLISEC) < 0) {
              if (parseOption == ADurationParseOption.YEAR_MONTH) {
                throw new HyracksDataException(onlyYearMonthErrorMessage);
              }
              int i = 1;
              for (; offset + i < length; i++) {
                if (charAccessor.getCharAt(offset + i) >= '0'
                    && charAccessor.getCharAt(offset + i) <= '9') {
                  if (i < 4) {
                    millisecond =
                        millisecond * DECIMAL_UNIT + (charAccessor.getCharAt(offset + i) - '0');
                  } else {
                    throw new HyracksDataException(
                        durationErrorMessage + ": wrong MILLISECOND field.");
                  }
                } else {
                  break;
                }
              }
              offset += i;
              state = State.MILLISEC;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong MILLISECOND field.");
            }
          case 'S':
            if (state.compareTo(State.SEC) < 0) {
              if (parseOption == ADurationParseOption.YEAR_MONTH) {
                throw new HyracksDataException(onlyYearMonthErrorMessage);
              }
              second = value;
              state = State.SEC;
            } else {
              throw new HyracksDataException(durationErrorMessage + ": wrong SECOND field.");
            }
            break;
          default:
            throw new HyracksDataException(durationErrorMessage + ": wrong format for duration.");
        }
        value = 0;
      }
    }

    if (state.compareTo(State.TIME) == 0) {
      throw new HyracksDataException(
          durationErrorMessage + ": no time fields after time separator.");
    }

    int totalMonths = sign * (year * 12 + month);
    long totalMilliseconds =
        sign
            * (day * GregorianCalendarSystem.CHRONON_OF_DAY
                + hour * GregorianCalendarSystem.CHRONON_OF_HOUR
                + minute * GregorianCalendarSystem.CHRONON_OF_MINUTE
                + second * GregorianCalendarSystem.CHRONON_OF_SECOND
                + millisecond);

    if (sign > 0) {
      if (totalMonths < 0) {
        throw new HyracksDataException(
            durationErrorMessage
                + ": total number of months is beyond its max value (-2147483647 to 2147483647).");
      }
      if (totalMilliseconds < 0) {
        throw new HyracksDataException(
            durationErrorMessage
                + ": total number of milliseconds is beyond its max value (-9223372036854775808 to 9223372036854775807).");
      }
    }

    if (mutableObject instanceof AMutableDuration) {
      ((AMutableDuration) mutableObject).setValue(totalMonths, totalMilliseconds);
    } else if (mutableObject instanceof AMutableYearMonthDuration) {
      ((AMutableYearMonthDuration) mutableObject).setMonths(totalMonths);
    } else if (mutableObject instanceof AMutableDayTimeDuration) {
      ((AMutableDayTimeDuration) mutableObject).setMilliseconds(totalMilliseconds);
    }
  }