/**
 * supports jsscripting
 */
import Blockly from 'blockly'
import { javascriptGenerator } from 'blockly/javascript.js'
import { FieldDatePicker } from './fields/date-field.js'
import { addDateSupport, addDateComparisonSupportNashorn, addDateComparisonSupportGraalVM, addGetZdtComponent, addChrono } from './utils.js'

export default function (f7, isGraalJs) {
  /*
  * Typed (DayOffset) block that can be used with the Ephemeris check block
  * Note that the block basically returns a zero day offset for the check
  * Blockly part
  */
  Blockly.Blocks['oh_dayoffset_today'] = {
    init: function () {
      this.appendDummyInput()
        .appendField('today')
      this.setOutput(true, 'DayOffset')
      this.setColour(70)
      this.setTooltip('today\'s date for Ephemeris check block')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#today')
    }
  }

  /*
  * Typed block that can be used with Ephemeris check block
  * Code part
  */
  javascriptGenerator.forBlock['oh_dayoffset_today'] = function (block) {
    let code = '0'
    return [code, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Typed (DayOffset) block with a day positve or negative offset that can be used
  * Blockly part
  */
  Blockly.Blocks['oh_dayoffset'] = {
    init: function () {
      this.appendValueInput('offset')
        .setCheck('Number')
        .appendField('today +/-')
      this.appendDummyInput()
        .appendField('days')
      this.setOutput(true, 'DayOffset')
      this.setColour(70)
      this.setTooltip('today with a positive or negative day offset for Ephemeris check block')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#today-x-days')
    }
  }

  /*
  * Typed (DayOffset) block with a day positve or negative offset
  * Code part
  */
  javascriptGenerator.forBlock['oh_dayoffset'] = function (block) {
    let offsetValue = javascriptGenerator.valueToCode(block, 'offset', javascriptGenerator.ORDER_ATOMIC)
    let code = `${offsetValue}`
    return [code, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Typed (ZonedDateTime) block that returns the current date and time
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_now'] = {
    init: function () {
      this.appendDummyInput()
        .appendField('now')
      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      this.setTooltip('Obtains the current date-time from the system clock in the default time-zone.\nReturns: ZonedDateTime')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#get-datetime-now')
    }
  }

  /*
  * Typed (DayOffset) block with a day positve or negative offset that can be used with the Ephemeris check block
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_now'] = function (block) {
    const zdt = (isGraalJs) ? 'time.ZonedDateTime' : addDateSupport()[1]
    return [`${zdt}.now()`, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Typed (ZonedDateTime) block that adds or substracts a specified amount from the current time
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_plusminus'] = {
    init: function () {
      this.appendValueInput('offset')
        .setCheck('Number')
        .appendField('now')
        .appendField(new Blockly.FieldDropdown([
          ['+', 'plus'], ['-', 'minus']]), 'plusminus')
      this.appendDummyInput()
        .appendField(new Blockly.FieldDropdown([
          ['seconds', 'Seconds'], ['minutes', 'Minutes'],
          ['hours', 'Hours'], ['days', 'Days'], ['weeks', 'Weeks'],
          ['months', 'Months'], ['years', 'Years']
        ]), 'period')
      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      this.setTooltip('Obtains the current date-time from the system clock in the default time-zone with a simple positive or negative offset.\nReturns: ZonedDateTime')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#get-date-now-with-offset')
    }
  }

  /*
  * Typed (DayOffset) block with a day positive or negative offset that can be used with the Ephemeris check block
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_plusminus'] = function (block) {
    const offsetValue = javascriptGenerator.valueToCode(block, 'offset', javascriptGenerator.ORDER_ATOMIC)
    const plusMinus = block.getFieldValue('plusminus')
    const period = block.getFieldValue('period')

    const zdt = (isGraalJs) ? 'time.ZonedDateTime' : addDateSupport()[1]
    return [`${zdt}.now().${plusMinus}${period}(${offsetValue})`, javascriptGenerator.ORDER_ATOMIC]
  }

  /*
  * Create Typed (ZonedDateTime) block by providing date and time information
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_create'] = {
    init: function () {
      this.updateShape_()
      this.setInputsInline(true)
      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      this.setTooltip('Creates a ZonedDateTime based on the given input for date and time with nanos set to 0 and the system\'s time-zone. The values need to be valid.\nReturns: ZonedDateTime')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#datetime-with-date-and-time-values')
    },
    updateShape_: function () {
      let year = this.appendValueInput('year')
        .setCheck('Number')
        .appendField('datetime with date')
      let month = this.appendValueInput('month')
        .setCheck('Number')
        .appendField('-')
      let day = this.appendValueInput('day')
        .setCheck('Number')
        .appendField('-')
      let hour = this.appendValueInput('hour')
        .setCheck('Number')
        .appendField('and time')
      let minute = this.appendValueInput('minute')
        .setCheck('Number')
        .appendField(':')
      let second = this.appendValueInput('second')
        .setCheck('Number')
        .appendField(':')
    }
  }

  /*
  * ZonedDateTime block with preset date and time
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_create'] = function (block) {
    const year = javascriptGenerator.valueToCode(block, 'year', javascriptGenerator.ORDER_ATOMIC)
    const month = javascriptGenerator.valueToCode(block, 'month', javascriptGenerator.ORDER_ATOMIC)
    const day = javascriptGenerator.valueToCode(block, 'day', javascriptGenerator.ORDER_ATOMIC)
    const hour = javascriptGenerator.valueToCode(block, 'hour', javascriptGenerator.ORDER_ATOMIC)
    const minute = javascriptGenerator.valueToCode(block, 'minute', javascriptGenerator.ORDER_ATOMIC)
    const second = javascriptGenerator.valueToCode(block, 'second', javascriptGenerator.ORDER_ATOMIC)

    if (isGraalJs) {
      const code = `time.ZonedDateTime.now().withYear(${year}).withMonth(${month}).withDayOfMonth(${day}).withHour(${hour}).withMinute(${minute}).withSecond(${second}).withNano(0)`
      return [code, javascriptGenerator.ORDER_ATOMIC]
    } else {
      let [dtf, zdt, gzdt, czdt] = addDateSupport()
      let stringToParse = `${czdt}(${year}, ${month} ,${day}, ${hour}, ${minute}, ${second}, 0, ${zdt}.now().getOffset().getId(), ${zdt}.now().getZone().getId())`
      let code = `${zdt}.parse(${stringToParse}, ${dtf}.ISO_ZONED_DATE_TIME)`
      return [code, javascriptGenerator.ORDER_ATOMIC]
    }
  }

  /*
  * ZonedDateTime block
  * Allows the selection of a date. The default is the current date
  * Blockly part
  */
  Blockly.Blocks['oh_zdt'] = {
    init: function () {
      this.appendDummyInput()
        .appendField('datetime')
        .appendField(new FieldDatePicker('', null, { f7 }, 'date'), 'day')
      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      this.setTooltip('A ZonedDateTime with time set to zero')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#date-picker')
    }
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt'] = function (block) {
    const day = block.getFieldValue('day')
    const getZonedDateTime = (isGraalJs) ? 'time.toZDT' : addDateSupport()[2]
    return [`${getZonedDateTime}('${day}')`, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Allows input as string in many different patterns e.g. yyyy-MM-dd or yyyy-MM-dd HH:mm:ss or yyyy-MM-dd HH:mm:ss +HH:mm
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_fromText'] = {
    init: function () {
      this.appendValueInput('day') // cannot be renamed for backward compatibility reasons
        .appendField('datetime')
        .setCheck('String')
      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      let tooltip = 'Parses a text into a ZonedDateTime supporting the following formats detected:\n' +
        'HH:mm\n' +
        'HH:mm:ss\n' +
        'yyyy-MM-dd\n' +
        'yyyy-MM-ddTHH:mm\n' +
        'yyyy-MM-ddTHH:mm:ss\n' +
        'yyyy-MM-ddTHH:mm:ss+02:00\n' +
        'yyyy-MM-ddTHH:mm:ss.SSS\n' +
        'yyyy-MM-ddTHH:mm:ss.SSSSS\n' +
        'yyyy-MM-ddTHH:mm:ss.SSS+02:00\n' +
        'yyyy-MM-ddTHH:mm:ss.SSSSSS+02:00 -> local date time standard output\n' +
        'yyyy-MM-ddTHH:mm:ss.SS+0200 -> OH standard output format\n' +
        'Note: all of the above work with or without the "T" (replaced by a blank) in the given string"\n' +
        'returns ZonedDateTime"\n'

      this.setTooltip(tooltip)
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#get-date')
    }
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_fromText'] = function (block) {
    const day = javascriptGenerator.valueToCode(block, 'day', javascriptGenerator.ORDER_ATOMIC)
    const getZonedDateTime = (isGraalJs) ? 'time.toZDT' : addDateSupport()[2]
    return [`${getZonedDateTime}(${day})`, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Allows input as string in the format yyyy-MM-dd or yyyy-MM-dd HH:mm:ss or yyyy-MM-dd HH:mm:ss +HH:mm
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_fromItem'] = {
    init: function () {
      this.appendValueInput('itemName')
        .appendField('datetime from item')
        .setCheck(['String', 'oh_item'])
      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      this.setTooltip('ZonedDateTime from a datetime item')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#datetime-from-item')
    }
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_fromItem'] = function (block) {
    const itemName = javascriptGenerator.valueToCode(block, 'itemName', javascriptGenerator.ORDER_ATOMIC)
    if (isGraalJs) {
      return [`time.toZDT(items.getItem(${itemName}))`, javascriptGenerator.ORDER_NONE]
    } else {
      return [`itemRegistry.getItem(${itemName}).getState().getZonedDateTime()`, javascriptGenerator.ORDER_NONE]
    }
  }

  const nextImage =
      'data:image/png;base64,[encoded data, 8808 characters]'

  /*
  * A temporal unit that can be used to amend a ZonedDateTime
  * Block part
  */
  Blockly.Blocks['oh_zdt_temporal_unit'] = {
    currentBlockType: 0,
    blockTypes: ['year', 'month', 'day', 'hour', 'minute', 'second', 'milli', 'micro', 'nano', 'day of year'],
    maxValue: [9999, 12, 31, 23, 59, 59, 999, 999, 999, 366],
    init: function () {
      this.appendDummyInput()
        .appendField(new Blockly.FieldImage(nextImage, 15, 15, undefined, this.onClick), 'NEXT')
        .appendField(new Blockly.FieldNumber(0), 'value')
        .appendField('day', 'blockType')
      this.setInputsInline(true)
      this.setOutput(true, 'zdtTemporalUnit')
      this.setColour(120)
      this.setTooltip('click arrow to scroll through year, month, day, hour, minute, second, milli, nano, day of year')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#create-datetime-based-on-a-specific-date-copy-of')
    },
    onClick (nextField) {
      let block = this.getSourceBlock()
      let currentBlockType = block.currentBlockType
      let blockTypes = block.blockTypes
      if (currentBlockType < blockTypes.length - 1) {
        currentBlockType++
      } else {
        currentBlockType = 0
      }
      block.currentBlockType = currentBlockType
      block.setFieldValue(blockTypes[currentBlockType], 'blockType')
      block.getField('value').setMax(block.maxValue[currentBlockType])
    },
    updateType_: function (blockType) {
      this.currentBlockType = blockType
      this.setFieldValue(this.blockTypes[blockType], 'blockType')
    },
    mutationToDom: function () {
      let container = Blockly.utils.xml.createElement('mutation')
      container.setAttribute('blockType', this.currentBlockType)
      return container
    },
    domToMutation: function (xmlElement) {
      this.updateType_(xmlElement.getAttribute('blockType'))
    }
  }

  /*
  * A temporal unit that can be used to amend a ZonedDateTime
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_temporal_unit'] = function (block) {
    const value = block.getFieldValue('value')
    return [`(${value})`, javascriptGenerator.ORDER_NONE]
  }

  /*
  * A temporal unit that can be used to amend a ZonedDateTime
  * Block part
  */
  Blockly.Blocks['oh_zdt_temporal_unit_input'] = {
    numberBlockInput: null,
    currentBlockType: 0,
    blockTypes: ['year', 'month', 'day', 'hour', 'minute', 'second', 'milli', 'micro', 'nano', 'day of year'],
    // TODO: Set min values
    // minValue: [0, 1, 1, 0, 0, 0, 0, 0, 0, 1],
    maxValue: [9999, 12, 31, 23, 59, 59, 999, 999, 999, 366],
    addNumberBlock: function () {
      if (this.workspace.id !== Blockly.getMainWorkspace().id) {
        let parentConnection = this.getInput('value').connection
        let mathNumberBlock = this.workspace.newBlock('math_number')
        mathNumberBlock.initSvg()
        mathNumberBlock.render()
        parentConnection.connect(mathNumberBlock.outputConnection)
      }
    },
    init: function () {
      this.appendDummyInput()
        .appendField(new Blockly.FieldImage(nextImage, 15, 15, undefined, this.onClick), 'NEXT')
      this.appendValueInput('value')
      this.appendDummyInput()
        .appendField('day', 'blockType')

      this.addNumberBlock()
      this.setInputsInline(true)
      this.setOutput(true, 'zdtTemporalUnit')
      this.setColour(120)
      this.setTooltip('click arrow to scroll through year, month, day, hour, minute, second, milli, nano, day of year')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#create-datetime-based-on-a-specific-date-copy-of')
    },
    onClick () {
      let block = this.getSourceBlock()
      let currentBlockType = block.currentBlockType
      let blockTypes = block.blockTypes
      if (currentBlockType < blockTypes.length - 1) {
        currentBlockType++
      } else {
        currentBlockType = 0
      }
      block.currentBlockType = currentBlockType
      block.setFieldValue(blockTypes[currentBlockType], 'blockType')
      let numberBlockInput = block.getInput('value').connection
      let numberField = numberBlockInput.targetBlock().getField('NUM')
      // if it is still a math_number, then set the max value
      if (numberField) {
        numberField.setMax(block.maxValue[currentBlockType])
      }
    },
    updateType_: function (blockType) {
      this.currentBlockType = blockType
      this.setFieldValue(this.blockTypes[blockType], 'blockType')
    },
    mutationToDom: function () {
      let container = Blockly.utils.xml.createElement('mutation')
      container.setAttribute('blockType', this.currentBlockType)
      return container
    },
    domToMutation: function (xmlElement) {
      this.updateType_(xmlElement.getAttribute('blockType'))
    }
  }

  /*
  * A temporal unit that can be used to amend a ZonedDateTime
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_temporal_unit_input'] = function (block) {
    const value = javascriptGenerator.valueToCode(block, 'value', javascriptGenerator.ORDER_ATOMIC)
    return [`(${value})`, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Use a ZonedDateTime as a basis to be amended by a temporal unit
  *
  * Three different operations are supported
  * - set (overwrites the particular temporal unit)
  * - add (adds a value to the temporal unit)
  * - subtract (subtracts a value to the temporal unit)
  *
  * Temporal units
  * - temporal units can be added / removed in a flexible way via a dialog which allows the block to the needs of the user
  * - Note that milliseconds are supported indirectly via nanos
  *
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_amend'] = {
    init: function () {
      this.itemCount_ = 0
      this.updateShape_()

      this.setOutput(true, 'ZonedDateTime')
      this.setColour(70)
      this.setInputsInline(true)

      this.setMutator(new Blockly.icons.MutatorIcon(['oh_zdt_amend_item'], this))
      this.setTooltip('Use a ZonedDateTime as a basis and amend it via particular temporal blocks')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#create-datetime-based-on-a-specific-date-copy-of')
    },
    mutationToDom: function () {
      let container = Blockly.utils.xml.createElement('mutation')
      container.setAttribute('items', this.itemCount_)
      return container
    },
    domToMutation: function (xmlElement) {
      this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10)
      this.updateShape_()
    },
    decompose: function (workspace) {
      let containerBlock = workspace.newBlock('oh_zdt_amend_container')
      containerBlock.initSvg()
      let connection = containerBlock.getInput('STACK').connection
      for (let i = 0; i < this.itemCount_; i++) {
        let itemBlock = workspace.newBlock('oh_zdt_amend_item')
        itemBlock.initSvg()
        connection.connect(itemBlock.previousConnection)
        connection = itemBlock.nextConnection
      }
      return containerBlock
    },
    compose: function (containerBlock) {
      let itemBlock = containerBlock.getInputTargetBlock('STACK')
      // Count number of inputs.
      let connections = []
      while (itemBlock && !itemBlock.isInsertionMarker()) {
        connections.push(itemBlock.valueConnection_)
        itemBlock = itemBlock.nextConnection &&
          itemBlock.nextConnection.targetBlock()
      }
      // Disconnect any children that don't belong.
      for (let i = 0; i < this.itemCount_; i++) {
        let input = this.getInput('ADD' + i)
        if (!input) continue
        let connection = input.connection.targetConnection
        if (connection && connections.indexOf(connection) === -1) {
          connection.disconnect()
        }
      }
      this.itemCount_ = connections.length
      this.updateShape_()
      // Reconnect any child blocks.
      for (let i = 0; i < this.itemCount_; i++) {
        Blockly.icons.MutatorIcon.reconnect(connections[i], this, 'ADD' + i)
      }
    },
    saveConnections: function (containerBlock) {
      let itemBlock = containerBlock.getInputTargetBlock('STACK')
      let i = 0
      while (itemBlock) {
        let input = this.getInput('ADD' + i)
        itemBlock.valueConnection_ = input && input.connection.targetConnection
        i++
        itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock()
      }
    },
    updateShape_: function () {
      if (this.itemCount_ && this.getInput('EMPTY')) {
        this.removeInput('EMPTY')
      } else if (!this.itemCount_ && !this.getInput('EMPTY')) {
        if (!this.getInput('baseZdt')) {
          this.appendValueInput('baseZdt')
            .setCheck('ZonedDateTime')
            .appendField('copy of')
        }
        this.appendDummyInput('EMPTY')
          .appendField('add temporal units')
      }
      // Add new inputs.
      let i
      for (i = 0; i < this.itemCount_; i++) {
        if (!this.getInput('ADD' + i)) {
          let input = this.appendValueInput('ADD' + i)
            .setAlign(Blockly.ALIGN_RIGHT)
            .setCheck('zdtTemporalUnit')
          if (i === 0) {
            input.appendField(new Blockly.FieldDropdown([['set', 'with'], ['add', 'plus'], ['subtract', 'minus']]), 'operation')
          }
        }
      }
      // Remove deleted inputs.
      while (this.getInput('ADD' + i)) {
        this.removeInput('ADD' + i)
        i++
      }
    },
    blockTypesMethod:
      {
        'with': ['Year', 'Month', 'DayOfMonth', 'Hour', 'Minute', 'Second', 'Nano', 'Nano', 'Nano', 'DayOfYear'],
        'plus': ['Years', 'Months', 'Days', 'Hours', 'Minutes', 'Seconds', 'Nanos', 'Nanos', 'Nanos', 'undefined'],
        'minus': ['Years', 'Months', 'Days', 'Hours', 'Minutes', 'Seconds', 'Nanos', 'Nanos', 'Nanos', 'undefined']
      }
  }

  javascriptGenerator.forBlock['oh_zdt_amend'] = function (block) {
    const baseZdt = javascriptGenerator.valueToCode(block, 'baseZdt', javascriptGenerator.ORDER_ATOMIC)
    const operation = block.getFieldValue('operation')

    let code = baseZdt
    let millis = 0
    let micros = 0
    let nanos = 0

    let operationBlock = block.getInput('ADD0')
    if (operationBlock) {
      let i
      for (i = 0; i < this.itemCount_; i++) {
        let temporal = javascriptGenerator.valueToCode(block, 'ADD' + i, javascriptGenerator.ORDER_ATOMIC)
        temporal = temporal.replace(/\(/g, '').replace(/\)/g, '')

        let inputBlock = this.getInputTargetBlock('ADD' + i)
        if (inputBlock) {
          let blockType = inputBlock.currentBlockType

          let operationUnit = this.blockTypesMethod[operation][blockType]
          if (operationUnit !== 'undefined') {
            switch (parseInt(blockType)) {
              case 6:
                millis = parseInt(temporal)
                break
              case 7:
                micros = parseInt(temporal)
                break
              case 8:
                nanos = parseInt(temporal)
                break
              default:
                code += `.${operation}${operationUnit}(${temporal})`
            }
          }
        }
      }
      let totalNanos = 1000000 * millis + 1000 * micros + nanos
      if (totalNanos > 0) {
        let operationUnit = this.blockTypesMethod[operation][8]
        code += `.${operation}${operationUnit}(${totalNanos})`
      }
    }
    return [code, javascriptGenerator.ORDER_ATOMIC]
  }

  Blockly.Blocks['oh_zdt_amend_container'] = {
    init: function () {
      this.setColour(70)
      this.appendDummyInput()
        .appendField('amend datetime by a')
      this.appendStatementInput('STACK')
      this.setTooltip('a group of temporal units')
      this.contextMenu = false
    }
  }

  Blockly.Blocks['oh_zdt_amend_item'] = {
    init: function () {
      this.setColour(120)
      this.appendDummyInput()
        .appendField('temporal unit')
      this.setPreviousStatement(true)
      this.setNextStatement(true)
      this.setTooltip('a temporal unit')
      this.contextMenu = false
    }
  }

  /*
  * Returns a string representation of an ephemeris date
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_toText'] = {
    init: function () {
      const block = this
      this.appendValueInput('date')
        .appendField('text of')
        .setCheck('ZonedDateTime')

      const dropDown = new Blockly.FieldDropdown(
        [['without time', 'without'], ['with time', 'with'], ['as OH-Time', 'asOHTime'], ['with pattern', 'withPattern']],
        function (newMode) {
          block._updateType(newMode)
        })
      this.appendDummyInput()
        .appendField(dropDown, 'withtime')

      this.setOutput(true, 'String')
      this.setColour(160)
      this.setTooltip('converts an ZonedDateTime into a date string')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#get-string-representation-of-date')
    },
    _updateType: function (type) {
      if (type === 'withPattern') {
        if (this.getInput('pattern')) return
        this.appendValueInput('pattern')
          .setCheck('String')
          .setShadowDom(Blockly.utils.xml.textToDom('<shadow type="text"><field name="TEXT">yyyy-MM-dd</field></shadow>'))
      } else if (this.getInput('pattern')) {
        this.removeInput('pattern')
      }
    }
  }

  /*
  * Returns a string representation of an ephemeris date
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_toText'] = function (block) {
    const date = javascriptGenerator.valueToCode(block, 'date', javascriptGenerator.ORDER_ATOMIC)
    const withtime = block.getFieldValue('withtime')
    const dtf = (isGraalJs) ? 'time.DateTimeFormatter' : addDateSupport()[0]

    let code = ''
    if (withtime === 'with') {
      code = `${date}.format(${dtf}.ofPattern('yyyy-MM-dd HH:mm:ss'))`
    } else if (withtime === 'without') {
      code = `${date}.format(${dtf}.ofPattern('yyyy-MM-dd'))`
    } else if (withtime === 'withPattern') {
      const pattern = javascriptGenerator.valueToCode(block, 'pattern', javascriptGenerator.ORDER_ATOMIC)
      code = `${date}.format(${dtf}.ofPattern(${pattern}))`
    } else {
      code = `${date}.format(${dtf}.ofPattern('yyyy-MM-dd\\'T\\'HH:mm:ss.SSSZ'))`
    }

    return [code, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Returns a string representation of an ephemeris date
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_compare'] = {
    init: function () {
      this.appendValueInput('zdtOne')
        .setCheck('ZonedDateTime')
        .appendField('is')
      this.appendDummyInput()
        .appendField(new Blockly.FieldDropdown([['before', 'before'], ['equal to', 'equal'], ['after', 'after'], ['before or equal', 'beforeEqual'], ['after or equal', 'afterEqual']]), 'operation')
      this.appendValueInput('zdtTwo')
        .setCheck('ZonedDateTime')
      this.appendDummyInput()
        .appendField('use')
        .appendField(new Blockly.FieldDropdown([['date and time', 'dateandtime'], ['date', 'date'], ['time', 'time']]), 'dateComparison')
      this.appendDummyInput()
        .appendField('with resolution of')
        .appendField(new Blockly.FieldDropdown([['years', 'years'], ['months', 'months'], ['days', 'days'], ['hours', 'hours'], ['minutes', 'minutes'], ['seconds', 'seconds'], ['nanos', 'nanos']]), 'precision')

      this.setOutput(true, 'Boolean')
      this.setInputsInline(true)
      this.setColour('%{BKY_LOGIC_HUE}')
      this.setTooltip('Compares two ZonedDateTimes with each other')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#date-comparison')
    }
  }

  /*
  * Returns a string representation of an ephemeris date
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_compare'] = function (block) {
    const zdtOne = javascriptGenerator.valueToCode(block, 'zdtOne', javascriptGenerator.ORDER_ATOMIC)
    const zdtTwo = javascriptGenerator.valueToCode(block, 'zdtTwo', javascriptGenerator.ORDER_ATOMIC)
    const operation = block.getFieldValue('operation')
    const precision = block.getFieldValue('precision')
    const dateComparison = block.getFieldValue('dateComparison')

    const zdtCompare = (isGraalJs) ? addDateComparisonSupportGraalVM() : addDateComparisonSupportNashorn()
    return [`${zdtCompare}(${zdtOne}, ${zdtTwo}, '${operation}', '${precision}', '${dateComparison}')`, javascriptGenerator.ORDER_NONE]
  }

  /*
  * Returns a string representation of an ephemeris date
  * Blockly part
  */
  Blockly.Blocks['oh_zdt_between'] = {
    init: function () {
      this.appendValueInput('zdtOne')
        .setCheck('ZonedDateTime')
        .appendField('is')
      this.appendValueInput('zdtTwo')
        .appendField('between')
        .setCheck('ZonedDateTime')
      this.appendValueInput('zdtThree')
        .appendField('and')
        .setCheck('ZonedDateTime')
      this.appendDummyInput()
        .appendField('use')
        .appendField(new Blockly.FieldDropdown([['date and time', 'dateandtime'], ['date', 'date'], ['time', 'time']]), 'dateComparison')

      this.setOutput(true, 'Boolean')
      this.setInputsInline(true)
      this.setColour('%{BKY_LOGIC_HUE}')
      this.setTooltip('Compares two ZonedDateTimes with each other')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#date-comparison-between')
    }
  }

  /*
  * Returns a string representation of an ephemeris date
  * Code part
  */
  javascriptGenerator.forBlock['oh_zdt_between'] = function (block) {
    let zdtCompare = (isGraalJs) ? addDateComparisonSupportGraalVM() : addDateComparisonSupportNashorn()
    let zdtOne = javascriptGenerator.valueToCode(block, 'zdtOne', javascriptGenerator.ORDER_ATOMIC)
    let zdtTwo = javascriptGenerator.valueToCode(block, 'zdtTwo', javascriptGenerator.ORDER_ATOMIC)
    let zdtThree = javascriptGenerator.valueToCode(block, 'zdtThree', javascriptGenerator.ORDER_ATOMIC)
    let dateComparison = block.getFieldValue('dateComparison')

    if (isGraalJs) {
      const op = new Map([['dateandtime', 'isBetweenDateTimes'], ['date', 'isBetweenDates'], ['time', 'isBetweenTimes']]).get(dateComparison)
      return [`${zdtOne}.${op}(${zdtTwo}, ${zdtThree})`, javascriptGenerator.ORDER_NONE]
    } else {
      let codeLow = `${zdtCompare}(${zdtTwo}, ${zdtOne}, 'beforeEqual', 'nanos', '${dateComparison}')`
      let codeHigh = `${zdtCompare}(${zdtOne}, ${zdtThree}, 'beforeEqual', 'nanos', '${dateComparison}')`
      let code = `(${codeLow} && ${codeHigh})`
      return [code, javascriptGenerator.ORDER_NONE]
    }
  }

  /*
  * Returns a temporal part of a zoned date time  as a Number
  * Blockly part
  */
  Blockly.Blocks['oh_get_zdt_part'] = {
    init: function () {
      this.appendDummyInput()
        .appendField(new Blockly.FieldDropdown([['year', 'getYear'], ['month', 'getMonthValue'], ['day of month', 'getDayOfMonth'], ['day of week', 'getDayOfWeek'], ['day of year', 'getDayOfYear'], ['hour', 'getHour'], ['minute', 'getMinute'], ['second', 'getSecond'], ['milli', 'getMilli'], ['micro', 'getMicro'], ['nano', 'getNano']]), 'temporalPart')
        .appendField('of')
      this.appendValueInput('zdt')
        .setCheck('ZonedDateTime')
      this.setInputsInline(true)
      this.setOutput(true, 'Number')
      this.setColour(230)
      this.setTooltip('Returns the selected field as Number.')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#get-part-of-datetime')
    }
  }

  /*
  * Returns a temporal part of a ZonedDateTime as a Number
  * Code part
  */
  javascriptGenerator.forBlock['oh_get_zdt_part'] = function (block) {
    const zdt = javascriptGenerator.valueToCode(block, 'zdt', javascriptGenerator.ORDER_ATOMIC)
    let temporalPart = block.getFieldValue('temporalPart')

    if (isGraalJs) {
      const op = new Map([['getYear', 'year'], ['getMonthValue', 'monthValue'], ['getDayOfMonth', 'dayOfMonth'], ['getDayOfWeek', 'dayOfWeek().value'], ['getDayOfYear', 'dayOfYear'], ['getHour', 'hour'], ['getMinute', 'minute'], ['getSecond', 'second'], ['getNano', 'nano']])
      switch (temporalPart) {
        case 'getMilli':
          temporalPart = 'getLong(time.ChronoField.MILLI_OF_SECOND)'
          break
        case 'getMicro':
          temporalPart = 'getLong(time.ChronoField.MICRO_OF_SECOND) % 1000'
          break
        default:
          temporalPart = op.get(temporalPart) + '()'
      }
      return [`${zdt}.${temporalPart}`, javascriptGenerator.ORDER_NONE]
    } else {
      const getZdtComponent = addGetZdtComponent()
      const chrono = addChrono()
      switch (temporalPart) {
        case 'getMilli':
          temporalPart = `getLong(${chrono}.MILLI_OF_SECOND)`
          break
        case 'getMicro':
          temporalPart = `getLong(${chrono}.MICRO_OF_SECOND) % 1000`
          break
        case 'getNano':
          temporalPart = `getLong(${chrono}.NANO_OF_SECOND) % 1000`
          break
        default:
          temporalPart += '()'
      }

      let code = `${getZdtComponent}(${zdt}.${temporalPart})`
      return [code, javascriptGenerator.ORDER_NONE]
    }
  }

  /*
  * Computes the number of temporal unit between two zonedDateTimes
  * Blockly part
  */
  Blockly.Blocks['oh_get_time_between'] = {
    init: function () {
      this.appendDummyInput()
        .appendField(new Blockly.FieldDropdown([['years', 'YEARS'], ['months', 'MONTHS'], ['weeks', 'WEEKS'], ['days', 'DAYS'], ['hours', 'HOURS'], ['minutes', 'MINUTES'], ['seconds', 'SECONDS'], ['millis', 'MILLIS'], ['micros', 'MICROS'], ['nanos', 'NANOS']]), 'temporalPart')
        .appendField('between')
      this.appendValueInput('zdtOne')
        .setCheck('ZonedDateTime')
      this.appendValueInput('zdtTwo')
        .setCheck('ZonedDateTime')
        .appendField('and')
      this.setInputsInline(true)
      this.setOutput(true, 'Number')
      this.setColour(230)
      this.setTooltip('Returns the selected field as Number.')
      this.setHelpUrl('https://www.openhab.org/docs/configuration/blockly/rules-blockly-date-handling.html#get-difference-between-datetimes')
    }
  }

  /*
  * Computes the number of temporal units between two zonedDateTimes
  * Code part
  */
  javascriptGenerator.forBlock['oh_get_time_between'] = function (block) {
    const temporalPart = block.getFieldValue('temporalPart')
    const zdtOne = javascriptGenerator.valueToCode(block, 'zdtOne', javascriptGenerator.ORDER_ATOMIC)
    const zdtTwo = javascriptGenerator.valueToCode(block, 'zdtTwo', javascriptGenerator.ORDER_ATOMIC)

    const chronoUnit = (isGraalJs) ? 'time.ChronoUnit' : javascriptGenerator.provideFunction_(
      'chronoUnit',
      ['var ' + javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("java.time.temporal.ChronoUnit");'])

    let code = `${chronoUnit}.${temporalPart}.between(${zdtOne},${zdtTwo})`
    return [code, javascriptGenerator.ORDER_NONE]
  }
}
