import Blockly from 'blockly'
import { FieldDatePicker } from './fields/date-field'
import { addDateSupport, addDateComparisonSupport, addGetZdtComponent, addChrono } from './utils'

export default function (f7) {
  /*
  * 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
  */
  Blockly.JavaScript['oh_dayoffset_today'] = function (block) {
    let code = '0'
    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_dayoffset'] = function (block) {
    let offsetValue = Blockly.JavaScript.valueToCode(block, 'offset', Blockly.JavaScript.ORDER_ATOMIC)
    let code = `${offsetValue}`
    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_zdt_now'] = function (block) {
    let [dtf, zdt, gzdt, czdt] = addDateSupport()
    let code = `${zdt}.now()`
    return [code, Blockly.JavaScript.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 positve or negative offset that can be used with the Ephemeris check block
  * Code part
  */
  Blockly.JavaScript['oh_zdt_plusminus'] = function (block) {
    let [dtf, zdt, getZonedDatetime] = addDateSupport()
    let offsetValue = Blockly.JavaScript.valueToCode(block, 'offset', Blockly.JavaScript.ORDER_ATOMIC)
    let plusMinus = block.getFieldValue('plusminus')
    let period = block.getFieldValue('period')
    let code = `${zdt}.now().${plusMinus}${period}(${offsetValue})`
    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_zdt_create'] = function (block) {
    let [dtf, zdt, gzdt, czdt] = addDateSupport()

    let year = Blockly.JavaScript.valueToCode(block, 'year', Blockly.JavaScript.ORDER_ATOMIC)
    let month = Blockly.JavaScript.valueToCode(block, 'month', Blockly.JavaScript.ORDER_ATOMIC)
    let day = Blockly.JavaScript.valueToCode(block, 'day', Blockly.JavaScript.ORDER_ATOMIC)
    let hour = Blockly.JavaScript.valueToCode(block, 'hour', Blockly.JavaScript.ORDER_ATOMIC)
    let minute = Blockly.JavaScript.valueToCode(block, 'minute', Blockly.JavaScript.ORDER_ATOMIC)
    let second = Blockly.JavaScript.valueToCode(block, 'second', Blockly.JavaScript.ORDER_ATOMIC)

    let code = `${czdt}(${year}, ${month} ,${day}, ${hour}, ${minute}, ${second}, 0, ${zdt}.now().getOffset().getId(), ${zdt}.now().getZone().getId())`
    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_zdt'] = function (block) {
    let [dtf, zdt, getZonedDateTime] = addDateSupport()
    let day = block.getFieldValue('day')
    let code = `${getZonedDateTime}('${day}')`
    return [code, Blockly.JavaScript.ORDER_NONE]
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Allows input as string in the format 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_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' +
        '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
  */
  Blockly.JavaScript['oh_zdt_fromText'] = function (block) {
    let [dtf, zdt, gzdt, czdt] = addDateSupport()
    let day = Blockly.JavaScript.valueToCode(block, 'day', Blockly.JavaScript.ORDER_ATOMIC)
    let code = `${gzdt}(${day})`
    return [code, Blockly.JavaScript.ORDER_NONE]
  }

  /*
  * Typed (ZonedDateTime) block that can be used with the Ephemeris check block
  * Allows input as string in the format 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')
      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
  */
  Blockly.JavaScript['oh_zdt_fromItem'] = function (block) {
    let [dtf, zdt, gzdt, czdt] = addDateSupport()
    let itemName = Blockly.JavaScript.valueToCode(block, 'itemName', Blockly.JavaScript.ORDER_ATOMIC)
    let code = `itemRegistry.getItem(${itemName}).getState().getZonedDateTime()`
    return [code, Blockly.JavaScript.ORDER_NONE]
  }

  const nextImage =
      'data:image/png;base64, '

  /*
  * 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
  */
  Blockly.JavaScript['oh_zdt_temporal_unit'] = function (block) {
    let value = block.getFieldValue('value')
    let code = `(${value})`
    return [code, Blockly.JavaScript.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'],
    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 (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')
      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
  */
  Blockly.JavaScript['oh_zdt_temporal_unit_input'] = function (block) {
    let value1 = Blockly.JavaScript.valueToCode(block, 'value', Blockly.JavaScript.ORDER_ATOMIC)
    let value2 = block.getFieldValue('value')
    let code = `(${value1})`
    return [code, Blockly.JavaScript.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.Mutator(['oh_zdt_amend_item']))
      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.Mutator.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']
      }
  }

  Blockly.JavaScript['oh_zdt_amend'] = function (block) {
    let [dtf, zdt, gzdt, czdt] = addDateSupport()
    let chrono = addChrono()
    let baseZdt = Blockly.JavaScript.valueToCode(block, 'baseZdt', Blockly.JavaScript.ORDER_ATOMIC)
    let 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 = Blockly.JavaScript.valueToCode(block, 'ADD' + i, Blockly.JavaScript.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, Blockly.JavaScript.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 () {
      this.appendValueInput('date')
        .appendField('text of')
        .setCheck('ZonedDateTime')
      this.appendDummyInput()
        .appendField(new Blockly.FieldDropdown([['without time', 'without'], ['with time', 'with'], ['as OH-Time', 'asOHTime']]), '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')
    }
  }

  /*
  * Returns a string representation of an ephemeris date
  * Code part
  */
  Blockly.JavaScript['oh_zdt_toText'] = function (block) {
    let [dtf, zdt, gzdt, czdt] = addDateSupport()
    let date = Blockly.JavaScript.valueToCode(block, 'date', Blockly.JavaScript.ORDER_ATOMIC)
    let withtime = block.getFieldValue('withtime')

    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 {
      code = `${date}.format(${dtf}.ofPattern('yyyy-MM-dd\\'T\\'HH:mm:ss.SSSZ'))`
    }

    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_zdt_compare'] = function (block) {
    let zdtCompare = addDateComparisonSupport()
    let zdtOne = Blockly.JavaScript.valueToCode(block, 'zdtOne', Blockly.JavaScript.ORDER_ATOMIC)
    let zdtTwo = Blockly.JavaScript.valueToCode(block, 'zdtTwo', Blockly.JavaScript.ORDER_ATOMIC)
    let operation = block.getFieldValue('operation')
    let precision = block.getFieldValue('precision')
    let dateComparison = block.getFieldValue('dateComparison')

    let code = `${zdtCompare}(${zdtOne}, ${zdtTwo}, '${operation}', '${precision}', '${dateComparison}')`

    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_zdt_between'] = function (block) {
    let zdtCompare = addDateComparisonSupport()
    let zdtOne = Blockly.JavaScript.valueToCode(block, 'zdtOne', Blockly.JavaScript.ORDER_ATOMIC)
    let zdtTwo = Blockly.JavaScript.valueToCode(block, 'zdtTwo', Blockly.JavaScript.ORDER_ATOMIC)
    let zdtThree = Blockly.JavaScript.valueToCode(block, 'zdtThree', Blockly.JavaScript.ORDER_ATOMIC)
    let dateComparison = block.getFieldValue('dateComparison')

    let codeLow = `${zdtCompare}(${zdtTwo}, ${zdtOne}, 'beforeEqual', 'nanos', '${dateComparison}')`
    let codeHigh = `${zdtCompare}(${zdtOne}, ${zdtThree}, 'beforeEqual', 'nanos', '${dateComparison}')`
    let code = `(${codeLow} && ${codeHigh})`
    return [code, Blockly.JavaScript.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('date')
        .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 zoned date time as a Number
  * Code part
  */
  Blockly.JavaScript['oh_get_zdt_part'] = function (block) {
    let getZdtComponent = addGetZdtComponent()
    let chrono = addChrono()
    let temporalPart = block.getFieldValue('temporalPart')
    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 date = Blockly.JavaScript.valueToCode(block, 'date', Blockly.JavaScript.ORDER_ATOMIC)
    let code = `${getZdtComponent}(${date}.${temporalPart})`
    return [code, Blockly.JavaScript.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
  */
  Blockly.JavaScript['oh_get_time_between'] = function (block) {
    let chronoUnit = Blockly.JavaScript.provideFunction_(
      'chronoUnit',
      ['var ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = Java.type("java.time.temporal.ChronoUnit");'])
    let temporalPart = block.getFieldValue('temporalPart')
    let divisor = ''

    let zdtOne = Blockly.JavaScript.valueToCode(block, 'zdtOne', Blockly.JavaScript.ORDER_ATOMIC)
    let zdtTwo = Blockly.JavaScript.valueToCode(block, 'zdtTwo', Blockly.JavaScript.ORDER_ATOMIC)
    let code = `${chronoUnit}.${temporalPart}.between(${zdtOne},${zdtTwo})${divisor}`
    return [code, Blockly.JavaScript.ORDER_NONE]
  }
}
