/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://developers.google.com/blockly/
 *
 * 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.
 */

/**
 * @fileOverview Logic blocks for Blockly.
 * @author q.neutron@gmail.com (Quynh Neutron)
 */
'use strict';

goog.provide('Blockly.Blocks.logic');

goog.require('Blockly.Blocks');
goog.require('Blockly.Types');


/**
 * Common HSV hue for all blocks in this category.
 */
Blockly.Blocks.logic.HUE = 210;

// Blockly 13 moved the mutator UI to MutatorIcon and moved reconnect onto
// Connection. Keep the legacy reconnect helper available for the original
// Ardublockly compose methods while the blocks use the new icon API below.
if (!Blockly.Mutator) {
    Blockly.Mutator = {
        reconnect: function (connection, block, inputName) {
            return Boolean(connection && typeof connection.reconnect === 'function' &&
                connection.reconnect(block, inputName));
        },
    };
}

Blockly.Blocks['controls_if'] = {
    /**
     * Block for if/elseif/else condition.
     * @this Blockly.Block
     */
    init: function () {
        this.setHelpUrl(Blockly.Msg.CONTROLS_IF_HELPURL);
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendValueInput('IF0')
            .setCheck(Blockly.Types.BOOLEAN.checkList)
            .appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);
        this.appendStatementInput('DO0')
            .appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        if (Blockly.icons && Blockly.icons.MutatorIcon) {
            this.setMutator(new Blockly.icons.MutatorIcon(['controls_if_elseif',
                'controls_if_else'], this));
        } else if (typeof Blockly.Mutator === 'function') {
            this.setMutator(new Blockly.Mutator(['controls_if_elseif',
                'controls_if_else']));
        }
        // Assign 'this' to a variable for use in the tooltip closure below.
        var thisBlock = this;
        this.setTooltip(function () {
            if (!thisBlock.elseifCount_ && !thisBlock.elseCount_) {
                return Blockly.Msg.CONTROLS_IF_TOOLTIP_1;
            } else if (!thisBlock.elseifCount_ && thisBlock.elseCount_) {
                return Blockly.Msg.CONTROLS_IF_TOOLTIP_2;
            } else if (thisBlock.elseifCount_ && !thisBlock.elseCount_) {
                return Blockly.Msg.CONTROLS_IF_TOOLTIP_3;
            } else if (thisBlock.elseifCount_ && thisBlock.elseCount_) {
                return Blockly.Msg.CONTROLS_IF_TOOLTIP_4;
            }
            return '';
        });
        this.elseifCount_ = 0;
        this.elseCount_ = 0;
    },
    /**
     * Create XML to represent the number of else-if and else inputs.
     * @return {Element} XML storage element.
     * @this Blockly.Block
     */
    mutationToDom: function () {
        if (!this.elseifCount_ && !this.elseCount_) {
            return null;
        }
        var container = document.createElement('mutation');
        if (this.elseifCount_) {
            container.setAttribute('elseif', this.elseifCount_);
        }
        if (this.elseCount_) {
            container.setAttribute('else', 1);
        }
        return container;
    },
    /**
     * Parse XML to restore the else-if and else inputs.
     * @param {!Element} xmlElement XML storage element.
     * @this Blockly.Block
     */
    domToMutation: function (xmlElement) {
        this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0;
        this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0;
        this.updateShape_();
    },
    /**
     * Populate the mutator's dialog with this block's components.
     * @param {!Blockly.Workspace} workspace Mutator's workspace.
     * @return {!Blockly.Block} Root block in mutator.
     * @this Blockly.Block
     */
    decompose: function (workspace) {
        var containerBlock = workspace.newBlock('controls_if_if');
        containerBlock.initSvg();
        var connection = containerBlock.nextConnection;
        for (var i = 1; i <= this.elseifCount_; i++) {
            var elseifBlock = workspace.newBlock('controls_if_elseif');
            elseifBlock.initSvg();
            connection.connect(elseifBlock.previousConnection);
            connection = elseifBlock.nextConnection;
        }
        if (this.elseCount_) {
            var elseBlock = workspace.newBlock('controls_if_else');
            elseBlock.initSvg();
            connection.connect(elseBlock.previousConnection);
        }
        return containerBlock;
    },
    /**
     * Reconfigure this block based on the mutator dialog's components.
     * @param {!Blockly.Block} containerBlock Root block in mutator.
     * @this Blockly.Block
     */
    compose: function (containerBlock) {
        var clauseBlock = containerBlock.nextConnection.targetBlock();
        // Count number of inputs.
        this.elseifCount_ = 0;
        this.elseCount_ = 0;
        var valueConnections = [null];
        var statementConnections = [null];
        var elseStatementConnection = null;
        while (clauseBlock) {
            switch (clauseBlock.type) {
                case 'controls_if_elseif':
                    this.elseifCount_++;
                    valueConnections.push(clauseBlock.valueConnection_);
                    statementConnections.push(clauseBlock.statementConnection_);
                    break;
                case 'controls_if_else':
                    this.elseCount_++;
                    elseStatementConnection = clauseBlock.statementConnection_;
                    break;
                default:
                    throw 'Unknown block type.';
            }
            clauseBlock = clauseBlock.nextConnection &&
                clauseBlock.nextConnection.targetBlock();
        }
        this.updateShape_();
        // Reconnect any child blocks.
        for (var i = 1; i <= this.elseifCount_; i++) {
            Blockly.Mutator.reconnect(valueConnections[i], this, 'IF' + i);
            Blockly.Mutator.reconnect(statementConnections[i], this, 'DO' + i);
        }
        Blockly.Mutator.reconnect(elseStatementConnection, this, 'ELSE');
    },
    /**
     * Store pointers to any connected child blocks.
     * @param {!Blockly.Block} containerBlock Root block in mutator.
     * @this Blockly.Block
     */
    saveConnections: function (containerBlock) {
        var clauseBlock = containerBlock.nextConnection.targetBlock();
        var i = 1;
        while (clauseBlock) {
            switch (clauseBlock.type) {
                case 'controls_if_elseif':
                    var inputIf = this.getInput('IF' + i);
                    var inputDo = this.getInput('DO' + i);
                    clauseBlock.valueConnection_ =
                        inputIf && inputIf.connection.targetConnection;
                    clauseBlock.statementConnection_ =
                        inputDo && inputDo.connection.targetConnection;
                    i++;
                    break;
                case 'controls_if_else':
                    var inputDo = this.getInput('ELSE');
                    clauseBlock.statementConnection_ =
                        inputDo && inputDo.connection.targetConnection;
                    break;
                default:
                    throw 'Unknown block type.';
            }
            clauseBlock = clauseBlock.nextConnection &&
                clauseBlock.nextConnection.targetBlock();
        }
    },
    /**
     * Modify this block to have the correct number of inputs.
     * @private
     * @this Blockly.Block
     */
    updateShape_: function () {
        // Delete everything.
        if (this.getInput('ELSE')) {
            this.removeInput('ELSE');
        }
        var i = 1;
        while (this.getInput('IF' + i)) {
            this.removeInput('IF' + i);
            this.removeInput('DO' + i);
            i++;
        }
        // Rebuild block.
        for (var i = 1; i <= this.elseifCount_; i++) {
            this.appendValueInput('IF' + i)
                .setCheck(Blockly.Types.BOOLEAN.checkList)
                .appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSEIF);
            this.appendStatementInput('DO' + i)
                .appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN);
        }
        if (this.elseCount_) {
            this.appendStatementInput('ELSE')
                .appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSE);
        }
    }
};

