/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2017 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 Data Flyout components including variable and list blocks.
 * @author marisaleung@google.com (Marisa Leung)
 */
'use strict';

/**
 * @name Blockly.DataCategory
 * @namespace
 **/
goog.provide('Blockly.DataCategory');

goog.require('Blockly.Blocks');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Variables');
goog.require('Blockly.Workspace');

/**
 * Construct the blocks required by the flyout for the variable category.
 * @param {!Blockly.Workspace} workspace The workspace containing variables.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.DataCategory = function(workspace) {
  var variableModelList = workspace.getVariablesOfType('');
  variableModelList.sort(Blockly.VariableModel.compareByName);
  var xmlList = [];

  Blockly.DataCategory.addCreateButton(xmlList, workspace, 'VARIABLE');

  for (var i = 0; i < variableModelList.length; i++) {
    Blockly.DataCategory.addDataVariable(xmlList, variableModelList[i]);
  }

  if (variableModelList.length > 0) {
    xmlList[xmlList.length - 1].setAttribute('gap', 24);
    var firstVariable = variableModelList[0];

    Blockly.DataCategory.addSetVariableTo(xmlList, firstVariable);
    Blockly.DataCategory.addChangeVariableBy(xmlList, firstVariable);
    Blockly.DataCategory.addShowVariable(xmlList, firstVariable);
    Blockly.DataCategory.addHideVariable(xmlList, firstVariable);
  }

  // Now add list variables to the flyout
  Blockly.DataCategory.addCreateButton(xmlList, workspace, 'LIST');
  variableModelList = workspace.getVariablesOfType(Blockly.LIST_VARIABLE_TYPE);
  variableModelList.sort(Blockly.VariableModel.compareByName);
  for (var i = 0; i < variableModelList.length; i++) {
    Blockly.DataCategory.addDataList(xmlList, variableModelList[i]);
  }

  if (variableModelList.length > 0) {
    xmlList[xmlList.length - 1].setAttribute('gap', 24);
    var firstVariable = variableModelList[0];

    Blockly.DataCategory.addAddToList(xmlList, firstVariable);
    Blockly.DataCategory.addSep(xmlList);
    Blockly.DataCategory.addDeleteOfList(xmlList, firstVariable);
    Blockly.DataCategory.addDeleteAllOfList(xmlList, firstVariable);
    Blockly.DataCategory.addInsertAtList(xmlList, firstVariable);
    Blockly.DataCategory.addReplaceItemOfList(xmlList, firstVariable);
    Blockly.DataCategory.addSep(xmlList);
    Blockly.DataCategory.addItemOfList(xmlList, firstVariable);
    Blockly.DataCategory.addItemNumberOfList(xmlList, firstVariable);
    Blockly.DataCategory.addLengthOfList(xmlList, firstVariable);
    Blockly.DataCategory.addListContainsItem(xmlList, firstVariable);
    Blockly.DataCategory.addSep(xmlList);
    Blockly.DataCategory.addShowList(xmlList, firstVariable);
    Blockly.DataCategory.addHideList(xmlList, firstVariable);
  }

  return xmlList;
};

/**
 * Construct and add a data_variable block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addDataVariable = function(xmlList, variable) {
  // <block id="variableId" type="data_variable">
  //    <field name="VARIABLE">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_variable', 'VARIABLE');
  // In the flyout, this ID must match variable ID for monitor syncing reasons
  xmlList[xmlList.length - 1].setAttribute('id', variable.getId());
};

/**
 * Construct and add a data_setvariableto block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addSetVariableTo = function(xmlList, variable) {
  // <block type="data_setvariableto" gap="20">
  //   <value name="VARIABLE">
  //    <shadow type="data_variablemenu"></shadow>
  //   </value>
  //   <value name="VALUE">
  //     <shadow type="text">
  //       <field name="TEXT">0</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_setvariableto',
      'VARIABLE', ['VALUE', 'text', 0]);
};

/**
 * Construct and add a data_changevariableby block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addChangeVariableBy = function(xmlList, variable) {
  // <block type="data_changevariableby">
  //   <value name="VARIABLE">
  //    <shadow type="data_variablemenu"></shadow>
  //   </value>
  //   <value name="VALUE">
  //     <shadow type="math_number">
  //       <field name="NUM">1</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_changevariableby',
      'VARIABLE', ['VALUE', 'math_number', 1]);
};

/**
 * Construct and add a data_showVariable block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addShowVariable = function(xmlList, variable) {
  // <block type="data_showvariable">
  //   <value name="VARIABLE">
  //     <shadow type="data_variablemenu"></shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_showvariable',
      'VARIABLE');
};

/**
 * Construct and add a data_hideVariable block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addHideVariable = function(xmlList, variable) {
  // <block type="data_hidevariable">
  //   <value name="VARIABLE">
  //     <shadow type="data_variablemenu"></shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidevariable',
      'VARIABLE');
};

/**
 * Construct and add a data_listcontents block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addDataList = function(xmlList, variable) {
  // <block id="variableId" type="data_listcontents">
  //    <field name="LIST">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontents', 'LIST');
  // In the flyout, this ID must match variable ID for monitor syncing reasons
  xmlList[xmlList.length - 1].setAttribute('id', variable.getId());
};

/**
 * Construct and add a data_addtolist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addAddToList = function(xmlList, variable) {
  // <block type="data_addtolist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  //   <value name="ITEM">
  //     <shadow type="text">
  //       <field name="TEXT">thing</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_addtolist', 'LIST',
      ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
};

/**
 * Construct and add a data_deleteoflist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addDeleteOfList = function(xmlList, variable) {
  // <block type="data_deleteoflist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  //   <value name="INDEX">
  //     <shadow type="math_integer">
  //       <field name="NUM">1</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_deleteoflist', 'LIST',
      ['INDEX', 'math_integer', 1]);
};

/**
 * Construct and add a data_deleteoflist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addDeleteAllOfList = function(xmlList, variable) {
  // <block type="data_deletealloflist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_deletealloflist',
      'LIST');
};

/**
 * Construct and add a data_insertatlist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addInsertAtList = function(xmlList, variable) {
  // <block type="data_insertatlist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  //   <value name="INDEX">
  //     <shadow type="math_integer">
  //       <field name="NUM">1</field>
  //     </shadow>
  //   </value>
  //   <value name="ITEM">
  //     <shadow type="text">
  //       <field name="TEXT">thing</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_insertatlist', 'LIST',
      ['INDEX', 'math_integer', 1], ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
};

/**
 * Construct and add a data_replaceitemoflist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addReplaceItemOfList = function(xmlList, variable) {
  // <block type="data_replaceitemoflist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  //   <value name="INDEX">
  //     <shadow type="math_integer">
  //       <field name="NUM">1</field>
  //     </shadow>
  //   </value>
  //   <value name="ITEM">
  //     <shadow type="text">
  //       <field name="TEXT">thing</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_replaceitemoflist',
      'LIST', ['INDEX', 'math_integer', 1], ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
};

/**
 * Construct and add a data_itemoflist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addItemOfList = function(xmlList, variable) {
  // <block type="data_itemoflist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  //   <value name="INDEX">
  //     <shadow type="math_integer">
  //       <field name="NUM">1</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemoflist', 'LIST',
      ['INDEX', 'math_integer', 1]);
};

/** Construct and add a data_itemnumoflist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addItemNumberOfList = function(xmlList, variable) {
  // <block type="data_itemnumoflist">
  //   <value name="ITEM">
  //     <shadow type="text">
  //       <field name="TEXT">thing</field>
  //     </shadow>
  //   </value>
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemnumoflist',
      'LIST', ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
};

/**
 * Construct and add a data_lengthoflist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addLengthOfList = function(xmlList, variable) {
  // <block type="data_lengthoflist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_lengthoflist', 'LIST');
};

/**
 * Construct and add a data_listcontainsitem block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addListContainsItem = function(xmlList, variable) {
  // <block type="data_listcontainsitem">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  //   <value name="ITEM">
  //     <shadow type="text">
  //       <field name="TEXT">thing</field>
  //     </shadow>
  //   </value>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontainsitem',
      'LIST', ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]);
};

/**
 * Construct and add a data_showlist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addShowList = function(xmlList, variable) {
  // <block type="data_showlist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_showlist', 'LIST');
};

/**
 * Construct and add a data_hidelist block to xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 */
