// Copyright 2013 The Flutter Authors. 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:convert';
import 'dart:io';

import 'package:args/args.dart';
import 'package:kernel/const_finder.dart';

void main(List<String> args) {
  final ArgParser parser = ArgParser();
  parser
    ..addSeparator(
      'Finds constant instances of a specified class from the\n'
      'specified package, and outputs JSON like the following:',
    )
    ..addSeparator('''
  {
    "constantInstances": [
      {
        "codePoint": 59470,
        "fontFamily": "MaterialIcons",
        "fontPackage": null,
        "matchTextDirection": false
      }
    ],
    "nonConstantInstances": [
      {
        "file": "file:///Path/to/hello_world/lib/file.dart",
        "line": 19,
        "column": 11
      }
    ]
  }''')
    ..addSeparator(
      'Where the "constantInstances" is a list of objects containing\n'
      'the properties passed to the const constructor of the class, and\n'
      '"nonConstantInstances" is a list of source locations of non-constant\n'
      'creation of the specified class. Non-constant creation cannot be\n'
      'statically evaluated by this tool, and callers may wish to treat them\n'
      'as errors. The non-constant creation may include entries that are not\n'
      'reachable at runtime.',
    )
    ..addSeparator('Required arguments:')
    ..addOption(
      'kernel-file',
      valueHelp: 'path/to/main.dill',
      help:
          'The path to a kernel file to parse, which was created from the '
          'main-package-uri library.',
      mandatory: true,
    )
    ..addOption(
      'class-library-uri',
      mandatory: true,
      help: 'The package: URI of the class to find.',
      valueHelp: 'package:flutter/src/widgets/icon_data.dart',
    )
    ..addOption(
      'class-name',
      help: 'The class name for the class to find.',
      valueHelp: 'IconData',
      mandatory: true,
    )
    ..addSeparator('Optional arguments:')
    ..addFlag('pretty', negatable: false, help: 'Pretty print JSON output (defaults to false).')
    ..addFlag('help', abbr: 'h', negatable: false, help: 'Print usage and exit')
    ..addOption(
      'annotation-class-name',
      help:
          'The class name of the annotation for classes that should be '
          'ignored.',
      valueHelp: 'StaticIconProvider',
    )
    ..addOption(
      'annotation-class-library-uri',
      help:
          'The package: URI of the class of the annotation for classes '
          'that should be ignored.',
      valueHelp: 'package:flutter/src/material/icons.dart',
    );

  final ArgResults argResults = parser.parse(args);
  T getArg<T>(String name) => argResults[name] as T;

  final String? annotationClassName = getArg<String?>('annotation-class-name');
  final String? annotationClassLibraryUri = getArg<String?>('annotation-class-library-uri');

  final bool annotationClassNameProvided = annotationClassName != null;
  final bool annotationClassLibraryUriProvided = annotationClassLibraryUri != null;
  if (annotationClassNameProvided != annotationClassLibraryUriProvided) {
    throw StateError(
      'If either "--annotation-class-name" or "--annotation-class-library-uri" are provided they both must be',
    );
  }

  if (getArg<bool>('help')) {
    stdout.writeln(parser.usage);
    exit(0);
  }

  final ConstFinder finder = ConstFinder(
    kernelFilePath: getArg<String>('kernel-file'),
    classLibraryUri: getArg<String>('class-library-uri'),
    className: getArg<String>('class-name'),
    annotationClassName: annotationClassName,
    annotationClassLibraryUri: annotationClassLibraryUri,
  );

  final JsonEncoder encoder = getArg<bool>('pretty')
      ? const JsonEncoder.withIndent('  ')
      : const JsonEncoder();

  stdout.writeln(encoder.convert(finder.findInstances()));
}
