/** @param config */
  public GrSimNetworkCfg(SubnodeConfiguration config) {
    ip = config.getString("ip", "127.0.0.1");
    port = config.getInt("port", 20011);
    teamYellow = config.getBoolean("teamYellow", false);

    key = config.getInt("[@id]");
  }
  public void run() {
    EventLog.log("Starting nanny for \"" + getServerDesc() + "\"");
    setStartedAndRunning(true);
    NDC.push(getServerDesc());
    try {
      // Nanny villages
      while (isKeepRunning()) {
        util.getConsole().checkPause();
        TimeWhenRunnable firstTimeWhenRunnable = null;
        sharpTimeWhenRunnable = null;
        if ((messageMode != null) && !messageMode.equals("false")) {
          // check messages before for commands including resume
          int msgRead = messageReader.readMessages(util, loginUrl, messageMode);
          log.info("Messages Done next " + msgRead);
        }
        // check if still enabled
        if (!suspended) {
          // Loop all enabled villages
          EventLog.log("Checking Villages");
          firstTimeWhenRunnable = processVillages();
          log.info("Villages done");
          // gac - move servers below villages
          // Loop all enabled server strategies
          TimeWhenRunnable serverStrategiesTimeWhenRunnable = processServerStrategies();
          if (serverStrategiesTimeWhenRunnable.before(firstTimeWhenRunnable)) {
            log.debug("ServerStrategy Earlier than villages");
            firstTimeWhenRunnable = serverStrategiesTimeWhenRunnable;
          }
          // gac add report processing - exists as server strategy to run periodically
          // but this will run at the end of every active period
          // count if outstanding after this read
          int moreRead = 0;
          if ((reportMode != null) && !reportMode.equals("false")) {
            // set the reporting mode and check reports
            // ReportMessageReader.getInstance().setReportsMode(reportMode);
            // EventLog.log(loginUrl+" about to read reportMode="+reportMode);
            moreRead = reportReader.readReports(util, loginUrl, reportMode);
            log.info("Server Reports done");
          }
          // read messages again
          if ((messageMode != null) && !messageMode.equals("false")) {
            // check messages
            int msgRead = messageReader.readMessages(util, loginUrl, messageMode);
            log.info("Messages at end done next " + msgRead);
            // read more if consuming all message not just scanning titles for commands
            // if (!messageMode.equalsIgnoreCase("titles")) {
            if (!messageMode.equalsIgnoreCase(messageReader.getScanMode())) {
              moreRead += msgRead;
            }
          }
          if (moreRead != 0) {
            // still more to read
            firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Re-run immediately
            log.debug("Villages will be run again because more reports to read (" + moreRead + ")");
          } else {
            // no new reports left
          }
          if (isConfigurationChanged()) {
            // Update configuration
            updateConfig(getNextConfig());
            EventLog.log("evt.configReloadDone", this.getClass());
            this.nextConfig = null; // No need to synchronize here
            firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Re-run immediately
            log.debug("Villages will be run again because of configuration update");
          }
        } else {
          // suspended
          log.debug("All Strategies Suspended");
          firstTimeWhenRunnable =
              new TimeWhenRunnable(
                  System.currentTimeMillis() + SUSPENDCHECK * Util.MILLI_MINUTE); // try again later
        }
        boolean sharp = false;
        long milliPause = 0;
        long milliSharp = -1L;
        if (firstTimeWhenRunnable != null) {
          log.debug("Earliest Strategy @ " + firstTimeWhenRunnable);
          sharp = firstTimeWhenRunnable.isSharp();
          milliPause = firstTimeWhenRunnable.getTime() - System.currentTimeMillis();
          if (milliPause < 0) {
            milliPause = 0;
          }
          int pauseLimit = config.getInt("/@pauseLimit", 86400) * 1000; //  Max 24h
          if (milliPause > pauseLimit) {
            log.info("Limiting pause from " + milliPause + " to " + pauseLimit);
            milliPause = pauseLimit;
          }
          if (sharpTimeWhenRunnable != null) {
            log.debug("Earliest sharp Strategy @ " + sharpTimeWhenRunnable);
            milliSharp = sharpTimeWhenRunnable.getTime() - System.currentTimeMillis();
            if (milliSharp < 0) {
              milliSharp = 0;
            }
          }
          log.debug(
              "Next available Action in "
                  + Util.milliToTimeString(milliPause)
                  + (sharp ? " sharp: " + Util.milliToTimeString(milliSharp) : ""));
        }

        if (isKeepRunning()) {
          util.userPause(milliPause, sharp, milliSharp);
          util.shortestPause(sharp); // Just to be safe
        }
      }
    } catch (Exception e) {
      EventLog.log(e.getMessage());
      log.error("", e);
      setEnabledAndStartStop(false);
    } finally {
      NDC.remove(); // not pop()
    }
  }
  public TimeWhenRunnable execute() throws ConversationException {
    // <strategy class="Farmizzator" desc="DESCRIPTION" enabled="true">
    //   <target x="XCOORD" y="YCOORD" village="TARGET_VILLAGE" movement="REINFORCE|RAID|ATTACK"
    // spy="RESOURCES|DEFENSES" rate="RATE" item="CATA_TARGET1, CATA_TARGET2"/>
    //   <troops type="TROOP_NAME" allowLess="ALLOWLESS" min="MINIMUM_TROOPS" randomise="RANDOMISE"
    // enabled="true">TROOP_AMOUNT</troops>
    //   <minPauseMinutes>MIN_PAUSE</minPauseMinutes>
    // </strategy>
    log.info("Executing strategy " + super.getDesc());
    NDC.push(super.getDesc());

    // TODO Per le volte successive:
    // TODO Controlla il report dell'attacco (difficile!)
    // TODO Se sono state perse troppe truppe, cancella questa strategy

    try {
      // EventLog.log("checking reports for 92,-95 d=396988&c=50 = " +
      // util.getMapIdFromPosition("92","-95"));
      // ReportMessageReader.getInstance().getAverageBounty(util.getMapIdFromPosition("92","-95"));
      // EventLog.log("Bounty
      // "+ReportMessageReader.getInstance().getLastBounty(util.getServerId(),util.getMapIdFromPosition("92","-95")));
      // EventLog.log("Average Bounty "+ReportMessageReader.getInstance().getAverageBounty(util,
      // "92","-95"));
      // EventLog.log("Last Bounty
      // "+ReportMessageReader.getInstance().getLastBounty(util.getServerId(),util.getMapIdFromPosition("92","-95")));
      // System.exit(0);

      RallyPoint rallyPoint = village.getRallyPoint();
      if (rallyPoint == null) {
        log.debug("No rally point");
        int minPauseMinutes = super.config.getInt("/minPauseMinutes", 25);
        minPauseMinutes =
            super.config.getInt(
                "/@minPauseMinutes", minPauseMinutes); // support on strategy line as well
        return new TimeWhenRunnable(
            System.currentTimeMillis() + minPauseMinutes * Util.MILLI_MINUTE); // Try again later
      }
      TroopTypeMap availablePerType = rallyPoint.fetchSendableTroops(super.util, false);
      TroopTypeMap toSend = new TroopTypeMap();
      boolean sendable = true;
      int totTroops = 0;

      List<SubnodeConfiguration> troopsNodes = super.config.configurationsAt("/troops");
      for (SubnodeConfiguration troopsNode : troopsNodes) {
        boolean enabled = troopsNode.getBoolean("/@enabled", true); // Enabled by default
        if (!enabled) {
          continue;
        }
        String type = troopsNode.getString("/@type", null);
        if (type == null) {
          log.error("Missing \"type\" attribute in strategy \"" + super.getDesc() + "\"");
          continue;
        }
        String fullkey = util.getTranslator().getKeyword(type); // romans.troop1
        String typeKey = fullkey.substring(fullkey.indexOf(".") + 1);
        TroopType troopType = TroopType.fromString(typeKey);
        int val = troopsNode.getInt("/", 0);
        boolean allowLess = troopsNode.getBoolean("/@allowLess", false);
        Integer available = availablePerType.get(troopType);
        // Check if we have enough troops
        if (val > available && !allowLess) {
          sendable = false;
          break;
        }
        // Check if we can send at least min troops
        String minimum = troopsNode.getString("/@min", "0");
        int min = 0;
        boolean percent = false;
        if (minimum.endsWith("%")) {
          percent = true;
          minimum = minimum.replace("%", "");
        }
        try {
          min = Integer.parseInt(minimum);
        } catch (NumberFormatException e) {
          throw new FatalException(
              String.format("Invalid numeric value for %s: \"%s\"", type, minimum));
        }
        if (percent) {
          min = (val * min) / 100;
        }
        if (available < min) {
          sendable = false;
          break;
        }
        // Randomise
        // Accept both "randomise" and "randomize", with "true" as default
        boolean randomise =
            troopsNode.getBoolean("/@randomise", troopsNode.getBoolean("/@randomize", true));
        if (randomise) {
          int maxDelta = (int) (val * RANDOMISE_RATIO);
          if (!allowLess) {
            // value can't be less than what specified
            val = val + random.nextInt(maxDelta + 1);
          } else {
            // value can be +- randVal
            val = val - maxDelta + random.nextInt(2 * maxDelta + 1);
          }
        }
        // Add troops to send
        val = val > available ? available : val; // Upper limit
        val = val < min ? min : val; // Lower limit
        toSend.put(troopType, val);
        totTroops += val;
      }
      int minPauseMinutes = super.config.getInt("/minPauseMinutes", 5);
      minPauseMinutes =
          super.config.getInt(
              "/@minPauseMinutes", minPauseMinutes); // support on strategy line as well
      if (sendable == false || totTroops == 0) {
        EventLog.log("Not enough troops");
        return new TimeWhenRunnable(
            System.currentTimeMillis() + minPauseMinutes * Util.MILLI_MINUTE); // Try again later
      }
      Coordinates coord = new Coordinates(config, "target"); // Handles "travian style" coords
      String x = coord.getX();
      String y = coord.getY();
      String village = super.config.getString("/target/@village", "");
      String movement = super.config.getString("/target/@movement", "attack");
      String fullkey = "movement." + translator.getKeyword(movement, "movement"); // movement.attack
      TroopTransferType transferType = TroopTransferType.fromKey(fullkey);
      String itemString1 = super.config.getString("/target/@item[1]", null);
      String itemString2 = super.config.getString("/target/@item[2]", null);
      TroopTransferType spyType = null;
      String spyTypeString = super.config.getString("/target/@spy", null);
      if (spyTypeString != null) {
        fullkey =
            "movement."
                + translator.getKeyword(spyTypeString, "movement"); // movement.spy.resources
        spyType = TroopTransferType.fromKey(fullkey);
      } // check for previous bounty if not spying
      else if ((!x.equals("")) && (!y.equals(""))) {
        // log.debug("checking reports for " + x + "," + y + " id " +
        // util.getMapIdFromPosition(x,y));
        // ReportMessageReader.getInstance().getAverageBounty(util.getServerId(),util.getMapIdFromPosition(x,y));
        // log.debug("Average Bounty " + ReportMessageReader.getInstance().getAverageBounty(util, x,
        // y));
        Valley v = new Valley(util, x, y);
        log.debug("Average Bounty " + v.getAverageBounty());
        // EventLog.log("Last Bounty
        // "+ReportMessageReader.getInstance().getLastBounty(util.getServerId(),util.getMapIdFromPosition( x, y)));
      }

      super.village
          .gotoMainPage(); // Ensure you do it from the right village (someone might have clicked to
      // a different village meanwhile)
      int secondsToArrive;
      try {
        secondsToArrive =
            rallyPoint.sendTroops(
                util,
                x,
                y,
                village,
                toSend,
                transferType,
                new String[] {itemString1, itemString2},
                spyType);
        totErrors = 0;
      } catch (ConversationException e) {
        log.error(e);
        totErrors++;
        if (totErrors <= MAX_ERRORS) {
          EventLog.log(
              String.format(
                  "Strategy has error; retrying later (%s left)", MAX_ERRORS - totErrors));
          util.shortestPause(false); // Just to be safe
          return new TimeWhenRunnable(
              System.currentTimeMillis() + minPauseMinutes * Util.MILLI_MINUTE); // Try again later
        } else {
          log.error("Strategy has too many errors; disabling", e);
          EventLog.log("Strategy has too many errors; disabling");
          return TimeWhenRunnable.NEVER;
        }
      }
      if (secondsToArrive > 0) {
        log.info(String.format("Strategy %s done for now", getDesc()));
        double rate = Math.max(super.config.getDouble("/target/@rate", 1), Double.MIN_VALUE);
        double raidTime = (2 * secondsToArrive * Util.MILLI_SECOND);
        long waitMillis = (long) (raidTime / rate);
        long timeTemp = waitMillis / 60000;
        EventLog.log(
            String.format(
                "Strategy %s done for now: Next run in %s minutes at rate %s",
                getDesc(), Long.toString(timeTemp), Double.toString(rate)));
        long MyTimeToRun = System.currentTimeMillis() + waitMillis;
        return new TimeWhenRunnable(MyTimeToRun);
      } else {
        log.error("Error in Farmizzator");
        EventLog.log(String.format("Strategy %s disabled on error", getDesc()));
        return new TimeWhenRunnable(false);
      }
    } finally {
      NDC.pop();
    }
  }
 public int getLogProducerTimeout() {
   SubnodeConfiguration sObj = iniConfObj.getSection("general");
   int tmpTime = Math.round(sObj.getInt("logproducertimeout") / 1000);
   return tmpTime;
 }
 public int getControllerDiscoveryTimeout() {
   SubnodeConfiguration sObj = iniConfObj.getSection("general");
   int tmpTime = Math.round(sObj.getInt("controllerdiscoverytimeout"));
   return tmpTime;
 }
 public int getWatchDogTimer() {
   SubnodeConfiguration sObj = iniConfObj.getSection("general");
   return sObj.getInt("watchdogtimer");
 }