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

/// Notation:
///
/// * `[[T]]` is the runtime representation of type `T` (that is, `T` reified).
library kernel.transformations.reify.runtime.types;

import 'declarations.dart' show Class;

export 'declarations.dart';

export 'interceptors.dart';

// The public interface of this library are static functions to access parts of
// reified type objects and the constructors on the ReifiedType subclasses.

bool isSubtypeOf(ReifiedType a, ReifiedType b) => a._isSubtypeOf(b);

bool isMoreSpecificThan(ReifiedType a, ReifiedType b) {
  return a._isMoreSpecificThan(b);
}

Kind getKind(ReifiedType type) => type._kind;

ReifiedType asInstanceOf(Interface type, Class declaration) {
  return type.asInstanceOf(declaration);
}

List<ReifiedType> getTypeArguments(Interface type) => type._typeArguments;

bool isDynamic(ReifiedType type) => type._isDynamic;

bool isFunction(ReifiedType type) => type._isFunction;

bool isInterface(ReifiedType type) => type._isInterface;

bool isIntersection(ReifiedType type) => type._isIntersection;

bool isVariable(ReifiedType type) => type._isVariable;

bool isVoid(ReifiedType type) => type._isVoid;

bool isObject(ReifiedType type) => false;

ReifiedType getSupertype(var type) => type._supertype;

Iterable<ReifiedType> getInterfaces(Interface type) => type._interfaces;

ReifiedType subst(ReifiedType type, List<ReifiedType> arguments,
    List<ReifiedType> parameters) {
  return type._subst(arguments, parameters);
}

// TODO(ahe): Do we need ReifiedNullType?

ReifiedType _intersection(ReifiedType a, ReifiedType b) {
  if (a == null) return b;
  if (b == null) return a;
  if (a == b) return a;
  return new Intersection(a, b);
}

enum Kind {
  Bottom,
  Dynamic,
  Function,
  Interface,
  Intersection,
  Variable,
  Void,
}

abstract class ReifiedType {
  // TODO(ahe): Should this be a getter to save memory? Which is faster?
  final Kind _kind;

  const ReifiedType(this._kind);

  bool get _isDynamic => _kind == Kind.Dynamic;

  bool get _isFunction => _kind == Kind.Function;

  bool get _isInterface => _kind == Kind.Interface;

  bool get _isIntersection => _kind == Kind.Intersection;

  bool get _isVariable => _kind == Kind.Variable;

  bool get _isVoid => _kind == Kind.Void;

  bool get _isObject => false;

  /// Returns true if [this] is more specific than [type].
  bool _isMoreSpecificThan(ReifiedType type);

  /// Performs the substitution `[arguments[i]/parameters[i]]this`.
  ///
  /// The notation is known from this lambda calculus rule:
  ///
  ///     (lambda x.e0)e1 -> [e1/x]e0.
  ///
  /// Invariant: There must be the same number of [arguments] and [parameters].
  ReifiedType _subst(List<ReifiedType> arguments, List<ReifiedType> parameters);

  /// Returns true if [this] is a subtype of [type].
  bool _isSubtypeOf(ReifiedType type) {
    return _subst(const <ReifiedType>[const Bottom()],
        const <ReifiedType>[const Dynamic()])._isMoreSpecificThan(type);
  }

  bool _isAssignableTo(ReifiedType type) {
    if (type._isDynamic) return true;
    return this._isSubtypeOf(type) || type._isSubtypeOf(this);
  }
}

/// Represents the type `dynamic`.
class Dynamic extends ReifiedType {
  const Dynamic() : super(Kind.Dynamic);

  bool _isMoreSpecificThan(ReifiedType type) => type._isDynamic;

  ReifiedType _subst(
      List<ReifiedType> arguments, List<ReifiedType> parameters) {
    int index = 0;
    for (ReifiedType parameter in parameters) {
      if (parameter._isDynamic) return arguments[index];
      index++;
    }
    return this;
  }

  String toString() => "dynamic";
}

/// Represents the bottom type.
class Bottom extends ReifiedType {
  const Bottom() : super(Kind.Bottom);

  bool _isMoreSpecificThan(ReifiedType type) => true;

