// 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 'package:analyzer/src/codegen/tools.dart';
import 'package:front_end/src/codegen/tools.dart';

import 'api.dart';
import 'codegen_dart.dart';
import 'from_html.dart';

final GeneratedFile target = new GeneratedFile(
    'lib/protocol/protocol_constants.dart', (String pkgPath) async {
  CodegenVisitor visitor = new CodegenVisitor(readApi(pkgPath));
  return visitor.collectCode(visitor.visitApi);
});

/**
 * A visitor that produces Dart code defining constants associated with the API.
 */
class CodegenVisitor extends DartCodegenVisitor with CodeGenerator {
  CodegenVisitor(Api api) : super(api) {
    codeGeneratorSettings.commentLineLength = 79;
    codeGeneratorSettings.languageName = 'dart';
  }

  /**
   * Generate all of the constants associates with the [api].
   */
  void generateConstants() {
    _ConstantVisitor visitor = new _ConstantVisitor(api);
    visitor.visitApi();
    List<_Constant> constants = visitor.constants;
    constants.sort((first, second) => first.name.compareTo(second.name));
    for (_Constant constant in constants) {
      generateContant(constant);
    }
  }

  /**
   * Generate the given [constant].
   */
  void generateContant(_Constant constant) {
    write('const String ');
    write(constant.name);
    write(' = ');
    write(constant.value);
    writeln(';');
  }

  @override
  visitApi() {
    outputHeader(year: '2017');
    writeln();
    generateConstants();
  }
}

/**
 * A representation of a constant that is to be generated.
 */
class _Constant {
  /**
   * The name of the constant.
   */
  final String name;

  /**
   * The value of the constant.
   */
  final String value;

  /**
   * Initialize a newly created constant.
   */
  _Constant(this.name, this.value);
}

/**
 * A visitor that visits an API to compute a list of constants to be generated.
 */
class _ConstantVisitor extends HierarchicalApiVisitor {
  /**
   * The list of constants to be generated.
   */
  List<_Constant> constants = <_Constant>[];

  /**
   * Initialize a newly created visitor to visit the given [api].
   */
  _ConstantVisitor(Api api) : super(api);

  @override
  void visitNotification(Notification notification) {
    String domainName = notification.domainName;
    String event = notification.event;

    String constantName = _generateName(domainName, 'notification', event);
    constants.add(new _Constant(constantName, "'$domainName.$event'"));
    _addFieldConstants(constantName, notification.params);
  }

  @override
  void visitRequest(Request request) {
    String domainName = request.domainName;
    String method = request.method;

    String requestConstantName = _generateName(domainName, 'request', method);
    constants.add(new _Constant(requestConstantName, "'$domainName.$method'"));
    _addFieldConstants(requestConstantName, request.params);

    String responseConstantName = _generateName(domainName, 'response', method);
    _addFieldConstants(responseConstantName, request.result);
  }

  /**
   * Generate a constant for each of the fields in the given [type], where the
   * name of each constant will be composed from the [parentName] and the name
   * of the field.
   */
  void _addFieldConstants(String parentName, TypeObject type) {
    if (type == null) {
      return;
    }
    type.fields.forEach((TypeObjectField field) {
      String name = field.name;
      List<String> components = <String>[];
      components.add(parentName);
      components.addAll(_split(name));
      String fieldConstantName = _fromComponents(components);
      constants.add(new _Constant(fieldConstantName, "'$name'"));
    });
  }

  /**
   * Return a name generated by converting each of the given [components] to an
   * uppercase equivalent, then joining them with underscores.
   */
  String _fromComponents(List<String> components) =>
      components.map((String component) => component.toUpperCase()).join('_');

  /**
   * Generate a name from the [domainName], [kind] and [name] components.
   */
  String _generateName(String domainName, String kind, String name) {
    List<String> components = <String>[];
    components.addAll(_split(domainName));
    components.add(kind);
    components.addAll(_split(name));
    return _fromComponents(components);
  }

  /**
   * Return the components of the given [string] that are indicated by an upper
   * case letter.
   */
  Iterable<String> _split(String first) {
    RegExp regExp = new RegExp('[A-Z]');
    List<String> components = <String>[];
    int start = 1;
    int index = first.indexOf(regExp, start);
    while (index >= 0) {
      components.add(first.substring(start - 1, index));
      start = index + 1;
      index = first.indexOf(regExp, start);
    }
    components.add(first.substring(start - 1));
    return components;
  }
}