Blockly.DataCategory.addHideList = function(xmlList, variable) {
  // <block type="data_hidelist">
  //   <field name="LIST" variabletype="list" id="">variablename</field>
  // </block>
  Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidelist', 'LIST');
};

/**
 * Construct a create variable button and push it to the xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {Blockly.Workspace} workspace Workspace to register callback to.
 * @param {string} type Type of variable this is for. For example, 'LIST' or
 *     'VARIABLE'.
 */
Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) {
  var button = goog.dom.createDom('button');
  // Set default msg, callbackKey, and callback values for type 'VARIABLE'
  var msg = Blockly.Msg.NEW_VARIABLE;
  var callbackKey = 'CREATE_VARIABLE';
  var callback = function(button) {
    Blockly.Variables.createVariable(button.getTargetWorkspace(), null, '');};

  if (type === 'LIST') {
    msg = Blockly.Msg.NEW_LIST;
    callbackKey = 'CREATE_LIST';
    callback = function(button) {
      Blockly.Variables.createVariable(button.getTargetWorkspace(), null,
          Blockly.LIST_VARIABLE_TYPE);};
  }
  button.setAttribute('text', msg);
  button.setAttribute('callbackKey', callbackKey);
  workspace.registerButtonCallback(callbackKey, callback);
  xmlList.push(button);
};