  ReifiedType _subst(
      List<ReifiedType> arguments, List<ReifiedType> parameters) {
    return this;
  }

  String toString() => "<bottom>";
}

/// Represents the type `void`.
class Void extends ReifiedType {
  const Void() : super(Kind.Void);

  bool _isMoreSpecificThan(ReifiedType type) {
    // `void` isn't more specific than anything but itself.
    return type._isVoid;
  }

  bool _isSubtypeOf(ReifiedType type) {
    // `void` isn't the subtype of anything besides `dynamic` and itself.
    return type._isVoid || type._isDynamic;
  }

  ReifiedType _subst(
      List<ReifiedType> arguments, List<ReifiedType> parameters) {
    return this;
  }

  String toString() => "void";
}

/// Represents an interface type. That is, the type of any class.
///
/// For example, the type
///
///     String
///
/// Would be represented as:
///
///    new Interface(stringDeclaration);
///
/// Where `stringDeclaration` is an instance of [Class] which contains
/// information about [String]'s supertype and implemented interfaces.
///
/// A parameterized type, for example:
///
///     Box<int>
///
/// Would be represented as:
///
///     new Interface(boxDeclaration,
///         [new Interface(intDeclaration)]);
///
/// Implementation notes and considerations:
///
/// * It's possible that we want to split this class in two to save memory: one
///   for non-generic classes and one for generic classes. Only the latter
///   would need [_typeArguments]. However, this must be weighed against the
///   additional polymorphism.
/// * Generally, we don't canonicalize types. However, simple types like `new
///   Interface(intDeclaration)` should be canonicalized to save
///   memory. Precisely how this canonicalization will happen is TBD, but it
///   may simply be by using compile-time constants.
class Interface extends ReifiedType implements Type {
  final Class _declaration;

  final List<ReifiedType> _typeArguments;

  const Interface(this._declaration,
      [this._typeArguments = const <ReifiedType>[]])
      : super(Kind.Interface);

  bool get _isObject => _declaration.supertype == null;

  Interface get _supertype {
    return _declaration.supertype
        ?._subst(_typeArguments, _declaration.variables);
  }

  Iterable<Interface> get _interfaces {
    return _declaration.interfaces.map((Interface type) {
      return type._subst(_typeArguments, _declaration.variables);
    });
  }

  FunctionType get _callableType {
    return _declaration.callableType
        ?._subst(_typeArguments, _declaration.variables);
  }

  bool _isMoreSpecificThan(ReifiedType type) {
    if (type._isDynamic) return true;
    // Intersection types can only occur as the result of calling
    // [asInstanceOf], they should never be passed in to this method.
    assert(!type._isIntersection);
    if (type._isFunction) {
      return _callableType?._isMoreSpecificThan(type) ?? false;
    }
    if (!type._isInterface) return false;
    if (this == type) return true;
    ReifiedType supertype = asInstanceOfType(type);
    if (supertype == null) {
      // Special case: A callable class is a subtype of [Function], regardless
      // if it implements [Function]. It isn't more specific than
      // [Function]. The type representing [Function] is the supertype of
      // `declaration.callableType`.
      return _declaration.callableType?._supertype?._isSubtypeOf(type) ?? false;
    }
    if (type == supertype) return true;
    switch (supertype._kind) {
      case Kind.Dynamic:
      case Kind.Variable:
        // Shouldn't happen. See switch in [asInstanceOf].
        throw "internal error: $supertype";

      case Kind.Interface:
        Interface s = supertype;
        Interface t = type;
        for (int i = 0; i < s._typeArguments.length; i++) {
          if (!s._typeArguments[i]._isMoreSpecificThan(t._typeArguments[i])) {
            return false;
          }
        }
        return true;

      case Kind.Intersection:
        return supertype._isMoreSpecificThan(type);

      default:
        throw "Internal error: unhandled kind '${type._kind}'";
    }
  }

