// CodeMirror Lint addon to use ESLint, copyright (c) by Angelo ZERR and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Depends on eslint.js from https://github.com/eslint/eslint

/* global eslint */

import eslint from "src/external/eslint/eslint.js"
import {parse} from "./eslint-parser.js";





const defaultConfig = {
  parserOptions: {
    ecmaVersion: 8,                       // only for default eslint parser
    sourceType: "module",
    ecmaFeatures: {
      jsx: true,
      modules: true,
      experimentalObjectRestSpread: true  // only for default eslint parser
    }
  },
  env: {
    browser: true,
    node: false,
    amd: false,
    mocha: true,
    jasmine: false,
    es6: true
  },
  globals: {
    lively: true,
    aexpr: false,
    ae: false,
    lively4url: true,
    System: true,
    SystemJS: true,
    CodeMirror: true,
    a: true,
    b: true,
    _: true,
    runZoned: true,
    Zone: true,

    // gs support
    gs: true,
    me: true,
    allies: true,
    enemies: true,
    fallen: true,
    attack: true,
    defend: true,
    skill: true,
    item: true,
    editorHistory: true,
    input: true,
    tsMorph: true,
  },
  //extends: "eslint:recommended", // seems not to work
  rules: {
    // https://eslint.org/docs/rules/
    // generated using:
    //   Array.from(this.querySelectorAll("tr")).map(tr => {
    //     const ok = !!tr.querySelector(".glyphicon-ok");
    //     const children = Array.from(tr.querySelectorAll("p"));
    //     const label = children[0].textContent
    //     const comment = children[1].textContent
    //     return `${ok ? "" : "//"} "${label}": ["error"], // ${comment}`  
    //   }).join("\n");

    // "for-direction": 2, // enforce “for” loop update clause moving the counter in the right direction.
    // "getter-return": 2, // enforce return statements in getters
    // "no-await-in-loop": 2, // disallow await inside of loops
    "no-compare-neg-zero": 2, // disallow comparing against -0
    "no-cond-assign": 2, // disallow assignment operators in conditional expressions
    "no-console": [1, { allow: ["warn", "error"] }], // disallow the use of console
    "no-constant-condition": 2, // disallow constant expressions in conditions
    "no-control-regex": 2, // disallow control characters in regular expressions
    "no-debugger": 2, // disallow the use of debugger
    "no-dupe-args": 2, // disallow duplicate arguments in function definitions
    "no-dupe-keys": 2, // disallow duplicate keys in object literals
    "no-duplicate-case": 2, // disallow duplicate case labels
    "no-empty": 2, // disallow empty block statements
    "no-empty-character-class": 2, // disallow empty character classes in regular expressions
    "no-ex-assign": 2, // disallow reassigning exceptions in catch clauses
    "no-extra-boolean-cast": 2, // disallow unnecessary boolean casts
    // "no-extra-parens": 2, // disallow unnecessary parentheses
    "no-extra-semi": 2, // disallow unnecessary semicolons
    "no-func-assign": 2, // disallow reassigning function declarations
    "no-inner-declarations": 2, // disallow variable or function declarations in nested blocks
    "no-invalid-regexp": 2, // disallow invalid regular expression strings in RegExp constructors
    "no-irregular-whitespace": 2, // disallow irregular whitespace outside of strings and comments
    "no-obj-calls": 2, // disallow calling global object properties as functions
    // "no-prototype-builtins": 2, // disallow calling some Object.prototype methods directly on objects
    "no-regex-spaces": 2, // disallow multiple spaces in regular expressions
    "no-sparse-arrays": 2, // disallow sparse arrays
    // "no-template-curly-in-string": 2, // disallow template literal placeholder syntax in regular strings
    "no-unexpected-multiline": 2, // disallow confusing multiline expressions
    "no-unreachable": 2, // disallow unreachable code after return, throw, continue, and break statements
    "no-unsafe-finally": 2, // disallow control flow statements in finally blocks
    "no-unsafe-negation": 2, // disallow negating the left operand of relational operators
    "use-isnan": 2, // require calls to isNaN() when checking for NaN
    // "valid-jsdoc": 2, // enforce valid JSDoc comments
    "valid-typeof": 2, // enforce comparing typeof expressions against valid strings
    // "accessor-pairs": 2, // enforce getter and setter pairs in objects
    // "array-callback-return": 2, // enforce return statements in callbacks of array methods
    // "block-scoped-var": 2, // enforce the use of variables within the scope they are defined
    // "class-methods-use-this": 2, // enforce that class methods utilize this
    // "complexity": 2, // enforce a maximum cyclomatic complexity allowed in a program
    // "consistent-return": 2, // require return statements to either always or never specify values
    // "curly": 2, // enforce consistent brace style for all control statements
    // "default-case": 2, // require default cases in switch statements
    // "dot-location": 2, // enforce consistent newlines before and after dots
    // "dot-notation": 2, // enforce dot notation whenever possible
    // "eqeqeq": 2, // require the use of === and !==
    // "guard-for-in": 2, // require for-in loops to include an if statement
    // "no-alert": 2, // disallow the use of alert, confirm, and prompt
    // "no-caller": 2, // disallow the use of arguments.caller or arguments.callee
    "no-case-declarations": 2, // disallow lexical declarations in case clauses
    // "no-div-regex": 2, // disallow division operators explicitly at the beginning of regular expressions
    // "no-else-return": 2, // disallow else blocks after return statements in if statements
    // "no-empty-function": 2, // disallow empty functions
    "no-empty-pattern": 2, // disallow empty destructuring patterns
    // "no-eq-null": 2, // disallow null comparisons without type-checking operators
    // "no-eval": 2, // disallow the use of eval()
    // "no-extend-native": 2, // disallow extending native types
    // "no-extra-bind": 2, // disallow unnecessary calls to .bind()
    // "no-extra-label": 2, // disallow unnecessary labels
    "no-fallthrough": 2, // disallow fallthrough of case statements
    // "no-floating-decimal": 2, // disallow leading or trailing decimal points in numeric literals
    "no-global-assign": 2, // disallow assignments to native objects or read-only global variables
    // "no-implicit-coercion": 2, // disallow shorthand type conversions
    // "no-implicit-globals": 2, // disallow variable and function declarations in the global scope
    // "no-implied-eval": 2, // disallow the use of eval()-like methods
    // "no-invalid-this": 2, // disallow this keywords outside of classes or class-like objects
    // "no-iterator": 2, // disallow the use of the __iterator__ property
    // "no-labels": 2, // disallow labeled statements
    // "no-lone-blocks": 2, // disallow unnecessary nested blocks
    // "no-loop-func": 2, // disallow function declarations and expressions inside loop statements
    // "no-magic-numbers": 2, // disallow magic numbers
    // "no-multi-spaces": 2, // disallow multiple spaces
    // "no-multi-str": 2, // disallow multiline strings
    // "no-new": 2, // disallow new operators outside of assignments or comparisons
    // "no-new-func": 2, // disallow new operators with the Function object
    // "no-new-wrappers": 2, // disallow new operators with the String, Number, and Boolean objects
    "no-octal": 2, // disallow octal literals
    // "no-octal-escape": 2, // disallow octal escape sequences in string literals
    // "no-param-reassign": 2, // disallow reassigning function parameters
    // "no-proto": 2, // disallow the use of the __proto__ property
    "no-redeclare": 2, // disallow variable redeclaration
    // "no-restricted-properties": 2, // disallow certain properties on certain objects
    // "no-return-assign": 2, // disallow assignment operators in return statements
    // "no-return-await": 2, // disallow unnecessary return await
    // "no-script-url": 2, // disallow javascript: urls
    "no-self-assign": 2, // disallow assignments where both sides are exactly the same
    // "no-self-compare": 2, // disallow comparisons where both sides are exactly the same
    // "no-sequences": 2, // disallow comma operators
    "no-throw-literal": 1, // disallow throwing literals as exceptions
    // "no-unmodified-loop-condition": 2, // disallow unmodified loop conditions
    // "no-unused-expressions": 2, // disallow unused expressions
    "no-unused-labels": 0, // disallow unused labels
    // "no-useless-call": 2, // disallow unnecessary calls to .call() and .apply()
    // "no-useless-concat": 2, // disallow unnecessary concatenation of literals or template literals
    "no-useless-escape": 1, // disallow unnecessary escape characters
    // "no-useless-return": 2, // disallow redundant return statements
    // "no-void": 2, // disallow void operators
    // "no-warning-comments": 2, // disallow specified warning terms in comments
    "no-with": 1, // disallow with statements
    // "prefer-promise-reject-errors": 2, // require using Error objects as Promise rejection reasons
    // "radix": 2, // enforce the consistent use of the radix argument when using parseInt()
    // "require-await": 2, // disallow async functions which have no await expression
    // "vars-on-top": 2, // require var declarations be placed at the top of their containing scope
    // "wrap-iife": 2, // require parentheses around immediate function invocations
    // "yoda": 2, // require or disallow “Yoda” conditions
    // "strict": 2, // require or disallow strict mode directives
    // "init-declarations": 2, // require or disallow initialization in variable declarations
    // "no-catch-shadow": 2, // disallow catch clause parameters from shadowing variables in the outer scope
    "no-delete-var": 2, // disallow deleting variables
    // "no-label-var": 2, // disallow labels that share a name with a variable
    // "no-restricted-globals": 2, // disallow specified global variables
    // "no-shadow": 2, // disallow variable declarations from shadowing variables declared in the outer scope
    // "no-shadow-restricted-names": 2, // disallow identifiers from shadowing restricted names
    "no-undef": 2, // disallow the use of undeclared variables unless mentioned in /*global */ comments
    // "no-undef-init": 2, // disallow initializing variables to undefined
    // "no-undefined": 2, // disallow the use of undefined as an identifier
    "no-unused-vars": 1, // disallow unused variables
    // "no-use-before-define": 2, // disallow the use of variables before they are defined
    // "callback-return": 2, // require return statements after callbacks
    // "global-require": 2, // require require() calls to be placed at top-level module scope
    // "handle-callback-err": 2, // require error handling in callbacks
    // "no-buffer-constructor": 2, // disallow use of the Buffer() constructor
    // "no-mixed-requires": 2, // disallow require calls to be mixed with regular variable declarations
    // "no-new-require": 2, // disallow new operators with calls to require
    // "no-path-concat": 2, // disallow string concatenation with __dirname and __filename
    // "no-process-env": 2, // disallow the use of process.env
    // "no-process-exit": 2, // disallow the use of process.exit()
    // "no-restricted-modules": 2, // disallow specified modules when loaded by require
    // "no-sync": 2, // disallow synchronous methods
    // "array-bracket-newline": 2, // enforce linebreaks after opening and before closing array brackets
    // "array-bracket-spacing": 2, // enforce consistent spacing inside array brackets
    // "array-element-newline": 2, // enforce line breaks after each array element
    // "block-spacing": 2, // disallow or enforce spaces inside of blocks after opening block and before closing block
    // "brace-style": 2, // enforce consistent brace style for blocks
    // "camelcase": 2, // enforce camelcase naming convention
    // "capitalized-comments": 2, // enforce or disallow capitalization of the first letter of a comment
    // "comma-dangle": 2, // require or disallow trailing commas
    // "comma-spacing": 2, // enforce consistent spacing before and after commas
    // "comma-style": 2, // enforce consistent comma style
    // "computed-property-spacing": 2, // enforce consistent spacing inside computed property brackets
    // "consistent-this": 2, // enforce consistent naming when capturing the current execution context
    // "eol-last": 2, // require or disallow newline at the end of files
    // "func-call-spacing": 2, // require or disallow spacing between function identifiers and their invocations
    // "func-name-matching": 2, // require function names to match the name of the variable or property to which they are assigned
    // "func-names": 2, // require or disallow named function expressions
    // "func-style": 2, // enforce the consistent use of either function declarations or expressions
    // "function-paren-newline": 2, // enforce consistent line breaks inside function parentheses
    // "id-blacklist": 2, // disallow specified identifiers
    // "id-length": 2, // enforce minimum and maximum identifier lengths
    // "id-match": 2, // require identifiers to match a specified regular expression
    // "implicit-arrow-linebreak": 2, // enforce the location of arrow function bodies
    // "indent": 2, // enforce consistent indentation
    // "jsx-quotes": 2, // enforce the consistent use of either double or single quotes in JSX attributes
    // "key-spacing": 2, // enforce consistent spacing between keys and values in object literal properties
    // "keyword-spacing": 2, // enforce consistent spacing before and after keywords
    // "line-comment-position": 2, // enforce position of line comments
    // "linebreak-style": 2, // enforce consistent linebreak style
    // "lines-around-comment": 2, // require empty lines around comments
    // "lines-between-class-members": 2, // require or disallow an empty line between class members
    // "max-depth": 2, // enforce a maximum depth that blocks can be nested
    // "max-len": 2, // enforce a maximum line length
    // "max-lines": 2, // enforce a maximum number of lines per file
    // "max-nested-callbacks": 2, // enforce a maximum depth that callbacks can be nested
    // "max-params": 2, // enforce a maximum number of parameters in function definitions
    // "max-statements": 2, // enforce a maximum number of statements allowed in function blocks
    // "max-statements-per-line": 2, // enforce a maximum number of statements allowed per line
    // "multiline-comment-style": 2, // enforce a particular style for multiline comments
    // "multiline-ternary": 2, // enforce newlines between operands of ternary expressions
    // "new-cap": 2, // require constructor names to begin with a capital letter
    // "new-parens": 2, // require parentheses when invoking a constructor with no arguments
    // "newline-per-chained-call": 2, // require a newline after each call in a method chain
    // "no-array-constructor": 2, // disallow Array constructors
    // "no-bitwise": 2, // disallow bitwise operators
    // "no-continue": 2, // disallow continue statements
    // "no-inline-comments": 2, // disallow inline comments after code
    // "no-lonely-if": 2, // disallow if statements as the only statement in else blocks
    // "no-mixed-operators": 2, // disallow mixed binary operators
    "no-mixed-spaces-and-tabs": 2, // disallow mixed spaces and tabs for indentation
    // "no-multi-assign": 2, // disallow use of chained assignment expressions
    // "no-multiple-empty-lines": 2, // disallow multiple empty lines
    // "no-negated-condition": 2, // disallow negated conditions
    // "no-nested-ternary": 2, // disallow nested ternary expressions
    // "no-new-object": 2, // disallow Object constructors
    // "no-plusplus": 2, // disallow the unary operators ++ and --
    // "no-restricted-syntax": 2, // disallow specified syntax
    // "no-tabs": 2, // disallow all tabs
    // "no-ternary": 2, // disallow ternary operators
    // "no-trailing-spaces": 2, // disallow trailing whitespace at the end of lines
    // "no-underscore-dangle": 2, // disallow dangling underscores in identifiers
    // "no-unneeded-ternary": 2, // disallow ternary operators when simpler alternatives exist
    // "no-whitespace-before-property": 2, // disallow whitespace before properties
    // "nonblock-statement-body-position": 2, // enforce the location of single-line statements
    // "object-curly-newline": 2, // enforce consistent line breaks inside braces
    // "object-curly-spacing": 2, // enforce consistent spacing inside braces
    // "object-property-newline": 2, // enforce placing object properties on separate lines
    // "one-var": 2, // enforce variables to be declared either together or separately in functions
    // "one-var-declaration-per-line": 2, // require or disallow newlines around variable declarations
    // "operator-assignment": 2, // require or disallow assignment operator shorthand where possible
    // "operator-linebreak": 2, // enforce consistent linebreak style for operators
    // "padded-blocks": 2, // require or disallow padding within blocks
    // "padding-line-between-statements": 2, // require or disallow padding lines between statements
    // "quote-props": 2, // require quotes around object literal property names
    // "quotes": 2, // enforce the consistent use of either backticks, double, or single quotes
    // "require-jsdoc": 2, // require JSDoc comments
    // "semi": 2, // require or disallow semicolons instead of ASI
    // "semi-spacing": 2, // enforce consistent spacing before and after semicolons
    // "semi-style": 2, // enforce location of semicolons
    // "sort-keys": 2, // require object keys to be sorted
    // "sort-vars": 2, // require variables within the same declaration block to be sorted
    // "space-before-blocks": 2, // enforce consistent spacing before blocks
    // "space-before-function-paren": 2, // enforce consistent spacing before function definition opening parenthesis
    // "space-in-parens": 2, // enforce consistent spacing inside parentheses
    // "space-infix-ops": 2, // require spacing around infix operators
    // "space-unary-ops": 2, // enforce consistent spacing before or after unary operators
    // "spaced-comment": 2, // enforce consistent spacing after the // or /* in a comment
    // "switch-colon-spacing": 2, // enforce spacing around colons of switch statements
    // "template-tag-spacing": 2, // require or disallow spacing between template tags and their literals
    // "unicode-bom": 2, // require or disallow Unicode byte order mark (BOM)
    // "wrap-regex": 2, // require parenthesis around regex literals
    // "arrow-body-style": 2, // require braces around arrow function bodies
    // "arrow-parens": 2, // require parentheses around arrow function arguments
    // "arrow-spacing": 2, // enforce consistent spacing before and after the arrow in arrow functions
    "constructor-super": 2, // require super() calls in constructors
    // "generator-star-spacing": 2, // enforce consistent spacing around * operators in generator functions
    "no-class-assign": 2, // disallow reassigning class members
    // "no-confusing-arrow": 2, // disallow arrow functions where they could be confused with comparisons
    "no-const-assign": 2, // disallow reassigning const variables
    "no-dupe-class-members": 2, // disallow duplicate class members
    // "no-duplicate-imports": 2, // disallow duplicate module imports
    "no-new-symbol": 2, // disallow new operators with the Symbol object
    // "no-restricted-imports": 2, // disallow specified modules when loaded by import
    "no-this-before-super": 2, // disallow this/super before calling super() in constructors
    // "no-useless-computed-key": 2, // disallow unnecessary computed property keys in object literals
    // "no-useless-constructor": 2, // disallow unnecessary constructors
    // "no-useless-rename": 2, // disallow renaming import, export, and destructured assignments to the same name
    // "no-var": 2, // require let or const instead of var
    // "object-shorthand": 2, // require or disallow method and property shorthand syntax for object literals
    // "prefer-arrow-callback": 2, // require using arrow functions for callbacks
    // "prefer-const": 2, // require const declarations for variables that are never reassigned after declared
    // "prefer-destructuring": 2, // require destructuring from arrays and/or objects
    // "prefer-numeric-literals": 2, // disallow parseInt() and Number.parseInt() in favor of binary, octal, and hexadecimal literals
    // "prefer-rest-params": 2, // require rest parameters instead of arguments
    // "prefer-spread": 2, // require spread operators instead of .apply()
    // "prefer-template": 2, // require template literals instead of string concatenation
    "require-yield": 2, // require generator functions to contain yield
    // "rest-spread-spacing": 2, // enforce spacing between rest and spread operators and their expressions
    // "sort-imports": 2, // enforce sorted import declarations within modules
    // "symbol-description": 2, // require symbol descriptions
    // "template-curly-spacing": 2, // require or disallow spacing around embedded expressions of template strings
    // "yield-star-spacing": 2, // require or disallow spacing around the * in yield* expressions
  }
}

