package com.jscape.inet.terminal;

import com.jscape.inet.terminal.commands.*;

import java.util.HashMap;

/**
 * Represents command set (ANSI, VT52) in terminal.
 * "State" pattern [Gamma 94].
 */
public class CommandSet {

    private final String name;
    private final HashMap commandMap = new HashMap();
    private final Object lock = new Object();

    //----------------------------------------------------------------------------

    /**
     * Creates <code>CommandSet</code> object giving its name.
     *
     * @param name command set name.
     */
    public CommandSet(String name) {
        if (name == null) {
            throw new NullPointerException("CommandSet.constructor(): null passed");
        }

        if (name.length() == 0) {
            throw new IllegalArgumentException();
        }

        this.name = name;
        addStandardCommands();
    }

    //----------------------------------------------------------------------------

    /**
     * Check if character passed is a control character.
     * Control codes:
     * oct: 0, 5, 7, 10, 11, 12, 13, 14, 15, 16, 17, 21, 23, 30, 32, 33, 177
     * dec: 0, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 24, 26, 27, 127
     *
     * @param ch character to check
     *
     * @return true if character is a control character, false otherwise
     */
    public boolean isControlChar(char ch) {
        return (
                (ch == 0)
                        || (ch == 5)
                        || (ch >= 7 && ch <= 15)
                        || (ch == 17)
                        || (ch == 19)
                        || (ch == 24)
                        || (ch == 26)
                        || (ch == 27)
                        || (ch == 127));
    }

    /**
     * Get command as a <code>ControlCommand</code> object by its
     * character sequence.
     *
     * @param sequence command sequence name.
     *
     * @return command obtained from hashmap as a <code>ControlCommand</code> object.
     */
    public ControlCommand getCommand(String sequence) {
        if (sequence == null) {
            throw new NullPointerException("CommandSet.getCommand(): null passed");
        }

        synchronized (this.lock) {
            ControlCommand result = (ControlCommand) this.commandMap.get(sequence);
            return (result != null) ? result : NullCommand.getInstance();
        }
    }

    /**
     * Add command as ControlCommand object to the internal command hash map.
     *
     * @param sequence command char sequence to add.
     * @param command  ControlCommand object for command.
     *
     * @return false if sequence already contained as a key in the map,
     *         otherwise true.
     */
    public boolean addCommand(String sequence, ControlCommand command) {
        if (sequence == null || command == null) {
            throw new NullPointerException("CommandSet.addCommand(): null passed");
        }

        synchronized (this.lock) {
            if (this.commandMap.containsKey(sequence)) {
                return false;
            }

            this.commandMap.put(sequence, command);

            return true;
        }
    }

    /**
     * Remove command's ControlCommand object from internal command hash map.
     *
     * @param sequence command sequence to remove.
     *
     * @return false if command hashmap does not contain sequence as a key,
     *         otherwise true.
     */
    public boolean removeCommand(String sequence) {
        if (sequence == null) {
            throw new NullPointerException("CommandSet.removeCommand(): null passed");
        }

        synchronized (this.lock) {
            if (!this.commandMap.containsKey(sequence)) {
                return false;
            }

            this.commandMap.remove(sequence);

            return true;
        }
    }

    /**
     * Returns command set's name.
     *
     * @return command set's name.
     */
    public String getName() {
        return this.name;
    }


    /**
     * Adds control commands wich are invoked by control characters.
     */
    private void addStandardCommands() {
        addCommand(BSCommand.CANONICAL_FORM, BSCommand.getInstance());
        addCommand(HTCommand.CANONICAL_FORM, HTCommand.getInstance());
        addCommand(LFCommand.CANONICAL_FORM, LFCommand.getInstance());
        addCommand("\013", LFCommand.getInstance());
        addCommand("\014", LFCommand.getInstance());
        addCommand(CRCommand.CANONICAL_FORM, CRCommand.getInstance());
        addCommand(SOCommand.CANONICAL_FORM, SOCommand.getInstance());
        addCommand(SICommand.CANONICAL_FORM, SICommand.getInstance());
        addCommand(CANCommand.CANONICAL_FORM, CANCommand.getInstance());
        addCommand("\032", CANCommand.getInstance());
        addCommand(ESCCommand.CANONICAL_FORM, ESCCommand.getInstance());
    }

}