/*
 * Copyright 2015-2016 Telinc1
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.telinc1.rpjg.script.command;

import java.util.HashMap;

import com.telinc1.rpjg.script.Script;
import com.telinc1.rpjg.script.command.branch.CommandCallIf;
import com.telinc1.rpjg.script.command.branch.CommandCompareVar;
import com.telinc1.rpjg.script.command.branch.CommandCompareVars;
import com.telinc1.rpjg.script.command.branch.CommandGotoIf;
import com.telinc1.rpjg.script.command.buffer.CommandBufferString;
import com.telinc1.rpjg.script.command.buffer.CommandBufferVariable;
import com.telinc1.rpjg.script.command.buffer.CommandClearBuffer;
import com.telinc1.rpjg.script.command.buffer.CommandClearString;
import com.telinc1.rpjg.script.command.buffer.CommandLoadString;
import com.telinc1.rpjg.script.command.control.CommandBlock;
import com.telinc1.rpjg.script.command.control.CommandCall;
import com.telinc1.rpjg.script.command.control.CommandEnd;
import com.telinc1.rpjg.script.command.control.CommandGoto;
import com.telinc1.rpjg.script.command.control.CommandLock;
import com.telinc1.rpjg.script.command.control.CommandRelease;
import com.telinc1.rpjg.script.command.control.CommandReturn;
import com.telinc1.rpjg.script.command.flag.CommandCheckFlag;
import com.telinc1.rpjg.script.command.flag.CommandClearFlag;
import com.telinc1.rpjg.script.command.flag.CommandSetFlag;
import com.telinc1.rpjg.script.command.message.CommandError;
import com.telinc1.rpjg.script.command.message.CommandMessage;
import com.telinc1.rpjg.script.command.variable.CommandAddVar;
import com.telinc1.rpjg.script.command.variable.CommandAddVars;
import com.telinc1.rpjg.script.command.variable.CommandAndVar;
import com.telinc1.rpjg.script.command.variable.CommandAndVars;
import com.telinc1.rpjg.script.command.variable.CommandCopyVar;
import com.telinc1.rpjg.script.command.variable.CommandDivVar;
import com.telinc1.rpjg.script.command.variable.CommandDivVars;
import com.telinc1.rpjg.script.command.variable.CommandLslVar;
import com.telinc1.rpjg.script.command.variable.CommandLsrVar;
import com.telinc1.rpjg.script.command.variable.CommandMulVar;
import com.telinc1.rpjg.script.command.variable.CommandMulVars;
import com.telinc1.rpjg.script.command.variable.CommandNotVar;
import com.telinc1.rpjg.script.command.variable.CommandOrVar;
import com.telinc1.rpjg.script.command.variable.CommandOrVars;
import com.telinc1.rpjg.script.command.variable.CommandSetVar;
import com.telinc1.rpjg.script.command.variable.CommandSubVar;
import com.telinc1.rpjg.script.command.variable.CommandSubVars;
import com.telinc1.rpjg.script.command.variable.CommandXorVar;
import com.telinc1.rpjg.script.command.variable.CommandXorVars;
import com.telinc1.rpjg.script.exception.ScriptException;

public abstract class ScriptCommand {
    private static HashMap<Integer, ScriptCommand> commands = new HashMap<>();
    private static HashMap<String, Integer> commandNames = new HashMap<>();

    static {
        ScriptCommand.addCommand(0, new CommandLock());
        ScriptCommand.addCommand(1, new CommandRelease());
        ScriptCommand.addCommand(2, new CommandBlock());
        ScriptCommand.addCommand(3, new CommandEnd());
        ScriptCommand.addCommand(4, new CommandGoto());
        ScriptCommand.addCommand(5, new CommandCall());
        ScriptCommand.addCommand(6, new CommandReturn());
        ScriptCommand.addCommand(7, new CommandSetFlag());
        ScriptCommand.addCommand(8, new CommandClearFlag());
        ScriptCommand.addCommand(9, new CommandSetVar());
        ScriptCommand.addCommand(10, new CommandAddVar());
        ScriptCommand.addCommand(11, new CommandSubVar());
        ScriptCommand.addCommand(12, new CommandAndVar());
        ScriptCommand.addCommand(13, new CommandOrVar());
        ScriptCommand.addCommand(14, new CommandXorVar());
        ScriptCommand.addCommand(15, new CommandAddVars());
        ScriptCommand.addCommand(16, new CommandSubVars());
        ScriptCommand.addCommand(17, new CommandAndVars());
        ScriptCommand.addCommand(18, new CommandOrVars());
        ScriptCommand.addCommand(19, new CommandXorVars());
        ScriptCommand.addCommand(20, new CommandCopyVar());
        ScriptCommand.addCommand(21, new CommandLoadString());
        ScriptCommand.addCommand(22, new CommandClearString());
        ScriptCommand.addCommand(23, new CommandBufferString());
        ScriptCommand.addCommand(24, new CommandBufferVariable());
        ScriptCommand.addCommand(25, new CommandClearBuffer());
        ScriptCommand.addCommand(26, new CommandMessage());
        ScriptCommand.addCommand(27, new CommandError());
        ScriptCommand.addCommand(28, new CommandNop());
        ScriptCommand.addCommand(29, new CommandNop());
        ScriptCommand.addCommand(30, new CommandNop());
        ScriptCommand.addCommand(31, new CommandNop());
        ScriptCommand.addCommand(32, new CommandNop());
        ScriptCommand.addCommand(33, new CommandCompareVar());
        ScriptCommand.addCommand(34, new CommandCompareVars());
        ScriptCommand.addCommand(35, new CommandGotoIf());
        ScriptCommand.addCommand(36, new CommandCallIf());
        ScriptCommand.addCommand(37, new CommandMulVar());
        ScriptCommand.addCommand(38, new CommandDivVar());
        ScriptCommand.addCommand(39, new CommandMulVars());
        ScriptCommand.addCommand(40, new CommandDivVars());
        ScriptCommand.addCommand(41, new CommandLslVar());
        ScriptCommand.addCommand(42, new CommandLsrVar());
        ScriptCommand.addCommand(43, new CommandNotVar());
        ScriptCommand.addCommand(44, new CommandNop());
        ScriptCommand.addCommand(45, new CommandCheckFlag());
    }

    /**
     * Adds a new command.
     * 
     * @param id - The ID of the new command.
     * @param command - The command to add.
     */
    public static void addCommand(int id, ScriptCommand command){
        ScriptCommand.commands.put(id, command);
        ScriptCommand.commandNames.put(command.getName(), id);
    }

    /**
     * Checks if the given script command exists.
     * 
     * @param id - The ID to check for.
     * @return Whether the command exists.
     */
    public static boolean exists(int id){
        return ScriptCommand.commands.containsKey(id);
    }

    /**
     * Checks if the given script command exists.
     * 
     * @param name - The name to check for.
     * @return Whether the command exists.
     */
    public static boolean exists(String name){
        if(!ScriptCommand.commandNames.containsKey(name)){
            return false;
        }

        return ScriptCommand.exists(ScriptCommand.commandNames.get(name));
    }

    /**
     * Gets an already registered command.
     * 
     * @param id - The ID of the requested command.
     * @return The requested command, or <code>null</code> if it doesn't exist.
     */
    public static ScriptCommand getCommand(int id){
        if(!ScriptCommand.exists(id)){
            return null;
        }

        return ScriptCommand.commands.get(id);
    }

    /**
     * Gets an already registered command.
     * 
     * @param name - The name of the requested command.
     * @return The requested command, or <code>null</code> if it doesn't exist.
     */
    public static ScriptCommand getCommand(String name){
        if(!ScriptCommand.exists(name)){
            return null;
        }

        return ScriptCommand.getCommand(ScriptCommand.commandNames.get(name));
    }

    /**
     * Runs the command: reads the parameters, does everything necessary.
     * 
     * @param script - The script which is executing this command.
     * @throws ScriptException If anything goes wrong.
     */
    public abstract void run(Script script) throws ScriptException;

    /**
     * @return The command's name (as defined in <code>scripts.md</code>).
     */
    public abstract String getName();

    public String getNameFormat(){
        return null;
    }

    /**
     * @return A list of the command's parameters.
     */
    public EnumCommandParameter[] getParameterTypes(){
        return new EnumCommandParameter[0];
    }
}