Blockly.Blocks['controls_if_if'] = {
    /**
     * Mutator block for if container.
     * @this Blockly.Block
     */
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_IF_IF_TITLE_IF);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_IF_IF_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['controls_if_elseif'] = {
    /**
     * Mutator bolck for else-if condition.
     * @this Blockly.Block
     */
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_IF_ELSEIF_TITLE_ELSEIF);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_IF_ELSEIF_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['controls_if_else'] = {
    /**
     * Mutator block for else condition.
     * @this Blockly.Block
     */
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_IF_ELSE_TITLE_ELSE);
        this.setPreviousStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_IF_ELSE_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['logic_compare'] = {
    /**
     * Block for comparison operator.
     * @this Blockly.Block
     */
    init: function () {
        var OPERATORS = this.RTL ? [
            ['=', 'EQ'],
            ['\u2260', 'NEQ'],
            ['>', 'LT'],
            ['\u2265', 'LTE'],
            ['<', 'GT'],
            ['\u2264', 'GTE']
        ] : [
            ['=', 'EQ'],
            ['\u2260', 'NEQ'],
            ['<', 'LT'],
            ['\u2264', 'LTE'],
            ['>', 'GT'],
            ['\u2265', 'GTE']
        ];
        this.setHelpUrl(Blockly.Msg.LOGIC_COMPARE_HELPURL);
        this.setColour(Blockly.Blocks.logic.HUE);
        this.setOutput(true, Blockly.Types.BOOLEAN.output);
        this.appendValueInput('A');
        this.appendValueInput('B')
            .appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
        this.setInputsInline(true);
        // Assign 'this' to a variable for use in the tooltip closure below.
        var thisBlock = this;
        this.setTooltip(function () {
            var op = thisBlock.getFieldValue('OP');
            var TOOLTIPS = {
                'EQ': Blockly.Msg.LOGIC_COMPARE_TOOLTIP_EQ,
                'NEQ': Blockly.Msg.LOGIC_COMPARE_TOOLTIP_NEQ,
                'LT': Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LT,
                'LTE': Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LTE,
                'GT': Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GT,
                'GTE': Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GTE
            };
            return TOOLTIPS[op];
        });
        this.prevBlocks_ = [null, null];
    },
    /**
     * Called whenever anything on the workspace changes.
     * Prevent mismatched types from being compared.
     * @param {!Blockly.Events.Abstract} e Change event.
     * @this Blockly.Block
     */
    onchange: function (e) {
        var blockA = this.getInputTargetBlock('A');
        var blockB = this.getInputTargetBlock('B');
        // Disconnect blocks that existed prior to this change if they don't match.
        if (blockA && blockB &&
            !blockA.outputConnection.checkType_(blockB.outputConnection)) {
            // Mismatch between two inputs.  Disconnect previous and bump it away.
            // Ensure that any disconnections are grouped with the causing event.
            Blockly.Events.setGroup(e.group);
            for (var i = 0; i < this.prevBlocks_.length; i++) {
                var block = this.prevBlocks_[i];
                if (block === blockA || block === blockB) {
                    block.unplug();
                    block.bumpNeighbours_();
                }
            }
            Blockly.Events.setGroup(false);
        }
        this.prevBlocks_[0] = blockA;
        this.prevBlocks_[1] = blockB;
    },
    /** Assigns a type to the block, comparison operations result in booleans. */
    getBlockType: function () {
        return Blockly.Types.BOOLEAN;
    }
};