export function validator(text, options, cm) {
  
  if (cm?.options?.mode === 'text/typescript') return [] // to TypeScript support at the moment
  
  var result = [], config = defaultConfig;
  var linter = new eslint();
  linter.defineParser("babel-parser", {parse});
  config.parser = "babel-parser";
  try {
    var errors = linter.verify(text, config);

    // lively.notify("validator ", text.slice(0,100), 2000, () => lively.openInspector(errors))

  } catch(err) {
    lively.warn("BUG error during linting ")
    // console.error("ESLINT ERROR during linting", err, "source: " + text)
    errors = []
  }

  for (var i = 0; i < errors.length; i++) {
    var error = errors[i];
    result.push({message: error.message,
               severity: getSeverity(error),
               from: getPos(error, true) ,
                 to: getPos(error, false)});	
  }
  return result;	  
}

CodeMirror.registerHelper("lint", "javascript", validator);

function getPos(error, from) {
  if (error.line === undefined) return CodeMirror.Pos(0, 0)

  var line = error.line-1, ch = from ? error.column : error.column+1;
  if (error.node && error.node.loc) {
    line = from ? error.node.loc.start.line -1 : error.node.loc.end.line -1;
    ch = from ? error.node.loc.start.column : error.node.loc.end.column;
  }
  return CodeMirror.Pos(line, ch);
}

function getSeverity(error) {
  switch(error.severity) {
    case 1:
      return "warning";
    case 2:
    return "error";	    
    default:
    return "error";
  }    
}




