/**
   * Add more trips from JTW data into the current trips list. There are 2 kinds of added trips: 1 -
   * "go_to_work" trips: which have Origin locations are outside study area and Destination
   * locations are inside study area
   *
   * <p>2 - "go_home" trips: which have Origin locations are inside study area and Destination
   * locations are outside study area
   *
   * @author vlcao
   */
  public static List<String[]> addJTWtripsFromOutsideStudyArea() {
    ModelMain main = ModelMain.getInstance();
    List<String[]> tripListFromOutsideStudyArea = new ArrayList<>();

    try {
      int modeIdxInArray = 1;
      int destinationIdxInArry = 0;

      // store the trip list, vehicle list and process link
      // before add more trips from JTW data

      List<Vehicle> vehicleList = main.getVehicleList();

      // get the last household ID in the pool
      int lastHholdID = 0;
      for (Household household : main.getHouseholdPool().getHouseholds().values()) {
        if (household.getId() > lastHholdID) {
          lastHholdID = household.getId();
        }
      }

      List<JTW2006Entity> jtw2006Entities = ModelMain.getInstance().getJtw2006DAO().findAll();

      // create trips with the data from database
      for (JTW2006Entity jtw2006Entity : jtw2006Entities) {

        int[] desMode = new int[2];
        desMode[destinationIdxInArry] = jtw2006Entity.getD_Tz06();

        switch (jtw2006Entity.getMode9()) {
          case 1:
            desMode[modeIdxInArray] = TravelModes.Taxi.getIntValue();
            break;

          case 2:
            desMode[modeIdxInArray] = TravelModes.LightRail.getIntValue();
            break;

          case 3:
            desMode[modeIdxInArray] = TravelModes.Bus.getIntValue();
            break;

          case 4:
            desMode[modeIdxInArray] = TravelModes.CarDriver.getIntValue();
            break;

          case 5:
            desMode[modeIdxInArray] = TravelModes.CarPassenger.getIntValue();
            break;

          case 6:
            desMode[modeIdxInArray] = TravelModes.Bike.getIntValue();
            break;

          case 7:
            desMode[modeIdxInArray] = TravelModes.Walk.getIntValue();
            break;

          default:
            break;
        }

        // add more trips from JTW data
        /*
         * randomly choose the start time:
         * 1- in the morning from 6AM (21600)
         * to 9.AM (32400) for "go_to_work" trip (purpose = 1)
         *
         * 2- in the afternoon from 3PM (54000) to 7PM
         * for "go_home" trip (purpose = 0)
         */
        int inSecond_6AM = 21600;
        int inSecond_3PM = 54000;
        int inSecond_2hours = 2 * 3600;
        int inSecond_3hours = 3 * 3600;
        int inSecond_4hours = 4 * 3600;
        int inSecond_halfHour = 1800;

        int startTime = inSecond_6AM + HardcodedData.random.nextInt(inSecond_3hours);
        int endTime = startTime + inSecond_halfHour + HardcodedData.random.nextInt(inSecond_2hours);

        int startTimeHome = inSecond_3PM + HardcodedData.random.nextInt(inSecond_4hours);
        int endTimeHome =
            startTimeHome + inSecond_halfHour + HardcodedData.random.nextInt(inSecond_2hours);

        int newHholdID = lastHholdID + jtw2006Entity.getID();
        int vehicleID = 0, origin, destination;

        int[] desArr = null;

        // set up origin and destination for new trips
        int[] orgArr = ActivityLocationManager.getInstance().getActivityLocationsForZone(-1);
        origin = orgArr[HardcodedData.random.nextInt(orgArr.length)];

        // when there are no matching activity locations in the destination
        // travel zone, randomly pick up one activity location in the study area
        if (ActivityLocationManager.getInstance()
                    .getActivityLocationsForZone(desMode[destinationIdxInArry])
                == null
            || ActivityLocationManager.getInstance()
                    .getActivityLocationsForZone(desMode[destinationIdxInArry])[0]
                == -1) {

          int randomLocation =
              HardcodedData.random.nextInt(
                  ActivityLocationManager.getInstance().getActivityLocation().length);

          destination =
              ActivityLocationManager.getInstance().getActivityLocation()[randomLocation][1];
        } else {
          desArr =
              ActivityLocationManager.getInstance()
                  .getActivityLocationsForZone(desMode[destinationIdxInArry]);
          destination = desArr[HardcodedData.random.nextInt(desArr.length)];
        }

        // set up the vehicle ID for new trips
        switch (TravelModes.classify(desMode[modeIdxInArray])) {
          case CarDriver:
            vehicleID = 1;

            // add a car at origin location
            Vehicle newCar = new Vehicle();
            newCar.setVehicleId(vehicleID);
            newCar.sethHold(newHholdID);
            newCar.setLocation(main.getProcessLink().get(origin));
            newCar.setType(1);
            newCar.setSubType(1);

            vehicleList.add(newCar);
            break;

          case Taxi:
            vehicleID = 1;

            // add a taxi at origin location
            Vehicle newTaxi = new Vehicle();
            newTaxi.setVehicleId(vehicleID);
            newTaxi.sethHold(newHholdID);
            newTaxi.setLocation(main.getProcessLink().get(origin));
            newTaxi.setType(1);
            newTaxi.setSubType(2);

            vehicleList.add(newTaxi);
            break;
          default:
            break;
        }

        String personID = "1";
        String goToWorkTripID = "1";
        String goToWorkTripPurposeCode = "1";
        String constraintCode = "0";

        /* 1 -  "go_to_work" trip */
        String[] newTrip = {
          String.valueOf(newHholdID),
          personID,
          goToWorkTripID,
          goToWorkTripPurposeCode,
          String.valueOf(desMode[modeIdxInArray]),
          String.valueOf(vehicleID),
          String.valueOf(startTime),
          String.valueOf(origin),
          String.valueOf(endTime),
          String.valueOf(destination),
          constraintCode
        };

        // initial the Hash Maps
        String hhPersonTripString = newHholdID + "_" + personID + "_" + goToWorkTripID;
        main.getHmHhPersonTripTravelMode().put(hhPersonTripString, desMode[modeIdxInArray]);

        // add new trip into the trip list
        tripListFromOutsideStudyArea.add(newTrip);

        /* 2 - "go_home" trip */
        String goHomeTripID = "2";
        String goHomeTripPurposeCode = "0";

        String[] newTripHome = {
          String.valueOf(newHholdID),
          personID,
          goHomeTripID,
          goHomeTripPurposeCode,
          String.valueOf(desMode[modeIdxInArray]),
          String.valueOf(vehicleID),
          String.valueOf(startTimeHome),
          /* originHome is the destination of "go_to_work" trip */
          String.valueOf(destination),
          String.valueOf(endTimeHome),
          /* destinationHome is the origin of "go_to_work" trip */
          String.valueOf(origin),
          constraintCode
        };

        // initial the Hash Maps
        hhPersonTripString = newHholdID + "_" + personID + "_" + goHomeTripID;
        main.getHmHhPersonTripTravelMode().put(hhPersonTripString, desMode[modeIdxInArray]);

        // add new trip into the trip list
        tripListFromOutsideStudyArea.add(newTripHome);
      }

      // Assign back vehicle list
      main.setVehicleList(vehicleList);

    } catch (Exception e) {
      logger.error("Exception caught", e);
    }

    return tripListFromOutsideStudyArea;
  }
  /**
   * corrects TRIPS file (TRANSIMS input). This correction has 2 parts:
   *
   * <p>1 - Correction in Origin of next trip and current trip in order to make sure they are the
   * same. In this correction, the location of taxi also is updated in the Process_Link_2 file for
   * TRANSIMS
   *
   * <p>2 - Correction for trips with mode DRIVE (car driver): - IF at least one of the trips of an
   * individual has DRIVE mode and the first trip mode is not DRIVE, change mode of all trips among
   * these two trips to DRIVE mode. This correction also ensures that the vehicle IDs of these new
   * DRIVE trips are the same.
   *
   * <p>- IF this person doesn't drive at all, don't do anything.
   *
   * @param tripsList 2D array containing all trips done by the population, grouped by individuals,
   *     which are grouped by households. Columns in tripsList are in the order of columns in
   *     TRANSIMS' TRIPS file, which are below 1 household 2 person 3 trip 4 purpose 5 mode 6
   *     vehicle 7 start time 8 origin 9 end time 10 destination 11 constraint
   */
  public void correctTransimsFilesDriveOnly(int[][] tripsList) {

    // 1- correct to make sure that the origin of next trip
    // must be the same with the destination of current trip

    // make HashMap of tripsList index with key [hhold, person, trip]
    HashMap<String, Integer> hmTrips = new HashMap<>();
    String keyStr = "";

    for (int i = 0; i < tripsList.length; i++) {
      // only correct from the second trip of each individual's travel diary
      if (tripsList[i][tripIdxInTRIPS] != 1) {
        tripsList[i][originIdxInTRIPS] = tripsList[i - 1][destIdxInTRIPS];
      }

      // put into Syd.VEHICLE the origin location of the taxi
      if (tripsList[i][4] == modeHOV4) {
        int parkingLocation =
            ModelMain.getInstance().getProcessLink().get(tripsList[i][originIdxInTRIPS] - 1);
        for (Vehicle vehicle : ModelMain.getInstance().getVehicleList()) {
          if ((vehicle.getVehicleId() == tripsList[i][vehicleIdxInTRIPS])
              && (vehicle.gethHold() == tripsList[i][hholdIdxInTRIPS])) {

            vehicle.setLocation(parkingLocation);
            vehicle.setSubType(2);
            break;
          }
        }
      }
      keyStr =
          String.valueOf(tripsList[i][hholdIdxInTRIPS])
              + "_"
              + String.valueOf(tripsList[i][personIdxInTRIPS])
              + "_"
              + String.valueOf(tripsList[i][tripIdxInTRIPS]);
      hmTrips.put(keyStr, i);
    }

    // get trips belong to each household
    List<int[][]> hholdTrips = getHholdTrips(tripsList);

    // 2 - correct Drive mode trips for each house hold
    for (int ihh = 0; ihh <= hholdTrips.size() - 1; ihh++) {
      // get trips belong to each person process
      int[][] thisHholdTrips = hholdTrips.get(ihh);
      List<int[][]> personsTrips = getPersonTrips(thisHholdTrips);

      // check and correct DRIVE trips of each person in this household
      for (int ipp = 0; ipp <= personsTrips.size() - 1; ipp++) { // for each person
        // get trips belong to each person
        int[][] thisPersonTrips = personsTrips.get(ipp);

        // check trips of this person has DRIVE
        int vehicleID = 0;
        int idxTripHasDriveMode = 0;
        for (int iTrips = 0; iTrips < thisPersonTrips.length; iTrips++) {
          if (thisPersonTrips[iTrips][modeIdxInTRIPS] == modeDRIVE) {

            // save the index and vehicle ID of the trip that has DRIVE mode
            vehicleID = thisPersonTrips[iTrips][vehicleIdxInTRIPS];
            idxTripHasDriveMode = iTrips;
          }
        }

        // correct if trips of this person has DRIVE mode and
        // the first trip does NOT have DRIVE mode
        for (int i = idxTripHasDriveMode - 1; i >= 0; i--) {
          // get index of current trip in trip list
          keyStr =
              String.valueOf(thisPersonTrips[i][hholdIdxInTRIPS])
                  + "_"
                  + String.valueOf(thisPersonTrips[i][personIdxInTRIPS])
                  + "_"
                  + String.valueOf(thisPersonTrips[i][tripIdxInTRIPS]);
          TextFileHandler.writeToText(
              String.valueOf(ModelMain.getInstance().getCurrentYear()) + "_toDriver.csv",
              keyStr,
              true);
          int idxCurrentTripInTripList = hmTrips.get(keyStr);

          // correct and update current trip
          tripsList[idxCurrentTripInTripList][modeIdxInTRIPS] = modeDRIVE;
          tripsList[idxCurrentTripInTripList][vehicleIdxInTRIPS] = vehicleID;
        }
      }
    }
  }