/*
   * The grammar of this function is: RawLine = !TCLine || TCLine TCLine =
   * ItemRangeAmount || ItemRangeAmount:ItemRangeAmount ItemRangeAmount =
   * ItemRange@Amount || ItemRange ItemRange = Item-Item || Item Item =
   * ID;Data || ID ID = int Data = int Amount = int
   */
  public static IntMap parseLineSplit(String line, Direction d, IntMap map) {
    boolean isNegate = false;
    if (line.length() > 0) {
      if (line.charAt(0) == ItemCharacter.NEGATE.getCharacter()) {
        isNegate = true;
        line = line.substring(1);
      }
    }

    String[] TCexpSplit = line.split("" + ItemCharacter.DELIMITER.getCharacter());

    for (String exp : TCexpSplit) {
      int amount = Integer.MAX_VALUE;
      boolean range = false;
      int[] itemFrom = null;
      int[] itemTo = null;

      String[] itemAmount = exp.split("" + ItemCharacter.AMOUNT.getCharacter());
      if (itemAmount.length == 2) {
        try {
          amount = Integer.parseInt(itemAmount[1]);
        } catch (NumberFormatException ex) {
          // We forgive the user, simply ignoring amount here
        }
      } else if (itemAmount.length != 1) {
        continue;
      }

      String[] itemRange = itemAmount[0].split("" + ItemCharacter.RANGE.getCharacter());
      if (itemRange.length == 1) {
        itemFrom = checkIDData(itemRange[0]);
      } else if (itemRange.length == 2) {
        itemFrom = checkIDData(itemRange[0]);
        itemTo = checkIDData(itemRange[1]);
        range = true;
      } else {
        // User has made a range of ranges, thats not possible :(
        continue;
      }

      if (range && itemFrom != null && itemTo != null) {
        // Something with ranges
        if (isNegate) {
          amount = 0;
        }
        map.setRange(itemFrom[0], itemFrom[1], itemTo[0], itemTo[1], amount);

      } else if (itemFrom != null) {
        // Something without ranges
        if (isNegate) {
          amount = 0;
        }
        map.setInt(itemFrom[0], itemFrom[1], amount);
      }
    }
    return map;
  }
  // TODO: optimize, don't use .toLowerCase() too often...
  public static IntMap parseLine(String line, Direction d, IntMap map) {
    // The String 'line' shouldn't contain any directions anymore.
    line += ":";
    /* if negate char is @ start of the line we need to negate the hole line, if not negate will only do a single item */
    boolean isNegate = false;
    boolean isNegateAtStart = line.charAt(0) == ItemCharacter.NEGATE.getCharacter();
    if (isNegateAtStart) {
      line = line.substring(1);
      isNegate = true;
    }
    String[] numbers = new String[] {"", "", "", "", ""};
    int[] ints = new int[DataType.size];
    ItemCharacter lastChar = null;
    boolean range = false;
    for (char character : line.toCharArray()) {
      ItemCharacter newChar = ItemCharacter.getItemParseCharacter(character);
      if (newChar == null) {
        return null;
      }
      switch (newChar) {
        case NEGATE:
          isNegate = !isNegate;
          break;
        case DIGIT:
          if (!range && lastChar == null) {
            numbers[DataType.START_ID.ordinal()] += character;
          } else if (!range && lastChar == ItemCharacter.DATA_VALUE) {
            numbers[DataType.START_DATA.ordinal()] += character;
          } else if (range && lastChar == null) {
            numbers[DataType.END_ID.ordinal()] += character;
          } else if (range && lastChar == ItemCharacter.DATA_VALUE) {
            numbers[DataType.END_DATA.ordinal()] += character;
          } else if (lastChar == ItemCharacter.AMOUNT) {
            numbers[DataType.AMOUNT.ordinal()] += character;
          }
          break;
        case RANGE:
          if (range) {
            return null;
          } else {
            range = true;
            lastChar = null;
          }
          break;
        case DATA_VALUE:
        case AMOUNT:
          lastChar = newChar;
          break;
        case DELIMITER:
          try {
            for (int index = 0; index < numbers.length; index++) {
              if (numbers[index].length() == 0) {
                numbers[index] = "-1";
              }
            }
            ints[DataType.START_ID.ordinal()] =
                Integer.parseInt(numbers[DataType.START_ID.ordinal()]);
            ints[DataType.START_DATA.ordinal()] =
                Integer.parseInt(numbers[DataType.START_DATA.ordinal()]);
            if (range) {
              ints[DataType.END_ID.ordinal()] =
                  Integer.parseInt(numbers[DataType.END_ID.ordinal()]);
              ints[DataType.END_DATA.ordinal()] =
                  Integer.parseInt(numbers[DataType.END_DATA.ordinal()]);
            }
            ints[DataType.AMOUNT.ordinal()] = Integer.parseInt(numbers[DataType.AMOUNT.ordinal()]);
          } catch (NumberFormatException e) {
            if (CartStorage.DEBUG) {
              TweakCart.log("NFE thrown: " + e.getMessage(), Level.WARNING);
            }
          }

          if (isNegate) {
            ints[DataType.AMOUNT.ordinal()] = 0;
          } else if (ints[DataType.AMOUNT.ordinal()] < 1) {
            ints[DataType.AMOUNT.ordinal()] = Integer.MAX_VALUE;
          }

          if (range) {
            /*
             * Faulty sign fix
             */
            if (ints[DataType.START_ID.ordinal()] == -1 || ints[DataType.END_ID.ordinal()] == -1) {
              continue;
            }
            boolean succeed =
                map.setRange(
                    ints[DataType.START_ID.ordinal()],
                    ints[DataType.START_DATA.ordinal()],
                    ints[DataType.END_ID.ordinal()],
                    ints[DataType.END_DATA.ordinal()],
                    ints[DataType.AMOUNT.ordinal()]);
          } else {
            /*
             * Faulty sign fix
             */
            if (ints[DataType.START_ID.ordinal()] == -1) {
              continue;
            }
            map.setInt(
                ints[DataType.START_ID.ordinal()],
                ints[DataType.START_DATA.ordinal()],
                ints[DataType.AMOUNT.ordinal()]);
          }
          /* Only reset negate if negate wasnt detected @ start*/
          if (!isNegateAtStart) {
            isNegate = false;
          }

          ints = new int[DataType.size];
          numbers = new String[] {"", "", "", "", ""};
          lastChar = null;
          range = false;
          break;
        default:
          // Syntax error
          return null;
      }
    }
    return map;
  }