/** initialization */
  public void initializeDataStructures(Domain domain) {
    try {
      ArrayList<Issue> issues = domain.getIssues();
      for (Issue lIssue : issues) {
        switch (lIssue.getType()) {
          case DISCRETE:
            IssueDiscrete lIssueDiscrete = (IssueDiscrete) lIssue;
            HashMap<Value, Integer> discreteIssueValuesMap = new HashMap<Value, Integer>();
            for (int j = 0; j < lIssueDiscrete.getNumberOfValues(); j++) {
              Value v = lIssueDiscrete.getValue(j);
              discreteIssueValuesMap.put(v, 0);
            }
            opponentBidsStatisticsDiscrete.add(discreteIssueValuesMap);
            break;

          case REAL:
            IssueReal lIssueReal = (IssueReal) lIssue;
            ArrayList<Integer> numProposalsPerValue = new ArrayList<Integer>();
            int lNumOfPossibleValuesInThisIssue = lIssueReal.getNumberOfDiscretizationSteps();
            for (int i = 0; i < lNumOfPossibleValuesInThisIssue; i++) {
              numProposalsPerValue.add(0);
            }
            opponentBidsStatisticsForReal.add(numProposalsPerValue);
            break;

          case INTEGER:
            IssueInteger lIssueInteger = (IssueInteger) lIssue;
            ArrayList<Integer> numOfValueProposals = new ArrayList<Integer>();

            // number of possible value when issue is integer (we should add 1 in order to include
            // all values)
            int lNumOfPossibleValuesForThisIssue =
                lIssueInteger.getUpperBound() - lIssueInteger.getLowerBound() + 1;
            for (int i = 0; i < lNumOfPossibleValuesForThisIssue; i++) {
              numOfValueProposals.add(0);
            }
            opponentBidsStatisticsForInteger.add(numOfValueProposals);
            break;
        }
      }
    } catch (Exception e) {
      System.out.println("EXCEPTION in initializeDataAtructures");
    }
  }
  public Bid ChooseBid(List<Bid> candidateBids, Domain domain) {

    int upperSearchLimit = 200; // 100;

    int maxIndex = -1;
    Random ran = new Random();
    ArrayList<Issue> issues = domain.getIssues();
    int maxFrequency = 0;
    int realIndex = 0;
    int discreteIndex = 0;
    int integerIndex = 0;

    if (candidateBids.size() >= upperSearchLimit) {
      List<Bid> bids = new ArrayList<Bid>();
      for (int i = 0; i < upperSearchLimit; i++) {
        int issueIndex = ran.nextInt(candidateBids.size());
        bids.add(candidateBids.get(issueIndex));
      }
      candidateBids = bids;
    }

    // this whole block of code is to find the best bid
    try {

      for (int i = 0; i < candidateBids.size(); i++) {
        int maxValue = 0;
        realIndex = discreteIndex = integerIndex = 0;
        for (int j = 0; j < issues.size(); j++) {
          Value v = candidateBids.get(i).getValue(issues.get(j).getNumber());
          switch (issues.get(j).getType()) {
            case DISCRETE:
              if (opponentBidsStatisticsDiscrete.get(discreteIndex) != null) {
                int counterPerValue = opponentBidsStatisticsDiscrete.get(discreteIndex).get(v);
                maxValue += counterPerValue;
              }
              discreteIndex++;
              break;
            case REAL:
              IssueReal lIssueReal = (IssueReal) issues.get(j);
              int lNumOfPossibleRealValues = lIssueReal.getNumberOfDiscretizationSteps();
              double lOneStep =
                  (lIssueReal.getUpperBound() - lIssueReal.getLowerBound())
                      / lNumOfPossibleRealValues;
              double first = lIssueReal.getLowerBound();
              double last = lIssueReal.getLowerBound() + lOneStep;
              double valueReal = ((ValueReal) v).getValue();
              boolean found = false;
              for (int k = 0;
                  !found && k < opponentBidsStatisticsForReal.get(realIndex).size();
                  k++) {
                if (valueReal >= first && valueReal <= last) {
                  int counterPerValue = opponentBidsStatisticsForReal.get(realIndex).get(k);
                  maxValue += counterPerValue;
                  found = true;
                }
                first = last;
                last = last + lOneStep;
              }
              if (found == false) {
                int k = opponentBidsStatisticsForReal.get(realIndex).size() - 1;
                int counterPerValue = opponentBidsStatisticsForReal.get(realIndex).get(k);
                maxValue += counterPerValue;
              }
              realIndex++;
              break;

            case INTEGER:
              IssueInteger lIssueInteger = (IssueInteger) issues.get(j);
              int valueInteger = ((ValueInteger) v).getValue();
              int valueIndex =
                  valueInteger
                      - lIssueInteger
                          .getLowerBound(); // For ex. LowerBound index is 0, and the lower bound is
              // 2, the value is 4, so the index of 4 would be 2 which
              // is exactly 4-2
              int counterPerValue =
                  opponentBidsStatisticsForInteger.get(integerIndex).get(valueIndex);
              maxValue += counterPerValue;
              integerIndex++;
              break;
          }
        }
        if (maxValue > maxFrequency) { // choose the bid with the maximum maxValue
          maxFrequency = maxValue;
          maxIndex = i;
        } else if (maxValue == maxFrequency) { // random exploration
          if (ran.nextDouble() < 0.5) {
            maxFrequency = maxValue;
            maxIndex = i;
          }
        }
      }

    } catch (Exception e) {
      System.out.println("Exception in choosing a bid");
      System.out.println(e.getMessage() + "---" + discreteIndex);
    }
    if (maxIndex == -1) {
      return candidateBids.get(ran.nextInt(candidateBids.size()));
    } else {
      // here we adopt the random exploration mechanism
      if (ran.nextDouble() < 0.95) {
        return candidateBids.get(maxIndex);
      } else {
        return candidateBids.get(ran.nextInt(candidateBids.size()));
      }
    }
  }
  /** This function updates the statistics of the bids that were received from the opponent. */
  private void updateStatistics(Bid bidToUpdate, boolean toRemove, Domain domain) {
    try {
      ArrayList<Issue> issues = domain.getIssues();

      // counters for each type of the issues
      int realIndex = 0;
      int discreteIndex = 0;
      int integerIndex = 0;
      for (Issue lIssue : issues) {
        int issueNum = lIssue.getNumber();
        Value v = bidToUpdate.getValue(issueNum);
        switch (lIssue.getType()) {
          case DISCRETE:
            if (opponentBidsStatisticsDiscrete == null) {
              System.out.println("opponentBidsStatisticsDiscrete is NULL");
            } else if (opponentBidsStatisticsDiscrete.get(discreteIndex) != null) {
              int counterPerValue = opponentBidsStatisticsDiscrete.get(discreteIndex).get(v);
              if (toRemove) {
                counterPerValue--;
              } else {
                counterPerValue++;
              }
              opponentBidsStatisticsDiscrete.get(discreteIndex).put(v, counterPerValue);
            }
            discreteIndex++;
            break;

          case REAL:
            IssueReal lIssueReal = (IssueReal) lIssue;
            int lNumOfPossibleRealValues = lIssueReal.getNumberOfDiscretizationSteps();
            double lOneStep =
                (lIssueReal.getUpperBound() - lIssueReal.getLowerBound())
                    / lNumOfPossibleRealValues;
            double first = lIssueReal.getLowerBound();
            double last = lIssueReal.getLowerBound() + lOneStep;
            double valueReal = ((ValueReal) v).getValue();
            boolean found = false;

            for (int i = 0;
                !found && i < opponentBidsStatisticsForReal.get(realIndex).size();
                i++) {
              if (valueReal >= first && valueReal <= last) {
                int countPerValue = opponentBidsStatisticsForReal.get(realIndex).get(i);
                if (toRemove) {
                  countPerValue--;
                } else {
                  countPerValue++;
                }

                opponentBidsStatisticsForReal.get(realIndex).set(i, countPerValue);
                found = true;
              }
              first = last;
              last = last + lOneStep;
            }
            // If no matching value was found, update the last cell
            if (found == false) {
              int i = opponentBidsStatisticsForReal.get(realIndex).size() - 1;
              int countPerValue = opponentBidsStatisticsForReal.get(realIndex).get(i);
              if (toRemove) {
                countPerValue--;
              } else {
                countPerValue++;
              }

              opponentBidsStatisticsForReal.get(realIndex).set(i, countPerValue);
            }
            realIndex++;
            break;

          case INTEGER:
            IssueInteger lIssueInteger = (IssueInteger) lIssue;
            int valueInteger = ((ValueInteger) v).getValue();

            int valueIndex =
                valueInteger
                    - lIssueInteger
                        .getLowerBound(); // For ex. LowerBound index is 0, and the lower bound is
            // 2, the value is 4, so the index of 4 would be 2 which
            // is exactly 4-2
            int countPerValue = opponentBidsStatisticsForInteger.get(integerIndex).get(valueIndex);
            if (toRemove) {
              countPerValue--;
            } else {
              countPerValue++;
            }
            opponentBidsStatisticsForInteger.get(integerIndex).set(valueIndex, countPerValue);
            integerIndex++;
            break;
        }
      }
    } catch (Exception e) {
      System.out.println("Exception in updateStatistics: " + e.getMessage());
    }
  }