Blockly.Blocks['logic_operation'] = {
    /**
     * Block for logical operations: 'and', 'or'.
     * @this Blockly.Block
     */
    init: function () {
        var OPERATORS =
            [[Blockly.Msg.LOGIC_OPERATION_AND, 'AND'],
                [Blockly.Msg.LOGIC_OPERATION_OR, 'OR']];
        this.setHelpUrl(Blockly.Msg.LOGIC_OPERATION_HELPURL);
        this.setColour(Blockly.Blocks.logic.HUE);
        this.setOutput(true, Blockly.Types.BOOLEAN.output);
        this.appendValueInput('A')
            .setCheck(Blockly.Types.BOOLEAN.checkList);
        this.appendValueInput('B')
            .setCheck(Blockly.Types.BOOLEAN.checkList)
            .appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
        this.setInputsInline(true);
        // Assign 'this' to a variable for use in the tooltip closure below.
        var thisBlock = this;
        this.setTooltip(function () {
            var op = thisBlock.getFieldValue('OP');
            var TOOLTIPS = {
                'AND': Blockly.Msg.LOGIC_OPERATION_TOOLTIP_AND,
                'OR': Blockly.Msg.LOGIC_OPERATION_TOOLTIP_OR
            };
            return TOOLTIPS[op];
        });
    },
    /** Assigns a block type, logic comparison operations result in bools. */
    getBlockType: function () {
        return Blockly.Types.BOOLEAN;
    }
};

