// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of dart.io;

class _Platform {
  external static int _numberOfProcessors();
  external static String _pathSeparator();
  external static String _operatingSystem();
  external static _localHostname();
  external static _executable();
  external static _resolvedExecutable();
  /**
   * Retrieve the entries of the process environment.
   *
   * The result is an [Iterable] of strings, where each string represents
   * an environment entry.
   *
   * Environment entries should be strings containing
   * a non-empty name and a value separated by a '=' character.
   * The name does not contain a '=' character,
   * so the name is everything up to the first '=' character.
   * Values are everything after the first '=' charcacter.
   * A value may contain further '=' characters, and it may be empty.
   *
   * Returns an [OSError] if retrieving the environment fails.
   */
  external static _environment();
  external static List<String> _executableArguments();
  external static String _packageRoot();
  external static String _version();

  static String executable = _executable();
  static String resolvedExecutable = _resolvedExecutable();
  static String packageRoot = _packageRoot();

  // Cache the OS environemnt. This can be an OSError instance if
  // retrieving the environment failed.
  static var _environmentCache;

  static int get numberOfProcessors => _numberOfProcessors();
  static String get pathSeparator => _pathSeparator();
  static String get operatingSystem => _operatingSystem();
  static Uri script;

  // This script singleton is written to by the embedder if applicable.
  static void set _nativeScript(String path) {
    if (path.startsWith('http:') ||
        path.startsWith('https:') ||
        path.startsWith('package:') ||
        path.startsWith('dart:') ||
        path.startsWith('data:') ||
        path.startsWith('file:')) {
      script = Uri.parse(path);
    } else {
      script = Uri.base.resolveUri(new Uri.file(path));
    }
  }

  static String get localHostname {
    var result = _localHostname();
    if (result is OSError) {
      throw result;
    } else {
      return result;
    }
  }

  static List<String> get executableArguments => _executableArguments();

  static Map<String, String> get environment {
    if (_environmentCache == null) {
      var env = _environment();
      if (env is !OSError) {
        var isWindows = operatingSystem == 'windows';
        var result = isWindows ? new _CaseInsensitiveStringMap() : new Map();
        for (var str in env) {
          // The Strings returned by [_environment()] are expected to be
          // valid environment entries, but exceptions have been seen
          // (e.g., an entry of just '=' has been seen on OS/X).
          // Invalid entries (lines without a '=' or with an empty name)
          // are discarded.
          var equalsIndex = str.indexOf('=');
          if (equalsIndex > 0) {
            result[str.substring(0, equalsIndex)] =
                str.substring(equalsIndex + 1);
          }
        }
        _environmentCache = new UnmodifiableMapView<String, String>(result);
      } else {
        _environmentCache = env;
      }
    }

    if (_environmentCache is OSError) {
      throw _environmentCache;
    } else {
      return _environmentCache;
    }
  }

  static String get version => _version();
}

// Environment variables are case-insensitive on Windows. In order
// to reflect that we use a case-insensitive string map on Windows.
class _CaseInsensitiveStringMap<V> implements Map<String, V> {
  final Map<String, V> _map = new Map<String, V>();

  bool containsKey(String key) => _map.containsKey(key.toUpperCase());
  bool containsValue(Object value) => _map.containsValue(value);
  V operator [](String key) => _map[key.toUpperCase()];
  void operator []=(String key, V value) {
    _map[key.toUpperCase()] = value;
  }
  V putIfAbsent(String key, V ifAbsent()) {
    _map.putIfAbsent(key.toUpperCase(), ifAbsent);
  }
  addAll(Map other) {
    other.forEach((key, value) => this[key.toUpperCase()] = value);
  }
  V remove(String key) => _map.remove(key.toUpperCase());
  void clear() => _map.clear();
  void forEach(void f(String key, V value)) => _map.forEach(f);
  Iterable<String> get keys => _map.keys;
  Iterable<V> get values => _map.values;
  int get length => _map.length;
  bool get isEmpty => _map.isEmpty;
  bool get isNotEmpty => _map.isNotEmpty;
  String toString() => _map.toString();
}