  bool _isSubtypeOf(ReifiedType type) {
    if (type._isInterface) {
      Interface interface = type;
      if (interface._declaration != this._declaration) {
        // This addition to the specified rules allows us to handle cases like
        //     class D extends A<dynamic> {}
        //     new D() is A<A>
        // where directly going to `isMoreSpecific` would leave `dynamic` in the
        // result of `asInstanceOf(A)` instead of bottom.
        ReifiedType that = asInstanceOf(interface._declaration);
        if (that != null) {
          return that._isSubtypeOf(type);
        }
      }
    }
    return super._isSubtypeOf(type) ||
        (_callableType?._isSubtypeOf(type) ?? false);
  }

  /// Returns [this] translated to [type] if [type] is a supertype of
  /// [this]. Otherwise null.
  ///
  /// For example, given:
  ///
  ///     class Box<T> {}
  ///     class BeatBox extends Box<Beat> {}
  ///     class Beat {}
  ///
  /// We have:
  ///
  ///     [[BeatBox]].asInstanceOf([[Box]]) -> [[Box<Beat>]].
  ReifiedType asInstanceOf(Class other) {
    if (_declaration == other) return this;
    ReifiedType result = _declaration.supertype
        ?._subst(_typeArguments, _declaration.variables)
        ?.asInstanceOf(other);
    for (Interface interface in _declaration.interfaces) {
      result = _intersection(
          result,
          interface
              ._subst(_typeArguments, _declaration.variables)
              .asInstanceOf(other));
    }
    return result;
  }

  ReifiedType asInstanceOfType(Interface type) {
    return asInstanceOf(type._declaration);
  }

  Interface _subst(List<ReifiedType> arguments, List<ReifiedType> parameters) {
    List<ReifiedType> copy;
    int index = 0;
    for (ReifiedType typeArgument in _typeArguments) {
      ReifiedType substitution = typeArgument._subst(arguments, parameters);
      if (substitution != typeArgument) {
        if (copy == null) {
          copy = new List<ReifiedType>.from(_typeArguments);
        }
        copy[index] = substitution;
      }
      index++;
    }
    return copy == null ? this : new Interface(_declaration, copy);
  }

  String toString() {
    StringBuffer sb = new StringBuffer();
    sb.write(_declaration.name);
    if (_typeArguments.isNotEmpty) {
      sb.write("<");
      sb.writeAll(_typeArguments, ", ");
      sb.write(">");
    }
    return "$sb";
  }

  int get hashCode {
    int code = 23;
    code = (71 * code + _declaration.hashCode) & 0x3fffffff;
    for (ReifiedType typeArgument in _typeArguments) {
      code = (71 * code + typeArgument.hashCode) & 0x3fffffff;
    }
    return code;
  }

  bool operator ==(other) {
    if (other is Interface) {
      if (_declaration != other._declaration) return false;
      if (identical(_typeArguments, other._typeArguments)) return true;
      assert(_typeArguments.length == other._typeArguments.length);
      for (int i = 0; i < _typeArguments.length; i++) {
        if (_typeArguments[i] != other._typeArguments[i]) {
          return false;
        }
      }
      return true;
    }
    return false;
  }
}

/// Represents the intersection type of [typeA] and [typeB]. The intersection
/// type represents a type that is a subtype of both [typeA] and [typeB].
///
/// This type is produced when a class implements the same interface twice with
/// different type arguments. For example:
///
///     abstract class MyNumberList implements List<int>, List<double> {}
///
/// Can lead to this intersection type:
///
///     new Intersection([[List<int>]], [[List<double>]])
///
/// For example,
///
///     [[MyNumberList]].asInstanceOf([[List]]) ->
///         new Intersection([[List<int>]], [[List<double>]])
///
/// Note: sometimes, people confuse this with union types. However the union
/// type of `A` and `B` would be anything that is a subtype of either `A` or
/// `B`.
///
/// See [Intersection types]
/// (https://en.wikipedia.org/wiki/Type_system#Intersection_types).
class Intersection extends ReifiedType {
  final ReifiedType typeA;
  final ReifiedType typeB;

  const Intersection(this.typeA, this.typeB) : super(Kind.Intersection);

  bool _isMoreSpecificThan(ReifiedType type) {
    // In the above example, `MyNumberList` is a subtype of List<int> *or*
    // List<double>.
    return typeA._isMoreSpecificThan(type) || typeB._isMoreSpecificThan(type);
  }

