/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-15 The Processing Foundation

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package processing.mode.java.pdex;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.TreeMap;

import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;

import processing.app.Language;
import processing.core.PApplet;
import processing.data.StringList;


public class ErrorMessageSimplifier {
  /**
   * Mapping between ProblemID constant and the constant name. Holds about 650
   * of them. Also, this is just temporary, will be used to find the common
   * error types, cos you know, identifying String names is easier than
   * identifying 8 digit int constants!
   * TODO: this is temporary
   */
  private static TreeMap<Integer, String> constantsMap;


  public ErrorMessageSimplifier() {

    new Thread() {
      public void run() {
        prepareConstantsList();
      }
    }.start();
  }


  private static void prepareConstantsList() {
    constantsMap = new TreeMap<Integer, String>();
    Class<DefaultProblem> probClass = DefaultProblem.class;
    Field f[] = probClass.getFields();
    for (Field field : f) {
      if (Modifier.isStatic(field.getModifiers()))
        try {
          //System.out.println(field.getName() + " :" + field.get(null));
          Object val = field.get(null);
          if (val instanceof Integer) {
            constantsMap.put((Integer) (val), field.getName());
          }
        } catch (Exception e) {
          e.printStackTrace();
          break;
        }
    }
    //System.out.println("Total items: " + constantsMap.size());
  }


  public static String getIDName(int id) {
    if (constantsMap == null){
      prepareConstantsList();
    }
    return constantsMap.get(id);
  }


  /**
   * Tones down the jargon in the ecj reported errors.
   */
  public static String getSimplifiedErrorMessage(Problem problem) {
    if (problem == null) return null;

    IProblem iprob = problem.getIProblem();
    String args[] = iprob.getArguments();
//    Base.log("Simplifying message: " + problem.getMessage() + " ID: "
//        + getIDName(iprob.getID()));
//    Base.log("Arg count: " + args.length);
//    for (int i = 0; i < args.length; i++) {
//      Base.log("Arg " + args[i]);
//    }

    String result = null;

    switch (iprob.getID()) {

    case IProblem.ParsingError:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.error_on", args[0]);
      }
      break;

    case IProblem.ParsingErrorDeleteToken:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.error_on", args[0]);
      }
      break;

    case IProblem.ParsingErrorInsertToComplete:
      if (args.length > 0) {
        if (args[0].length() == 1) {
          result = getErrorMessageForBracket(args[0].charAt(0));

        } else {
          if (args[0].equals("AssignmentOperator Expression")) {
            result = Language.interpolate("editor.status.missing.add", "=");

          } else if (args[0].equalsIgnoreCase(") Statement")) {
            result = getErrorMessageForBracket(args[0].charAt(0));

          } else {
            result = Language.interpolate("editor.status.error_on", args[0]);
          }
        }
      }
      break;

    case IProblem.ParsingErrorInvalidToken:
      if (args.length > 0) {
        if (args[1].equals("VariableDeclaratorId")) {
          if (args[0].equals("int")) {
            result = Language.text ("editor.status.reserved_words");
          } else {
            result = Language.interpolate("editor.status.error_on", args[0]);
          }
        } else {
          result = Language.interpolate("editor.status.error_on", args[0]);
        }
      }
      break;

    case IProblem.ParsingErrorInsertTokenAfter:
      if (args.length > 0) {
        if (args[1].length() == 1) {
          result = getErrorMessageForBracket(args[1].charAt(0));
        }
        else {
          // https://github.com/processing/processing/issues/3104
          if (args[1].equalsIgnoreCase("Statement")) {
            result = Language.interpolate("editor.status.error_on", args[0]);
          } else {
            result =
              Language.interpolate("editor.status.error_on", args[0]) + " " +
              Language.interpolate("editor.status.missing.add", args[1]);
          }
        }
      }
      break;

