text: 'use strict';
  
  goog.provide('Blockly.JavaScript.Timeouts');
  
  goog.require('Blockly.JavaScript');
  
  Blockly.CustomBlocks = Blockly.CustomBlocks || [];
  Blockly.CustomBlocks.push('Timeouts');
  
  Blockly.Timeouts = {
      HUE: 70,
      blocks: {}
  };
  
  /**
   * Ensure two identically-named procedures don't exist.
   * @param {string} name Proposed procedure name.
   * @param {!Blockly.Block} block Block to disambiguate.
   * @return {string} Non-colliding name.
   */
  Blockly.Timeouts.findLegalName = function(name, block) {
      if (block.isInFlyout) {
          // Flyouts can have multiple procedures called 'do something'.
          return name;
      }
      while (!Blockly.Timeouts.isLegalName_(name, block.workspace, block)) {
          // Collision with another procedure.
          var r = name.match(/^(.*?)(\d+)$/);
          if (!r) {
              name += '2';
          } else {
              name = r[1] + (parseInt(r[2], 10) + 1);
          }
      }
      return name;
  };
  
  /**
   * Does this procedure have a legal name?  Illegal names include names of
   * procedures already defined.
   * @param {string} name The questionable name.
   * @param {!Blockly.Workspace} workspace The workspace to scan for collisions.
   * @param {Blockly.Block=} opt_exclude Optional block to exclude from
   *     comparisons (one doesn't want to collide with oneself).
   * @return {boolean} True if the name is legal.
   * @private
   */
  Blockly.Timeouts.isLegalName_ = function(name, workspace, opt_exclude) {
      var blocks = workspace.getAllBlocks();
      // Iterate through every block and check the name.
      for (var i = 0; i < blocks.length; i++) {
          if (blocks[i] == opt_exclude) {
              continue;
          }
          if (blocks[i].isTimeout_ || blocks[i].isInterval_) {
              var blockName = blocks[i].getFieldValue('NAME');
              if (Blockly.Names.equals(blockName, name)) {
                  return false;
              }
          }
      }
      return true;
  };
  
  /**
   * Rename a procedure.  Called by the editable field.
   * @param {string} name The proposed new name.
   * @return {string} The accepted name.
   * @this {!Blockly.Field}
   */
  Blockly.Timeouts.rename = function (name) {
      // Strip leading and trailing whitespace.  Beyond this, all names are legal.
      name = name.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
      return Blockly.Timeouts.findLegalName(name, this.sourceBlock_);
  };
  
  // --- setTimeout -----------------------------------------------------------
  Blockly.Timeouts.blocks['timeouts_settimeout'] =
      '<block type="timeouts_settimeout">'
      + '     <value name="NAME">'
      + '     </value>'
      + '     <value name="DELAY">'
      + '     </value>'
      + '     <value name="UNIT">'
      + '     </value>'
      + '     <value name="STATEMENT">'
      + '     </value>'
      + '</block>';
  
  Blockly.Blocks['timeouts_settimeout'] = {
      init: function() {
          var nameField = new Blockly.FieldTextInput(
              Blockly.Timeouts.findLegalName('timeout', this),
              Blockly.Timeouts.rename);
  
          nameField.setSpellcheck(false);
  
          this.appendDummyInput()
              .appendField(Blockly.Words['timeouts_settimeout'][systemLang])
              .appendField(nameField, 'NAME')
              .appendField(Blockly.Words['timeouts_settimeout_in'][systemLang])
              .appendField(new Blockly.FieldTextInput(1000), "DELAY")
              .appendField(new Blockly.FieldDropdown([
                  [Blockly.Words['timeouts_settimeout_ms'][systemLang], 'ms'],
                  [Blockly.Words['timeouts_settimeout_sec'][systemLang], 'sec'],
                  [Blockly.Words['timeouts_settimeout_min'][systemLang], 'min']
              ]), 'UNIT')
              .appendField(Blockly.Words['timeouts_settimeout_ms'][systemLang]);
  
          this.appendStatementInput('STATEMENT')
              .setCheck(null);
  
          this.setPreviousStatement(true, null);
          this.setNextStatement(true, null);
          this.setInputsInline(false);
          this.setColour(Blockly.Timeouts.HUE);
          this.setTooltip(Blockly.Words['timeouts_settimeout_tooltip'][systemLang]);
          this.setHelpUrl(getHelp('timeouts_settimeout_help'));
      },
      isTimeout_: true,
      getVars: function () {
          return [this.getFieldValue('NAME')];
      },
      getVarModels: function () {
          var name = this.getFieldValue('NAME');
          return [{getId: function () {return name}, name: name}];
      }
  };
  
  Blockly.JavaScript['timeouts_settimeout'] = function(block) {
      var delay = block.getFieldValue('DELAY');
      var name  = block.getFieldValue('NAME');
      var unit  = block.getFieldValue('UNIT');
      if (unit === 'min') {
          delay *= 60000;
      } else if (unit === 'sec') {
          delay *= 1000;
      }
      var statements_name = Blockly.JavaScript.statementToCode(block, 'STATEMENT');
      return name + ' = setTimeout(function () {\n' + statements_name + '}, ' + delay + ');\n';
  };
  
  // --- clearTimeout -----------------------------------------------------------
  Blockly.Timeouts.getAllTimeouts = function (workspace) {
      var blocks = workspace.getAllBlocks();
      var result = [];
  
      // Iterate through every block and check the name.
      for (var i = 0; i < blocks.length; i++) {
          if (blocks[i].isTimeout_) {
              result.push([blocks[i].getFieldValue('NAME'), blocks[i].getFieldValue('NAME')]);
          }
      }
      !result.length && result.push(['', '']);
  
      return result;
  };
  
  Blockly.Timeouts.blocks['timeouts_cleartimeout'] =
      '<block type="timeouts_cleartimeout">'
      + '     <value name="NAME">'
      + '     </value>'
      + '</block>';
  
  Blockly.Blocks['timeouts_cleartimeout'] = {
      init: function() {
          this.appendDummyInput('NAME')
              .appendField(Blockly.Words['timeouts_cleartimeout'][systemLang])
              .appendField(new Blockly.FieldDropdown(function () {
                  return scripts.blocklyWorkspace ? Blockly.Timeouts.getAllTimeouts(scripts.blocklyWorkspace) : [];
              }), 'NAME');
  
          this.setPreviousStatement(true, null);
          this.setNextStatement(true, null);
          this.setInputsInline(true);
          this.setColour(Blockly.Timeouts.HUE);
          this.setTooltip(Blockly.Words['timeouts_cleartimeout_tooltip'][systemLang]);
          this.setHelpUrl(getHelp('timeouts_cleartimeout_help'));
      }
  };
  
  Blockly.JavaScript['timeouts_cleartimeout'] = function(block) {
      var name = block.getFieldValue('NAME');
      return '(function () {if (' + name + ') {clearTimeout(' + name + '); ' + name + ' = null;}})();\n';
  };
  
  // --- setInterval -----------------------------------------------------------
  Blockly.Timeouts.blocks['timeouts_setinterval'] =
      '<block type="timeouts_setinterval">'
      + '     <value name="NAME">'
      + '     </value>'
      + '     <value name="INTERVAL">'
      + '     </value>'
      + '     <value name="UNIT">'
      + '     </value>'
      + '     <value name="STATEMENT">'
      + '     </value>'
      + '</block>';
  
  Blockly.Blocks['timeouts_setinterval'] = {
      init: function() {
          var nameField = new Blockly.FieldTextInput(
              Blockly.Timeouts.findLegalName(Blockly.Words['timeouts_setinterval_name'][systemLang], this),
              Blockly.Timeouts.rename);
  
          nameField.setSpellcheck(false);
  
          this.appendDummyInput()
              .appendField(Blockly.Words['timeouts_setinterval'][systemLang])
              .appendField(nameField, 'NAME')
              .appendField(Blockly.Words['timeouts_setinterval_in'][systemLang])
              .appendField(new Blockly.FieldTextInput(1000), "INTERVAL")
              .appendField(new Blockly.FieldDropdown([
                  [Blockly.Words['timeouts_settimeout_ms'][systemLang], 'ms'],
                  [Blockly.Words['timeouts_settimeout_sec'][systemLang], 'sec'],
                  [Blockly.Words['timeouts_settimeout_min'][systemLang], 'min']
              ]), 'UNIT')
              .appendField(Blockly.Words['timeouts_setinterval_ms'][systemLang]);
  
          this.appendStatementInput("STATEMENT")
              .setCheck(null);
  
          this.setPreviousStatement(true, null);
          this.setNextStatement(true, null);
          this.setInputsInline(false);
          this.setColour(Blockly.Timeouts.HUE);
          this.setTooltip(Blockly.Words['timeouts_setinterval_tooltip'][systemLang]);
          this.setHelpUrl(getHelp('timeouts_setinterval_help'));
      },
      isInterval_: true,
      getVars: function () {
          return [this.getFieldValue('NAME')];
      },
      getVarModels: function () {
          var name = this.getFieldValue('NAME');
          return [{getId: function () {return name}, name: name}];
      }
  };
  
  Blockly.JavaScript['timeouts_setinterval'] = function(block) {
      var delay = block.getFieldValue('INTERVAL');
      var name  = block.getFieldValue('NAME');
      var unit  = block.getFieldValue('UNIT');
      if (unit === 'min') {
          delay *= 60000;
      } else if (unit === 'sec') {
          delay *= 1000;
      }
  
      var statements_name = Blockly.JavaScript.statementToCode(block, 'STATEMENT');
      return name + ' = setInterval(function () {\n' + statements_name + '}, ' + delay + ');\n';
  };
  
  // --- clearInterval -----------------------------------------------------------
  Blockly.Timeouts.blocks['timeouts_clearinterval'] =
      '<block type="timeouts_clearinterval">'
      + '     <value name="NAME">'
      + '     </value>'
      + '</block>';
  
  Blockly.Timeouts.getAllIntervals = function (workspace) {
      var blocks = workspace.getAllBlocks();
      var result = [];
  
      // Iterate through every block and check the name.
      for (var i = 0; i < blocks.length; i++) {
          if (blocks[i].isInterval_) {
              result.push([blocks[i].getFieldValue('NAME'), blocks[i].getFieldValue('NAME')]);
          }
      }
  
      if (!result.length) result.push(['', '']);
  
      return result;
  };
  
  Blockly.Blocks['timeouts_clearinterval'] = {
      init: function() {
          this.appendDummyInput("NAME")
              .appendField(Blockly.Words['timeouts_clearinterval'][systemLang])
              .appendField(new Blockly.FieldDropdown(function () {
                  return scripts.blocklyWorkspace ? Blockly.Timeouts.getAllIntervals(scripts.blocklyWorkspace) : [];
              }), "NAME");
  
          this.setPreviousStatement(true, null);
          this.setNextStatement(true, null);
          this.setInputsInline(true);
          this.setColour(Blockly.Timeouts.HUE);
          this.setTooltip(Blockly.Words['timeouts_clearinterval_tooltip'][systemLang]);
          this.setHelpUrl(getHelp('timeouts_clearinterval_help'));
      }
  };
  
  Blockly.JavaScript['timeouts_clearinterval'] = function(block) {
      var name = block.getFieldValue('NAME');
      return '(function () {if (' + name + ') {clearInterval(' + name + '); ' + name + ' = null;}})();\n';
  };