/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2016 Massachusetts Institute of Technology
 * All rights reserved.
 *
 * 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.
 */

'use strict';

goog.provide('Blockly.Blocks.data');
goog.provide('Blockly.Constants.Data');

goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
goog.require('Blockly.constants');
goog.require('Blockly.ScratchBlocks.VerticalExtensions');


Blockly.Blocks['data_variable'] = {
  /**
   * Block of Variables
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": "%1",
      "lastDummyAlign0": "CENTRE",
      "args0": [
        {
          "type": "field_variable_getter",
          "text": "",
          "name": "VARIABLE",
          "variableType": ""
        }
      ],
      "category": Blockly.Categories.data,
      "checkboxInFlyout": true,
      "extensions": ["contextMenu_getVariableBlock", "colours_data", "output_string"]
    });
  }
};

Blockly.Blocks['data_setvariableto'] = {
  /**
   * Block to set variable to a certain value
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_SETVARIABLETO,
      "args0": [
        {
          "type": "field_variable",
          "name": "VARIABLE"
        },
        {
          "type": "input_value",
          "name": "VALUE"
        }
      ],
      "category": Blockly.Categories.data,
      "extensions": ["colours_data", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_changevariableby'] = {
  /**
   * Block to change variable by a certain value
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_CHANGEVARIABLEBY,
      "args0": [
        {
          "type": "field_variable",
          "name": "VARIABLE"
        },
        {
          "type": "input_value",
          "name": "VALUE"
        }
      ],
      "category": Blockly.Categories.data,
      "extensions": ["colours_data", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_showvariable'] = {
  /**
   * Block to show a variable
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_SHOWVARIABLE,
      "args0": [
        {
          "type": "field_variable",
          "name": "VARIABLE"
        }
      ],
      "previousStatement": null,
      "nextStatement": null,
      "category": Blockly.Categories.data,
      "extensions": ["colours_data"]
    });
  }
};

Blockly.Blocks['data_hidevariable'] = {
  /**
   * Block to hide a variable
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_HIDEVARIABLE,
      "args0": [
        {
          "type": "field_variable",
          "name": "VARIABLE"
        }
      ],
      "previousStatement": null,
      "nextStatement": null,
      "category": Blockly.Categories.data,
      "extensions": ["colours_data"]
    });
  }
};

Blockly.Blocks['data_listcontents'] = {
  /**
   * List reporter.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": "%1",
      "args0": [
        {
          "type": "field_variable_getter",
          "text": "",
          "name": "LIST",
          "variableType": Blockly.LIST_VARIABLE_TYPE
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["contextMenu_getListBlock", "colours_data_lists", "output_string"],
      "checkboxInFlyout": true
    });
  }
};

Blockly.Blocks['data_listindexall'] = {
  /**
   * List index menu, with all option.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": "%1",
      "args0": [
        {
          "type": "field_numberdropdown",
          "name": "INDEX",
          "value": "1",
          "min": 1,
          "precision": 1,
          "options": [
            ["1", "1"],
            [Blockly.Msg.DATA_INDEX_LAST, "last"],
            [Blockly.Msg.DATA_INDEX_ALL, "all"]
          ]
        }
      ],
      "category": Blockly.Categories.data,
      "extensions": ["colours_textfield", "output_string"]
    });
  }
};

Blockly.Blocks['data_listindexrandom'] = {
  /**
   * List index menu, with random option.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": "%1",
      "args0": [
        {
          "type": "field_numberdropdown",
          "name": "INDEX",
          "value": "1",
          "min": 1,
          "precision": 1,
          "options": [
            ["1", "1"],
            [Blockly.Msg.DATA_INDEX_LAST, "last"],
            [Blockly.Msg.DATA_INDEX_RANDOM, "random"]
          ]
        }
      ],
      "category": Blockly.Categories.data,
      "extensions": ["colours_textfield", "output_string"]
    });
  }
};

Blockly.Blocks['data_addtolist'] = {
  /**
   * Block to add item to list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_ADDTOLIST,
      "args0": [
        {
          "type": "input_value",
          "name": "ITEM"
        },
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_deleteoflist'] = {
  /**
   * Block to delete item from list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_DELETEOFLIST,
      "args0": [
        {
          "type": "input_value",
          "name": "INDEX"
        },
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_deletealloflist'] = {
  /**
   * Block to delete all items from list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_DELETEALLOFLIST,
      "args0": [
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_insertatlist'] = {
  /**
   * Block to insert item to list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_INSERTATLIST,
      "args0": [
        {
          "type": "input_value",
          "name": "ITEM"
        },
        {
          "type": "input_value",
          "name": "INDEX"
        },
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_replaceitemoflist'] = {
  /**
   * Block to insert item to list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_REPLACEITEMOFLIST,
      "args0": [
        {
          "type": "input_value",
          "name": "INDEX"
        },
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        },
        {
          "type": "input_value",
          "name": "ITEM"
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_itemoflist'] = {
  /**
   * Block for reporting item of list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_ITEMOFLIST,
      "args0": [
        {
          "type": "input_value",
          "name": "INDEX"
        },
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "output": null,
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists"],
      "outputShape": Blockly.OUTPUT_SHAPE_ROUND
    });
  }
};

Blockly.Blocks['data_itemnumoflist'] = {
  /**
   * Block for reporting the item # of a string in a list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_ITEMNUMOFLIST,
      "args0": [
        {
          "type": "input_value",
          "name": "ITEM"
        },
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "output": null,
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists"],
      "outputShape": Blockly.OUTPUT_SHAPE_ROUND
    });
  }
};

Blockly.Blocks['data_lengthoflist'] = {
  /**
   * Block for reporting length of list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_LENGTHOFLIST,
      "args0": [
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "output_number"]
    });
  }
};

Blockly.Blocks['data_listcontainsitem'] = {
  /**
   * Block to report whether list contains item.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_LISTCONTAINSITEM,
      "args0": [
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        },
        {
          "type": "input_value",
          "name": "ITEM"
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "output_boolean"]
    });
  }
};

Blockly.Blocks['data_showlist'] = {
  /**
   * Block to show a list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_SHOWLIST,
      "args0": [
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

Blockly.Blocks['data_hidelist'] = {
  /**
   * Block to hide a list.
   * @this Blockly.Block
   */
  init: function() {
    this.jsonInit({
      "message0": Blockly.Msg.DATA_HIDELIST,
      "args0": [
        {
          "type": "field_variable",
          "name": "LIST",
          "variableTypes": [Blockly.LIST_VARIABLE_TYPE]
        }
      ],
      "category": Blockly.Categories.dataLists,
      "extensions": ["colours_data_lists", "shape_statement"]
    });
  }
};