  ReifiedType _subst(
      List<ReifiedType> arguments, List<ReifiedType> parameters) {
    ReifiedType aSubstitution = typeA._subst(arguments, parameters);
    ReifiedType bSubstitution = typeB._subst(arguments, parameters);
    return (aSubstitution == typeA && bSubstitution == typeB)
        ? this
        : _intersection(aSubstitution, bSubstitution);
  }

  String toString() => "{ $typeA, $typeB }";
}

/// Represents a type variable aka type parameter.
///
/// For example, this class:
///
///     class Box<T> {}
///
/// Defines one type variable. In the type `Box<int>`, there are no type
/// variables.  However, `int` is a type argument to the type
/// parameter/variable `T`.
class TypeVariable extends ReifiedType {
  final int _id;

  // TODO(ahe): Do we need to reify bounds?
  ReifiedType bound;

  TypeVariable(this._id) : super(Kind.Variable);

  bool _isMoreSpecificThan(ReifiedType type) {
    if (type == this || type._isDynamic || type._isObject) return true;
    // The bounds of a type variable may contain a cycle, such as:
    //
    //     class C<T extends S, S extends T> {}
    //
    // We use the [tortoise and hare algorithm]
    // (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to
    // handle cycles.
    ReifiedType tortoise = bound;
    if (tortoise == type) return true;
    ReifiedType hare = getBoundOrNull(bound);
    while (tortoise != hare) {
      tortoise = getBoundOrNull(tortoise);
      if (tortoise == type) return true;
      hare = getBoundOrNull(getBoundOrNull(hare));
    }
    // Here we know that `tortoise == hare`. Either they're both `null` or we
    // detected a cycle.
    if (tortoise != null) {
      // There was a cycle of type variables in the bounds, but it didn't
      // involve [type]. The variable [tortoise] visited all the type variables
      // in the cycle (at least once), and each time we compared it to [type].
      return false;
    }
    // There are no cycles and it's safe to recurse on [bound].
    return bound._isMoreSpecificThan(type);
  }

  ReifiedType _subst(
      List<ReifiedType> arguments, List<ReifiedType> parameters) {
    int index = 0;
    for (ReifiedType parameter in parameters) {
      if (this == parameter) return arguments[index];
      index++;
    }
    return this;
  }

  String toString() => "#$_id";
}

/// Represents a function type.
class FunctionType extends ReifiedType {
  /// Normally, the [Interface] representing [Function]. But an
  /// implementation-specific subtype of [Function] may also be used.
  final ReifiedType _supertype;

  final ReifiedType _returnType;

  /// Encodes number of optional parameters and if the optional parameters are
  /// named.
  final int _data;

  /// Encodes the argument types. Positional parameters use one element, the
  /// type; named parameters use two, the name [String] and type. Named
  /// parameters must be sorted by name.
  final List _encodedParameters;

  static const FunctionTypeRelation subtypeRelation =
      const FunctionSubtypeRelation();

  static const FunctionTypeRelation moreSpecificRelation =
      const FunctionMoreSpecificRelation();

  const FunctionType(
      this._supertype, this._returnType, this._data, this._encodedParameters)
      : super(Kind.Function);

  bool get hasNamedParameters => (_data & 1) == 1;

  int get optionalParameters => _data >> 1;

  int get parameters {
    return hasNamedParameters
        ? _encodedParameters.length - optionalParameters
        : _encodedParameters.length;
  }

  int get requiredParameters {
    return hasNamedParameters
        ? _encodedParameters.length - optionalParameters * 2
        : _encodedParameters.length - optionalParameters;
  }

  bool _isSubtypeOf(ReifiedType type) => subtypeRelation.areRelated(this, type);

  bool _isMoreSpecificThan(ReifiedType type) {
    return moreSpecificRelation.areRelated(this, type);
  }

