/// Misc things that were useful when porting the code from Python.
library utils;

import 'constants.dart';

typedef bool Predicate();

class Pair<F, S> {
  final F first;
  final S second;

  const Pair(this.first, this.second);

  int get hashCode => 37 * first.hashCode + second.hashCode;
  bool operator ==(other) => other.first == first && other.second == second;
}

int parseIntRadix(String str, [int radix = 10]) {
  int val = 0;
  for (int i = 0; i < str.length; i++) {
    var digit = str.codeUnitAt(i);
    if (digit >= LOWER_A) {
      digit += 10 - LOWER_A;
    } else if (digit >= UPPER_A) {
      digit += 10 - UPPER_A;
    } else {
      digit -= ZERO;
    }
    val = val * radix + digit;
  }
  return val;
}

bool any(List<bool> iterable) => iterable.any((f) => f);

bool startsWithAny(String str, List<String> prefixes) {
  for (var prefix in prefixes) {
    if (str.startsWith(prefix)) {
      return true;
    }
  }
  return false;
}

// Like the python [:] operator.
List<T> slice<T>(List<T> list, int start, [int end]) {
  if (end == null) end = list.length;
  if (end < 0) end += list.length;

  // Ensure the indexes are in bounds.
  if (end < start) end = start;
  if (end > list.length) end = list.length;
  return list.sublist(start, end);
}

bool allWhitespace(String str) {
  for (int i = 0; i < str.length; i++) {
    if (!isWhitespaceCC(str.codeUnitAt(i))) return false;
  }
  return true;
}

String padWithZeros(String str, int size) {
  if (str.length == size) return str;
  var result = new StringBuffer();
  size -= str.length;
  for (int i = 0; i < size; i++) result.write('0');
  result.write(str);
  return result.toString();
}

// TODO(jmesserly): this implementation is pretty wrong, but I need something
// quick until dartbug.com/1694 is fixed.
/// Format a string like Python's % string format operator. Right now this only
/// supports a [data] dictionary used with %s or %08x. Those were the only
/// things needed for [errorMessages].
String formatStr(String format, Map data) {
  if (data == null) return format;
  data.forEach((key, value) {
    var result = new StringBuffer();
    var search = '%($key)';
    int last = 0, match;
    while ((match = format.indexOf(search, last)) >= 0) {
      result.write(format.substring(last, match));
      match += search.length;

      int digits = match;
      while (isDigit(format[digits])) {
        digits++;
      }
      int numberSize;
      if (digits > match) {
        numberSize = int.parse(format.substring(match, digits));
        match = digits;
      }

      switch (format[match]) {
        case 's':
          result.write(value);
          break;
        case 'd':
          var number = value.toString();
          result.write(padWithZeros(number, numberSize));
          break;
        case 'x':
          var number = value.toRadixString(16);
          result.write(padWithZeros(number, numberSize));
          break;
        default:
          throw "not implemented: formatStr does not support format "
              "character ${format[match]}";
      }

      last = match + 1;
    }

    result.write(format.substring(last, format.length));
    format = result.toString();
  });

  return format;
}
