// Copyright (c) 2017, 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:async';

import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.dart'
    show AnalysisOptions, AnalysisOptionsImpl;
import 'package:analyzer/src/generated/source.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(AnalysisSessionImplTest);
  });
}

@reflectiveTest
class AnalysisSessionImplTest {
  MockAnalysisDriver driver;
  AnalysisSessionImpl session;

  void setUp() {
    driver = new MockAnalysisDriver();
    session = new AnalysisSessionImpl(driver);
    driver.currentSession = session;
  }

  test_getErrors() async {
    ErrorsResult result = new ErrorsResult(null, null, null, null, null, null);
    driver.errorsResult = result;
    expect(await session.getErrors('path'), result);
  }

  test_getLibraryByUri() async {
    String uri = 'uri';

    var source = new _SourceMock(Uri.parse(uri));
    var unit = new CompilationUnitElementImpl('')
      ..librarySource = source
      ..source = source;
    var library = new LibraryElementImpl(null, null, null, null)
      ..definingCompilationUnit = unit;

    driver.libraryMap[uri] = library;
    expect(await session.getLibraryByUri(uri), library);
  }

  test_getParsedAst() async {
    ParseResult result =
        new ParseResult(null, null, null, null, null, null, null, null);
    driver.parseResult = result;
    expect(await session.getParsedAst('path'), result);
  }

  test_getResolvedAst() async {
    AnalysisResult result = new AnalysisResult(driver, null, null, null, null,
        null, null, null, null, null, null, null);
    driver.result = result;
    expect(await session.getResolvedAst('path'), result);
  }

  test_getSourceKind() async {
    SourceKind kind = SourceKind.LIBRARY;
    driver.sourceKind = kind;
    expect(await session.getSourceKind('path'), kind);
  }

  test_getTopLevelDeclarations() async {
    List<TopLevelDeclarationInSource> declarations = [];
    driver.topLevelDeclarations = declarations;
    expect(await session.getTopLevelDeclarations('path'), declarations);
  }

  test_getUnitElement() async {
    UnitElementResult result =
        new UnitElementResult(null, null, null, null, null);
    driver.unitElementResult = result;
    expect(await session.getUnitElement('path'), result);
  }

  test_getUnitElementSignature() async {
    String signature = 'xyzzy';
    driver.unitElementSignature = signature;
    expect(await session.getUnitElementSignature('path'), signature);
  }

  test_resourceProvider() {
    ResourceProvider resourceProvider = new MemoryResourceProvider();
    driver.resourceProvider = resourceProvider;
    expect(session.resourceProvider, resourceProvider);
  }

  test_sourceFactory() {
    SourceFactory sourceFactory = new SourceFactory([]);
    driver.sourceFactory = sourceFactory;
    expect(session.sourceFactory, sourceFactory);
  }

  test_typeProvider() async {
    _initializeSDK();
    expect(await session.typeProvider, isNotNull);
  }

  test_typeSystem() async {
    _initializeSDK();
    expect(await session.typeSystem, isNotNull);
  }

  void _initializeSDK() {
    CompilationUnitElementImpl newUnit(String name) {
      CompilationUnitElementImpl unit = new CompilationUnitElementImpl(name);
      unit.accessors = [];
      unit.enums = [];
      unit.functions = [];
      unit.typeAliases = [];
      return unit;
    }

    ClassElementImpl newClass(String name) {
      TypeParameterElementImpl param = new TypeParameterElementImpl('E', 0);
      param.type = new TypeParameterTypeImpl(param);
      ClassElementImpl element = new ClassElementImpl(name, 0);
      element.typeParameters = [param];
      return element;
    }

    {
      CompilationUnitElementImpl coreUnit = newUnit('dart.core');
      coreUnit.types = <ClassElement>[newClass('Iterable')];
      LibraryElementImpl core = new LibraryElementImpl(null, null, null, null);
      core.definingCompilationUnit = coreUnit;
      driver.libraryMap['dart:core'] = core;
    }
    {
      CompilationUnitElementImpl asyncUnit = newUnit('dart.async');
      asyncUnit.types = <ClassElement>[
        newClass('Future'),
        newClass('FutureOr'),
        newClass('Stream')
      ];
      LibraryElementImpl async = new LibraryElementImpl(null, null, null, null);
      async.definingCompilationUnit = asyncUnit;
      driver.libraryMap['dart:async'] = async;
    }
  }
}

class MockAnalysisDriver implements AnalysisDriver {
  @override
  AnalysisSession currentSession;

  ErrorsResult errorsResult;
  Map<String, LibraryElement> libraryMap = <String, LibraryElement>{};
  ParseResult parseResult;
  ResourceProvider resourceProvider;
  AnalysisResult result;
  SourceFactory sourceFactory;
  SourceKind sourceKind;
  List<TopLevelDeclarationInSource> topLevelDeclarations;
  UnitElementResult unitElementResult;
  String unitElementSignature;

  AnalysisOptions get analysisOptions => new AnalysisOptionsImpl();

  @override
  Future<ErrorsResult> getErrors(String path) async {
    return errorsResult;
  }

  @override
  Future<LibraryElement> getLibraryByUri(String uri) async {
    return libraryMap[uri];
  }

  @override
  Future<AnalysisResult> getResult(String path,
      {bool sendCachedToStream: false}) async {
    return result;
  }

  @override
  Future<SourceKind> getSourceKind(String path) async {
    return sourceKind;
  }

  @override
  Future<List<TopLevelDeclarationInSource>> getTopLevelNameDeclarations(
      String name) async {
    return topLevelDeclarations;
  }

  @override
  Future<UnitElementResult> getUnitElement(String path) async {
    return unitElementResult;
  }

  @override
  Future<String> getUnitElementSignature(String path) async {
    return unitElementSignature;
  }

  @override
  dynamic noSuchMethod(Invocation invocation) {
    fail('Unexpected invocation of ${invocation.memberName}');
  }

  @override
  Future<ParseResult> parseFile(String path) async {
    return parseResult;
  }

  @override
  ParseResult parseFileSync(String path) {
    return parseResult;
  }
}

class _SourceMock implements Source {
  @override
  final Uri uri;

  _SourceMock(this.uri);

  @override
  noSuchMethod(Invocation invocation) {
    throw new StateError('Unexpected invocation of ${invocation.memberName}');
  }
}