Blockly.Blocks['logic_negate'] = {
    /**
     * Block for negation.
     * @this Blockly.Block
     */
    init: function () {
        this.jsonInit({
            "message0": Blockly.Msg.LOGIC_NEGATE_TITLE,
            "args0": [
                {
                    "type": "input_value",
                    "name": "BOOL",
                    "check": Blockly.Types.BOOLEAN.checkList
                }
            ],
            "output": Blockly.Types.BOOLEAN.output,
            "colour": Blockly.Blocks.logic.HUE,
            "tooltip": Blockly.Msg.LOGIC_NEGATE_TOOLTIP,
            "helpUrl": Blockly.Msg.LOGIC_NEGATE_HELPURL
        });
    },
    /** Assigns block type, 'block input' is meant to be a boolean, so same. */
    getBlockType: function () {
        return Blockly.Types.BOOLEAN;
    }
};

Blockly.Blocks['logic_boolean'] = {
    /**
     * Block for boolean data type: true and false.
     * @this Blockly.Block
     */
    init: function () {
        this.jsonInit({
            "message0": "%1",
            "args0": [
                {
                    "type": "field_dropdown",
                    "name": "BOOL",
                    "options": [
                        [Blockly.Msg.LOGIC_BOOLEAN_TRUE, "TRUE"],
                        [Blockly.Msg.LOGIC_BOOLEAN_FALSE, "FALSE"]
                    ]
                }
            ],
            "output": Blockly.Types.BOOLEAN.output,
            "colour": Blockly.Blocks.logic.HUE,
            "tooltip": Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP,
            "helpUrl": Blockly.Msg.LOGIC_BOOLEAN_HELPURL
        });
    },
    /** Assigns a type to the boolean block. */
    getBlockType: function () {
        return Blockly.Types.BOOLEAN;
    }
};

Blockly.Blocks['logic_null'] = {
    /**
     * Block for null data type.
     * @this Blockly.Block
     */
    init: function () {
        this.jsonInit({
            "message0": Blockly.Msg.LOGIC_NULL,
            "output": Blockly.Types.NULL.output,
            "colour": Blockly.Blocks.logic.HUE,
            "tooltip": Blockly.Msg.LOGIC_NULL_TOOLTIP,
            "helpUrl": Blockly.Msg.LOGIC_NULL_HELPURL
        });
    },
    /** Assigns a type to the NULL block. */
    getBlockType: function () {
        return Blockly.Types.NULL;
    }
};

Blockly.Blocks['logic_ternary'] = {
    /**
     * Block for ternary operator.
     * @this Blockly.Block
     */
    init: function () {
        this.setHelpUrl(Blockly.Msg.LOGIC_TERNARY_HELPURL);
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendValueInput('IF')
            .setCheck(Blockly.Types.BOOLEAN.checkList)
            .appendField(Blockly.Msg.LOGIC_TERNARY_CONDITION);
        this.appendValueInput('THEN')
            .appendField(Blockly.Msg.LOGIC_TERNARY_IF_TRUE);
        this.appendValueInput('ELSE')
            .appendField(Blockly.Msg.LOGIC_TERNARY_IF_FALSE);
        this.setOutput(true);
        this.setTooltip(Blockly.Msg.LOGIC_TERNARY_TOOLTIP);
        this.prevParentConnection_ = null;
    },
    /**
     * Called whenever anything on the workspace changes.
     * Prevent mismatched types.
     * @param {!Blockly.Events.Abstract} e Change event.
     * @this Blockly.Block
     */
    onchange: function (e) {
        var blockA = this.getInputTargetBlock('THEN');
        var blockB = this.getInputTargetBlock('ELSE');
        var parentConnection = this.outputConnection.targetConnection;
        // Disconnect blocks that existed prior to this change if they don't match.
        if ((blockA || blockB) && parentConnection) {
            for (var i = 0; i < 2; i++) {
                var block = (i == 1) ? blockA : blockB;
                if (block && !block.outputConnection.checkType_(parentConnection)) {
                    // Ensure that any disconnections are grouped with the causing event.
                    Blockly.Events.setGroup(e.group);
                    if (parentConnection === this.prevParentConnection_) {
                        this.unplug();
                        parentConnection.getSourceBlock().bumpNeighbours_();
                    } else {
                        block.unplug();
                        block.bumpNeighbours_();
                    }
                    Blockly.Events.setGroup(false);
                }
            }
        }
        this.prevParentConnection_ = parentConnection;
    }
    //TODO: getBlockType that uses the type of the given inputs
};

