// Copyright (c) 2021, 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 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/link.dart';

/// Resolves the explicit or implicit super-constructors invoked by
/// non-redirecting generative constructors.
class SuperConstructorResolver {
  final Linker _linker;

  SuperConstructorResolver(this._linker);

  void perform() {
    for (var builder in _linker.builders.values) {
      for (var interfaceElement in <InterfaceElementImpl>[
        ...builder.element.classes,
        ...builder.element.enums,
      ]) {
        for (var constructorElement in interfaceElement.constructors) {
          _constructor(interfaceElement, constructorElement);
        }
      }
    }
  }

  void _constructor(
    InterfaceElementImpl interfaceElement,
    ConstructorElementImpl element,
  ) {
    // Constructors of mixin applications are already configured.
    if (interfaceElement is ClassElementImpl &&
        interfaceElement.isMixinApplication) {
      return;
    }

    // We handle only generative constructors here.
    if (element.isFactory) {
      return;
    }

    var invokesDefaultSuperConstructor = true;

    void handleInitializers(List<ConstructorInitializer> initializers) {
      for (var initializer in initializers) {
        if (initializer is RedirectingConstructorInvocation) {
          invokesDefaultSuperConstructor = false;
        } else if (initializer is SuperConstructorInvocation) {
          invokesDefaultSuperConstructor = false;
          var name = initializer.constructorName?.name ?? 'new';
          element.superConstructor = interfaceElement.supertype
              ?.getNamedConstructor(name);
        }
      }
    }

    for (var fragment in element.fragments) {
      var node = _linker.getLinkingNode2(fragment);
      if (node is ConstructorDeclaration) {
        handleInitializers(node.initializers);
      } else if (node is PrimaryConstructorDeclaration) {
        if (node.body case var body?) {
          handleInitializers(body.initializers);
        }
      }
    }

    if (invokesDefaultSuperConstructor) {
      element.superConstructor = interfaceElement.supertype
          ?.getNamedConstructor('new');
    }
  }
}