/**
 * Construct a variable block with the given variable, blockType, and optional
 *     value tags. Add the variable block to the given xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 * @param {?Blockly.VariableModel} variable Variable to select in the field.
 * @param {string} blockType Type of block. For example, 'data_hidelist' or
 *     data_showlist'.
 * @param {string} fieldName Name of field in block. For example: 'VARIABLE' or
 *     'LIST'.
 * @param {?Array.<string>} opt_value Optional array containing the value name
 *     and shadow type of value tags.
 * @param {?Array.<string>} opt_secondValue Optional array containing the value
 *     name and shadow type of a second pair of value tags.
 */
Blockly.DataCategory.addBlock = function(xmlList, variable, blockType,
    fieldName, opt_value, opt_secondValue) {
  if (Blockly.Blocks[blockType]) {
    var firstValueField;
    var secondValueField;
    if (opt_value) {
      firstValueField = Blockly.DataCategory.createValue(opt_value[0],
          opt_value[1], opt_value[2]);
    }
    if (opt_secondValue) {
      secondValueField = Blockly.DataCategory.createValue(opt_secondValue[0],
          opt_secondValue[1], opt_secondValue[2]);
    }

    var gap = 8;
    var blockText = '<xml>' +
        '<block type="' + blockType + '" gap="' + gap + '">' +
        Blockly.Variables.generateVariableFieldXml_(variable, fieldName) +
        firstValueField + secondValueField +
        '</block>' +
        '</xml>';
    var block = Blockly.Xml.textToDom(blockText).firstChild;
    xmlList.push(block);
  }
};

/**
 * Create the text representation of a value dom element with a shadow of the
 *     indicated type inside.
 * @param {string} valueName Name of the value tags.
 * @param {string} type The type of the shadow tags.
 * @param {string|number} value The default shadow value.
 * @return {string} The generated dom element in text.
 */
Blockly.DataCategory.createValue = function(valueName, type, value) {
  var fieldName;
  switch (valueName) {
    case 'ITEM':
      fieldName = 'TEXT';
      break;
    case 'INDEX':
      fieldName = 'NUM';
      break;
    case 'VALUE':
      if (type === 'math_number') {
        fieldName = 'NUM';
      } else {
        fieldName = 'TEXT';
      }
      break;
  }
  var valueField =
      '<value name="' + valueName + '">' +
      '<shadow type="' + type + '">' +
      '<field name="' + fieldName + '">' + value + '</field>' +
      '</shadow>' +
      '</value>';
  return valueField;
};

/**
 * Construct a block separator. Add the separator to the given xmlList.
 * @param {!Array.<!Element>} xmlList Array of XML block elements.
 */
Blockly.DataCategory.addSep = function(xmlList) {
  var gap = 36;
  var sepText = '<xml>' +
      '<sep gap="' + gap + '"/>' +
      '</xml>';
  var sep = Blockly.Xml.textToDom(sepText).firstChild;
  xmlList.push(sep);
};