    case IProblem.UndefinedConstructor:
      if (args.length == 2) {
        String constructorName = args[0];
        // For messages such as "contructor sketch_name.ClassXYZ() is undefined", change
        // constructor name to "ClassXYZ()". See #3434
        if (constructorName.contains(".")) {
          // arg[0] contains sketch name twice: sketch_150705a.sketch_150705a.Thing
          constructorName = constructorName.substring(constructorName.indexOf('.') + 1);
          constructorName = constructorName.substring(constructorName.indexOf('.') + 1);
        }
        String constructorArgs = removePackagePrefixes(args[args.length - 1]);
        result = Language.interpolate("editor.status.undefined_constructor", constructorName, constructorArgs);
      }
      break;

    case IProblem.UndefinedMethod:
      if (args.length > 2) {
        String methodName = args[args.length - 2];
        String methodArgs = removePackagePrefixes(args[args.length - 1]);
        result = Language.interpolate("editor.status.undefined_method", methodName, methodArgs);
      }
      break;

    case IProblem.ParameterMismatch:
      if (args.length > 3) {
        // 2nd arg is method name, 3rd arg is correct param list
        if (args[2].trim().length() == 0) {
          // the case where no params are needed.
          result = Language.interpolate("editor.status.empty_param", args[1]);

        } else {
          result = Language.interpolate("editor.status.wrong_param",
                                 args[1], args[1], removePackagePrefixes(args[2]));
//          String method = q(args[1]);
//          String methodDef = " \"" + args[1] + "(" + getSimpleName(args[2]) + ")\"";
//          result = result.replace("method", method);
//          result += methodDef;
        }
      }
      break;

    case IProblem.UndefinedField:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.undef_global_var", args[0]);
      }
      break;

    case IProblem.UndefinedType:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.undef_class", args[0]);
      }
      break;

    case IProblem.UnresolvedVariable:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.undef_var", args[0]);
      }
      break;

    case IProblem.UndefinedName:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.undef_name", args[0]);
      }
      break;

    case IProblem.TypeMismatch:
      if (args.length > 1) {
        result = Language.interpolate("editor.status.type_mismatch", args[0], args[1]);
//        result = result.replace("typeA", q(args[0]));
//        result = result.replace("typeB", q(args[1]));
      }
      break;

    case IProblem.LocalVariableIsNeverUsed:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.unused_variable", args[0]);
      }
      break;

    case IProblem.UninitializedLocalVariable:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.uninitialized_variable", args[0]);
      }
      break;

    case IProblem.AssignmentHasNoEffect:
      if (args.length > 0) {
        result = Language.interpolate("editor.status.no_effect_assignment", args[0]);
      }
      break;
    }

    //log("Simplified Error Msg: " + result);
    return (result == null) ? problem.getMessage() : result;
  }


  /**
   * Converts java.lang.String into String, etc
   */
  static private String removePackagePrefixes(String input) {
    if (!input.contains(".")) {
      return input;
    }
    String[] names = PApplet.split(input, ',');
//    List<String> names = new ArrayList<String>();
//    if (inp.indexOf(',') >= 0) {
//      names.addAll(Arrays.asList(inp.split(",")));
//    } else {
//      names.add(inp);
//    }
    StringList result = new StringList();
    for (String name : names) {
      int dot = name.lastIndexOf('.');
      if (dot >= 0) {
        name = name.substring(dot + 1, name.length());
      }
      result.append(name);
    }
    return result.join(", ");
  }


  static private String getErrorMessageForBracket(char c) {
    switch (c) {
      case ';': return Language.text("editor.status.missing.semicolon");
      case '[': return Language.text("editor.status.missing.left_sq_bracket");
      case ']': return Language.text("editor.status.missing.right_sq_bracket");
      case '(': return Language.text("editor.status.missing.left_paren");
      case ')': return Language.text("editor.status.missing.right_paren");
      case '{': return Language.text("editor.status.missing.left_curly_bracket");
      case '}': return Language.text("editor.status.missing.right_curly_bracket");
    }
    // This seems to be unreachable and wasn't in PDE.properties.
    // I've added it for 3.0a8, but that seems gross. [fry]
    return Language.interpolate("editor.status.missing.default", c);
  }


//  static private final String q(Object quotable) {
//    return "\"" + quotable + "\"";
//  }


//  static private final String qs(Object quotable) {
//    return " " + q(quotable);
//  }
}