  FunctionType _subst(
      List<ReifiedType> arguments, List<ReifiedType> parameters) {
    List substitutedParameters;
    int positionalParameters =
        hasNamedParameters ? requiredParameters : this.parameters;
    for (int i = 0; i < _encodedParameters.length; i++) {
      if (i >= positionalParameters) {
        // Skip the name of a named parameter.
        i++;
      }
      ReifiedType type = _encodedParameters[i];
      ReifiedType substitution = type._subst(arguments, parameters);
      if (substitution != type) {
        if (substitutedParameters == null) {
          substitutedParameters = new List.from(_encodedParameters);
        }
        substitutedParameters[i] = substitution;
      }
    }
    ReifiedType substitutedReturnType =
        _returnType._subst(arguments, parameters);
    if (substitutedParameters == null) {
      if (_returnType == substitutedReturnType) return this;
      substitutedParameters = _encodedParameters;
    }
    return new FunctionType(
        _supertype, substitutedReturnType, _data, substitutedParameters);
  }

  String toString() {
    StringBuffer sb = new StringBuffer();
    sb.write("(");
    bool first = true;
    for (int i = 0; i < requiredParameters; i++) {
      if (!first) {
        sb.write(", ");
      }
      sb.write(_encodedParameters[i]);
      first = false;
    }
    if (requiredParameters != parameters) {
      if (!first) {
        sb.write(", ");
      }
      if (hasNamedParameters) {
        sb.write("{");
        first = true;
        for (int i = requiredParameters;
            i < _encodedParameters.length;
            i += 2) {
          if (!first) {
            sb.write(", ");
          }
          sb.write(_encodedParameters[i + 1]);
          sb.write(" ");
          sb.write(_encodedParameters[i]);
          first = false;
        }
        sb.write("}");
      } else {
        sb.write("[");
        first = true;
        for (int i = requiredParameters; i < _encodedParameters.length; i++) {
          if (!first) {
            sb.write(", ");
          }
          sb.write(_encodedParameters[i]);
          first = false;
        }
        sb.write("]");
      }
    }
    sb.write(") -> ");
    sb.write(_returnType);
    return "$sb";
  }
}

abstract class FunctionTypeRelation {
  const FunctionTypeRelation();

  bool areRelated(FunctionType self, ReifiedType type, {bool isMoreSpecific}) {
    if (!type._isFunction) {
      return arePartsRelated(self._supertype, type);
    }
    FunctionType other = type;
    if (!other._returnType._isVoid) {
      if (!arePartsRelated(self._returnType, other._returnType)) return false;
    }
    int positionalParameters =
        self.hasNamedParameters ? self.requiredParameters : self.parameters;
    int otherPositionalParameters =
        other.hasNamedParameters ? other.requiredParameters : other.parameters;
    if (self.requiredParameters > other.requiredParameters) return false;
    if (positionalParameters < otherPositionalParameters) return false;

    for (int i = 0; i < otherPositionalParameters; i++) {
      if (!arePartsRelated(
          self._encodedParameters[i], other._encodedParameters[i])) {
        return false;
      }
    }

    if (!other.hasNamedParameters) true;

    int j = positionalParameters;
    for (int i = otherPositionalParameters;
        i < other._encodedParameters.length;
        i += 2) {
      String name = other._encodedParameters[i];
      for (; j < self._encodedParameters.length; j += 2) {
        if (self._encodedParameters[j] == name) break;
      }
      if (j == self._encodedParameters.length) return false;
      if (!arePartsRelated(
          self._encodedParameters[j + 1], other._encodedParameters[i + 1])) {
        return false;
      }
    }

    return true;
  }

  bool arePartsRelated(ReifiedType a, ReifiedType b);
}

class FunctionSubtypeRelation extends FunctionTypeRelation {
  const FunctionSubtypeRelation();

  bool arePartsRelated(ReifiedType a, ReifiedType b) => a._isAssignableTo(b);
}

class FunctionMoreSpecificRelation extends FunctionTypeRelation {
  const FunctionMoreSpecificRelation();

  bool arePartsRelated(ReifiedType a, ReifiedType b) =>
      a._isMoreSpecificThan(b);
}

/// If [type] is a type variable, return its bound. Otherwise `null`.
ReifiedType getBoundOrNull(ReifiedType type) {
  if (type == null) return null;
  if (!type._isVariable) return null;
  TypeVariable tv = type;
  return tv.bound;
}