/**
 * Mixin to add a context menu for a data_variable block.  It adds one item for
 * each variable defined on the workspace.
 * @mixin
 * @augments Blockly.Block
 * @package
 * @readonly
 */
Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN = {
  /**
   * Add context menu option to change the selected variable.
   * @param {!Array} options List of menu options to add to.
   * @this Blockly.Block
   */
  customContextMenu: function(options) {
    var fieldName = 'VARIABLE';
    if (this.isCollapsed()) {
      return;
    }
    var currentVarName = this.getField(fieldName).text_;
    if (!this.isInFlyout) {
      var variablesList = this.workspace.getVariablesOfType('');
      variablesList.sort(function(a, b) {
        return Blockly.scratchBlocksUtils.compareStrings(a.name, b.name);
      });
      for (var i = 0; i < variablesList.length; i++) {
        var varName = variablesList[i].name;
        if (varName == currentVarName) continue;

        var option = {enabled: true};
        option.text = varName;

        option.callback =
            Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY(this,
                variablesList[i].getId(), fieldName);
        options.push(option);
      }
    } else {
      var renameOption = {
        text: Blockly.Msg.RENAME_VARIABLE,
        enabled: true,
        callback: Blockly.Constants.Data.RENAME_OPTION_CALLBACK_FACTORY(this,
            fieldName)
      };
      var deleteOption = {
        text: Blockly.Msg.DELETE_VARIABLE.replace('%1', currentVarName),
        enabled: true,
        callback: Blockly.Constants.Data.DELETE_OPTION_CALLBACK_FACTORY(this,
            fieldName)
      };
      options.push(renameOption);
      options.push(deleteOption);
    }
  }
};

