/*
 * The MIT License
 *
 * Copyright 2020 Karus Labs.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.karuslabs.commons.command.aot.generation.blocks;

import com.karuslabs.commons.command.aot.*;

import java.text.MessageFormat;
import java.util.*;
import javax.lang.model.element.*;

import org.apache.commons.lang.StringEscapeUtils;


public class MethodBlock {
    
    StringBuilder builder;
    MessageFormat argument;
    MessageFormat literal;
    MessageFormat alias;
    Set<String> variables;
    int count;
    
    
    public MethodBlock() {
        builder = new StringBuilder();
        argument = new MessageFormat("        var {0} = new Argument<>(\"{1}\", {2}, {3}, {4}, {5});\n");
        literal = new MessageFormat("        var {0} = new Literal<>(\"{1}\", {2}, {3});\n");
        alias = new MessageFormat("        Literal.alias({0}, \"{1}\");\n");
        variables = new HashSet<>();
        count = 0;
    }
    
    
    public void start(TypeElement element) {
        variables.add("command");
        variables.add("commands");
        
        builder.setLength(0);
        builder.append("    public static Map<String, CommandNode<CommandSender> of(").append(element.getQualifiedName()).append(" command) {\n");
        builder.append("        var commands = new HashMap<String, CommandNode<CommandSender>>();\n\n");
    }
    
    
    public String command(Token token) {
        var variable = token.lexeme;
        while (!variables.add(variable)) {
            variable = variable + count++;
        }
        
        switch (token.type) {
            case ARGUMENT:
                argument(variable, token);
                break;
                
            case LITERAL:
                literal(variable, token);
                break;
                
            default:
                throw new IllegalArgumentException("Invalid token type: '" + token.type + "', found when generating method");
        }
        
        builder.append("\n");
        return variable;
    }
    
    
    void argument(String variable, Token token) {
        builder.append(argument.format(new Object[] {
            variable,
            StringEscapeUtils.escapeJava(token.lexeme),
            parameter(token, Binding.TYPE, "null"),
            parameter(token, Binding.EXECUTION, "null"),
            parameter(token, Binding.REQUIREMENT, "REQUIREMENT"),
            parameter(token, Binding.SUGGESTIONS, "null")
        }));
    }
    
    void literal(String variable, Token token) {
        builder.append(literal.format(new Object[] {
            variable,
            StringEscapeUtils.escapeJava(token.lexeme),
            parameter(token, Binding.EXECUTION, "null"),
            parameter(token, Binding.REQUIREMENT, "REQUIREMENT")
        }));
        
        var parameters = new Object[2];
        for (var name : token.aliases) {
            parameters[0] = variable;
            parameters[1] = StringEscapeUtils.escapeJava(name);
            
            builder.append(alias.format(parameters));
        }
    }
    
    
    String parameter(Token token, Binding binding, String value) {
        var parameter = token.bindings.get(binding);
        if (parameter == null) {
            return value;
        }
      
        var element = parameter.location;
        if (element instanceof ExecutableElement) {
            return "command::" + element.getSimpleName();
            
        } else if (element instanceof VariableElement) {
            return "command." + element.getSimpleName();
            
        } else {
            return value;
        }
    }
    
    
    public void link(String variable, String child) {
        builder.append("        ").append(variable).append(".addChild(").append(child).append(");\n");
    }
    
    
    public String end(List<String> roots) {
        for (var root : roots) {
            builder.append("        commands.put(").append(root).append(".getName(), ").append(root).append(");\n");
        }
        
        builder.append("        return commands;\n");
        builder.append("    }\n\n");
        return builder.toString();
    }
    
}

@@ -3483,16 +3483,17 @@
  %22null%22)
+,
 %0A       
@@ -3810,16 +3810,17 @@
 REMENT%22)
+,
 %0A       

Fix style issue