Blockly.Blocks['controls_switch'] = {
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendValueInput('SWITCH')
            .appendField(Blockly.Msg.CONTROLS_SWITCH);
        this.appendValueInput('CASE0')
            .setCheck(null)
            .appendField(Blockly.Msg.CONTROLS_SWITCH_CASE);
        this.appendStatementInput('DO0')
            .appendField(Blockly.Msg.CONTROLS_SWITCH_DO);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        if (Blockly.icons && Blockly.icons.MutatorIcon) {
            this.setMutator(new Blockly.icons.MutatorIcon(['controls_switch_case',
                'controls_switch_default'], this));
        } else if (typeof Blockly.Mutator === 'function') {
            this.setMutator(new Blockly.Mutator(['controls_switch_case', 'controls_switch_default']));
        }
        // Assign 'this' to a variable for use in the tooltip closure below.
        var thisBlock = this;
        this.setTooltip(function () {
            if (!thisBlock.caseCount_ && !thisBlock.defaultCount_) {
                return Blockly.Msg.CONTROLS_SWITCH_TOOLTIP_1;
            } else if (!thisBlock.caseCount_ && thisBlock.defaultCount_) {
                return Blockly.Msg.CONTROLS_SWITCH_TOOLTIP_2;
            } else if (thisBlock.caseCount_ && !thisBlock.defaultCount_) {
                return Blockly.Msg.CONTROLS_SWITCH_TOOLTIP_3;
            } else if (thisBlock.caseCount_ && thisBlock.defaultCount_) {
                return Blockly.Msg.CONTROLS_SWITCH_TOOLTIP_4;
            }
            return '';
        });
        this.caseCount_ = 0;
        this.defaultCount_ = 0;
    },
    /**
     * Create XML to represent the number of else-if and else inputs.
     * @return {Element} XML storage element.
     * @this Blockly.Block
     */
    mutationToDom: function () {
        if (!this.caseCount_ && !this.defaultCount_) {
            return null;
        }
        var container = document.createElement('mutation');
        if (this.caseCount_) {
            container.setAttribute('case', this.caseCount_);
        }
        if (this.defaultCount_) {
            container.setAttribute('default', 1);
        }

        return container;
    },
    /**
     * Parse XML to restore the else-if and else inputs.
     * @param {!Element} xmlElement XML storage element.
     * @this Blockly.Block
     */
    domToMutation: function (xmlElement) {
        this.caseCount_ = parseInt(xmlElement.getAttribute('case'), 10) || 0;
        this.defaultCount_ = parseInt(xmlElement.getAttribute('default'), 10) || 0;
        this.updateShape_();
    },
    /**
     * Populate the mutator's dialog with this block's components.
     * @param {!Blockly.Workspace} workspace Mutator's workspace.
     * @return {!Blockly.Block} Root block in mutator.
     * @this Blockly.Block
     */
    decompose: function (workspace) {
        var containerBlock = workspace.newBlock('controls_switch_switch');
        containerBlock.initSvg();
        var connection = containerBlock.nextConnection;
        for (var x = 1; x <= this.caseCount_; x++) {
            var caseBlock = workspace.newBlock('controls_switch_case');
            caseBlock.initSvg();
            connection.connect(caseBlock.previousConnection);
            connection = caseBlock.nextConnection;
        }
        if (this.defaultCount_) {
            var defaultBlock = workspace.newBlock('controls_switch_default');
            defaultBlock.initSvg();
            connection.connect(defaultBlock.previousConnection);
        }
        return containerBlock;
    },
    /**
     * Reconfigure this block based on the mutator dialog's components.
     * @param {!Blockly.Block} containerBlock Root block in mutator.
     * @this Blockly.Block
     */
    compose: function (containerBlock) {
        var clauseBlock = containerBlock.nextConnection.targetBlock();
        // Count number of inputs.
        this.caseCount_ = 0;
        this.defaultCount_ = 0;
        var valueConnections = [null];
        var statementConnections = [null];
        var defaultStatementConnection = null;
        while (clauseBlock) {
            switch (clauseBlock.type) {
                case 'controls_switch_case':
                    this.caseCount_++;
                    valueConnections.push(clauseBlock.valueConnection_);
                    statementConnections.push(clauseBlock.statementConnection_);
                    break;
                case 'controls_switch_default':
                    this.defaultCount_++;
                    defaultStatementConnection = clauseBlock.statementConnection_;
                    break;
                default:
                    throw 'Unknown block type.';
            }
            clauseBlock = clauseBlock.nextConnection &&
                clauseBlock.nextConnection.targetBlock();
        }
        this.updateShape_();
        // Reconnect any child blocks.
        for (var i = 1; i <= this.caseCount_; i++) {
            Blockly.Mutator.reconnect(valueConnections[i], this, 'CASE' + i);
            Blockly.Mutator.reconnect(statementConnections[i], this, 'DO' + i);
        }
        Blockly.Mutator.reconnect(defaultStatementConnection, this, 'DEFAULT');
    },
    /**
     * Store pointers to any connected child blocks.
     * @param {!Blockly.Block} containerBlock Root block in mutator.
     * @this Blockly.Block
     */
    saveConnections: function (containerBlock) {
        var clauseBlock = containerBlock.getInputTargetBlock('STACK');
        var i = 1;
        while (clauseBlock) {
            switch (clauseBlock.type) {
                case 'controls_switch_case':
                    var inputCase = this.getInput('CASE' + i);
                    var inputDo = this.getInput('DO' + i);
                    clauseBlock.valueConnection_ =
                        inputCase && inputCase.connection.targetConnection;
                    clauseBlock.statementConnection_ =
                        inputDo && inputDo.connection.targetConnection;
                    x++;
                    break;
                case 'controls_switch_default':
                    var inputDo = this.getInput('DEFAULT');
                    clauseBlock.statementConnection_ =
                        inputDo && inputDo.connection.targetConnection;
                    break;
                default:
                    throw 'Unknown block type.';
            }
            clauseBlock = clauseBlock.nextConnection &&
                clauseBlock.nextConnection.targetBlock();
        }
    },
    //when the block is changed,
    /**
     * Modify this block to have the correct number of inputs.
     * @private
     * @this Blockly.Block
     */
    updateShape_: function () {
        // Delete everything.
        if (this.getInput('DEFAULT')) {
            this.removeInput('DEFAULT');
            this.removeInput('DEFAULTDO');
        }
        var i = 1;
        while (this.getInput('CASE' + i)) {
            this.removeInput('CASE' + i);
            this.removeInput('DO' + i);
            i++;
        }
        // Rebuild block.
        for (var i = 1; i <= this.caseCount_; i++) {
            this.appendValueInput('CASE' + i)
                .setCheck(null)
                .appendField(Blockly.Msg.CONTROLS_SWITCH_CASE);
            this.appendStatementInput('DO' + i)
                .appendField(Blockly.Msg.CONTROLS_SWITCH_DO);
        }
        if (this.defaultCount_) {
            this.appendStatementInput('DEFAULT')
                .appendField(Blockly.Msg.CONTROLS_SWITCH_DEFAULT);
            this.appendDummyInput('DEFAULTDO')
                .appendField(Blockly.Msg.CONTROLS_SWITCH_DO);
        }
    }
};