Blockly.Extensions.registerMixin('contextMenu_getVariableBlock',
    Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN);

/**
 * Mixin to add a context menu for a data_listcontents block.  It adds one item for
 * each list defined on the workspace.
 * @mixin
 * @augments Blockly.Block
 * @package
 * @readonly
 */
Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_LIST_MIXIN = {
  /**
   * Add context menu option to change the selected list.
   * @param {!Array} options List of menu options to add to.
   * @this Blockly.Block
   */
  customContextMenu: function(options) {
    var fieldName = 'LIST';
    if (this.isCollapsed()) {
      return;
    }
    var currentVarName = this.getField(fieldName).text_;
    if (!this.isInFlyout) {
      var variablesList = this.workspace.getVariablesOfType('list');
      variablesList.sort(function(a, b) {
        return Blockly.scratchBlocksUtils.compareStrings(a.name, b.name);
      });
      for (var i = 0; i < variablesList.length; i++) {
        var varName = variablesList[i].name;
        if (varName == currentVarName) continue;

        var option = {enabled: true};
        option.text = varName;

        option.callback =
            Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY(this,
                variablesList[i].getId(), fieldName);
        options.push(option);
      }
    } else {
      var renameOption = {
        text: Blockly.Msg.RENAME_LIST,
        enabled: true,
        callback: Blockly.Constants.Data.RENAME_OPTION_CALLBACK_FACTORY(this,
            fieldName)
      };
      var deleteOption = {
        text: Blockly.Msg.DELETE_LIST.replace('%1', currentVarName),
        enabled: true,
        callback: Blockly.Constants.Data.DELETE_OPTION_CALLBACK_FACTORY(this,
            fieldName)
      };
      options.push(renameOption);
      options.push(deleteOption);
    }
  }
};
Blockly.Extensions.registerMixin('contextMenu_getListBlock',
    Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_LIST_MIXIN);

/**
 * Callback factory for dropdown menu options associated with a variable getter
 * block.  Each variable on the workspace gets its own item in the dropdown
 * menu, and clicking on that item changes the text of the field on the source
 * block.
 * @param {!Blockly.Block} block The block to update.
 * @param {string} id The id of the variable to set on this block.
 * @param {string} fieldName The name of the field to update on the block.
 * @return {!function()} A function that updates the block with the new name.
 */
Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY = function(block,
    id, fieldName) {
  return function() {
    var variableField = block.getField(fieldName);
    if (!variableField) {
      console.log("Tried to get a variable field on the wrong type of block.");
    }
    variableField.setValue(id);
  };
};

/**
 * Callback for rename variable dropdown menu option associated with a
 * variable getter block.
 * @param {!Blockly.Block} block The block with the variable to rename.
 * @param {string} fieldName The name of the field to inspect on the block.
 * @return {!function()} A function that renames the variable.
 */
Blockly.Constants.Data.RENAME_OPTION_CALLBACK_FACTORY = function(block,
    fieldName) {
  return function() {
    var workspace = block.workspace;
    var variable = block.getField(fieldName).getVariable();
    Blockly.Variables.renameVariable(workspace, variable);
  };
};

/**
 * Callback for delete variable dropdown menu option associated with a
 * variable getter block.
 * @param {!Blockly.Block} block The block with the variable to delete.
 * @param {string} fieldName The name of the field to inspect on the block.
 * @return {!function()} A function that deletes the variable.
 */
Blockly.Constants.Data.DELETE_OPTION_CALLBACK_FACTORY = function(block,
    fieldName) {
  return function() {
    var workspace = block.workspace;
    var variable = block.getField(fieldName).getVariable();
    workspace.deleteVariableById(variable.getId());
  };
};