import 'dart:convert';
import 'dart:io';

import 'package:args/args.dart';
import 'package:json_path/json_path.dart';
import 'package:rfc_6901/rfc_6901.dart';

import 'ppjson.dart';

void main(List<String> arguments) {
  final parser = ArgParser()
    ..addOption('file', abbr: 'f', help: 'Read JSON from file instead of stdin', valueHelp: 'filename')
    ..addOption('input', help: 'Read input as parameter instead of stdin', valueHelp: 'json input')
    ..addOption('jsonpath', aliases: ['path'], help: 'Display only the matches of the JSON document', valueHelp: 'JSONPath query')
    ..addOption('jsonpointer', aliases: ['pointer'], abbr: 'p', help: 'Display only the matches of the JSON pointer')
    ..addOption('indent', abbr: 'i', help: 'Set space indentation level (prefix with t for tab indentation)', defaultsTo: '2')
    ..addOption('max-depth', abbr: 'm', help: 'Specify maximum nesting before stopping printing');
    // ..addFlag('force-color', help: "Output using colors even when the environment doesn't allow them", hide: true);
  final ArgResults parsedArgs;
  try {
    parsedArgs = parser.parse(arguments);
  }
  on ArgParserException catch(e) {
    print(e.message);
    print('');
    print(parser.usage);
    exit(1);
  }

  // Get indent
  final indentArg = parsedArgs['indent'] as String;
  final indent = indentArg[0] == 't' ? '\t' * int.parse(indentArg.substring(1)) : ' ' * int.parse(indentArg);

  // Get max depth
  final maxDepthArg = parsedArgs['max-depth'] as String?;
  final maxDepth = int.tryParse(maxDepthArg ?? '');

  // Get JSONPath
  final jsonPath = parsedArgs['jsonpath'] as String?;

  // Get JSON Pointer
  final jsonPointer = parsedArgs['jsonpointer'] as String?;

  // Read
  String jsonInput;
  if (parsedArgs['file'] != null) {
    jsonInput = File(parsedArgs['file']).readAsStringSync();
  }
  else if (parsedArgs['input'] != null) {
    jsonInput = parsedArgs['input'];
  }
  else {
    jsonInput = '';
    while (true) {
      final line = stdin.readLineSync(retainNewlines: true);
      if (line != null) {
        jsonInput += line;
      }
      else {
        break;
      }
    }
  }

  // Parse
  final decodedData;
  try {
    decodedData = jsonDecode(jsonInput);
  }
  catch(_) {
    stderr.writeln('Unable to parse JSON input.');
    exit(1);
  }
  final List<dynamic> data;
  try {
    data = jsonPath != null ? followJsonPath(decodedData, jsonPath) : jsonPointer != null ? followJsonPointer(decodedData, jsonPointer) : [decodedData];
  }
  catch (e) {
    stderr.writeln('JSONPath Error: $e');
    exit(1);
  } 

  // Print
  if (data.isEmpty) {
    stderr.writeln('No match');
  }
  else if (data.length == 1) {
    stdout.write(prettyPrintJson(data[0], indent, maxDepth));
  }
  else {
    for (final match in data) {
      print(prettyPrintJson(match, indent, maxDepth));
    }
  }
}

List<dynamic> followJsonPath(dynamic object, String jsonPath) {
  final path = JsonPath(jsonPath);
  return path.read(object).map((e) => e.value).toList(growable: false);
}

List<dynamic> followJsonPointer(dynamic object, String jsonPointer) {
  final pointer = JsonPointer(jsonPointer);
  return [pointer.read(object)];
}