Blockly.Blocks['controls_switch_switch'] = {
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_SWITCH);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_SWITCH_CASE_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['controls_switch_case'] = {
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_SWITCH_CASE);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_SWITCH_CASE_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['controls_switch_default'] = {
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_SWITCH_DEFAULT);
        this.setPreviousStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_SWITCH_DEFAULT_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['controls_switch_break'] = {
    /**
     * Block for break in the switch block.
     * @this Blockly.Block
     */
    init: function () {
        this.setColour(Blockly.Blocks.logic.HUE);
        this.appendDummyInput()
            .appendField(Blockly.Msg.CONTROLS_SWITCH_BREAK);
        this.setPreviousStatement(true);
        this.setTooltip(Blockly.Msg.CONTROLS_SWITCH_BREAK_TOOLTIP);
    },
    /**
     * Called whenever anything on the workspace changes.
     * Add warning if this flow block is not nested inside a loop.
     * @this Blockly.Block
     */
    onchange: function () {
        if (!this.workspace) {
            // Block has been deleted.
            return;
        }
        var legal = false;
        // Is the block nested in a control statement?
        var block = this;
        do {
            if (block.type == 'controls_switch') {
                legal = true;
                break;
            }
            block = block.getSurroundParent();
        } while (block);
        if (legal) {
            this.setWarningText(null);
        } else {
            this.setWarningText(Blockly.Msg.CONTROLS_SWITCH_BREAK_WARNING);
        }
    }
};
