// Copyright (c) 2014, 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.

import 'dart:core';

import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/util/asserts.dart' as asserts;
import 'package:path/path.dart' as pathos;

/**
 * A [UriResolver] implementation for the `package:` scheme that uses a map of
 * package names to their directories.
 */
class PackageMapUriResolver extends UriResolver {
  /**
   * The name of the `package` scheme.
   */
  static const String PACKAGE_SCHEME = "package";

  /**
   * A table mapping package names to the path of the directories containing
   * the package.
   */
  final Map<String, List<Folder>> packageMap;

  /**
   * The [ResourceProvider] for this resolver.
   */
  final ResourceProvider resourceProvider;

  /**
   * Create a new [PackageMapUriResolver].
   *
   * [packageMap] is a table mapping package names to the paths of the
   * directories containing the package
   */
  PackageMapUriResolver(this.resourceProvider, this.packageMap) {
    asserts.notNull(resourceProvider);
    asserts.notNull(packageMap);
    packageMap.forEach((name, folders) {
      if (folders.length != 1) {
        throw new ArgumentError(
            'Exactly one folder must be specified for a package.'
            'Found $name = $folders');
      }
    });
  }

  @override
  Source resolveAbsolute(Uri uri, [Uri actualUri]) {
    if (!isPackageUri(uri)) {
      return null;
    }
    // Prepare path.
    String path = uri.path;
    // Prepare path components.
    int index = path.indexOf('/');
    if (index == -1 || index == 0) {
      return null;
    }
    // <pkgName>/<relPath>
    String pkgName = path.substring(0, index);
    String relPath = path.substring(index + 1);
    // If the package is known, return the corresponding file.
    List<Folder> packageDirs = packageMap[pkgName];
    if (packageDirs != null) {
      Folder packageDir = packageDirs.single;
      File file = packageDir.getChildAssumingFile(relPath);
      return file.createSource(uri);
    }
    return null;
  }

  @override
  Uri restoreAbsolute(Source source) {
    String sourcePath = source.fullName;
    pathos.Context pathContext = resourceProvider.pathContext;
    for (String pkgName in packageMap.keys) {
      Folder pkgFolder = packageMap[pkgName][0];
      String pkgFolderPath = pkgFolder.path;
      if (sourcePath.startsWith(pkgFolderPath + pathContext.separator)) {
        String relPath = sourcePath.substring(pkgFolderPath.length + 1);
        List<String> relPathComponents = pathContext.split(relPath);
        String relUriPath = pathos.posix.joinAll(relPathComponents);
        return Uri.parse('$PACKAGE_SCHEME:$pkgName/$relUriPath');
      }
    }
    return null;
  }

  /**
   * Returns `true` if [uri] is a `package` URI.
   */
  static bool isPackageUri(Uri uri) {
    return uri.scheme == PACKAGE_SCHEME;
  }
}
