<!-- src/components/RecursiveList.vue -->

<!-- Usage:
  Generates a recursive list of items in which child items may be toggled on and off via their parent.

  Input:
    - list (array of hashes MUST include keys for 'id, name, parent')
    - [onClick]  (function to run when individual item is clicked on)
    - [onSelect]  (function to run when individual item is selected using prepended icon eg ( x, +, -, check )
    - [saveOnChange] (function to call when list is updated / saved)
  Options:
    - nameKey (key for name value (defaults to 'name'))
    - idKey (key for name value (defaults to 'id'))
    - parentKey (key for name value (defaults to 'parent_id'))
    - clear (start with empty list of preselected values)
    - open (selected or all) - indicates which parent branches are initially open

    - selectOne (toggle items one at a time, closing list automatically upon selection)
    - selectable (enable users to toggle individual items on and off using checkbox)

    - selectText (text prompt to trigger individual select method)
    - showSecondarySelect (method to determine if selectText option is available for each record)

    - showSecondarySelect (optional method to enable dynamic visibility for selecting single items)
    -
    - onPickIcon (icon to use for individual item selection (defaults to edit icon))

    - init (initial open setting (all, selected, top [default]) indicates which blocks to start in open mode.
    - style options (?)

    - secondaryPick (clickable link beside currentList list item - only appears after selection is complete)
      - show (boolean or method returning computed boolean value)
      - selectText (text for clickable link for secondary pick execution)
      - onPick (method to execute upon selection for individual item)

*** Advanced options: ****

  Dynamically include trigger to open new modal for each item:  supply showSecondarySelect, onSelect, selectText
  - showSecondarySelect [boolean]
  - showSecondarySelect [function] - pass function which can return boolean depending on input item record
 -->
  <template lang='pug'>
    div.recursiveList
      div.right(v-if='toggle_formats')
        span(v-for='f in formats' style='padding-left: 50px')
          a( href='#' @click="setFormat(f)")
            b.text-xs(v-if='myFormat === f') {{f}}
            span.text-xs(v-else) {{f}}
        hr
      div(style='float: left')(v-else)
        a(v-if='myFormat==="List"' @click.prevent='setFormat("Tree")')
          v-btn.btn-primary(x-small icon)
            v-icon(small) edit
      span(v-show='title')
        u
          h4 {{title}} [{{selected_count}} / {{total_count}} selected]
        span(style='color:green; font-weight: bold' v-show='edits.turnedOn.length') Add: {{edits.turnedOnName.join(', ')}} &nbsp;
        span(style='color:red; font-weight: bold' v-show='edits.turnedOff.length') Remove: {{edits.turnedOffName.join(', ')}} &nbsp;

      span(v-if="newItems && promptNew")
        b.unknown {{newItems}}
          i New {{options.label}} option(s) available: &nbsp; &nbsp;
            span {{Object.keys(newItem).join(', ')}}

      div(v-if='myFormat==="List"')
        v-layout.justify-left
          ul(style='list-style-type: none; text-align: left')
            li(v-if='!currentList || !currentList.length') Nothing selected in list yet
            li(v-for='picked in currentList') {{picked.alias || picked.name}} &nbsp;
              span(v-if='options.annotations && options.annotations[picked.id]' style='color: black') &nbsp; &nbsp; {{options.annotations[picked.id]}}
 
      div(v-else-if='myFormat==="Tree"')
        v-layout.justify-left
          ul(style='list-style-type: none; text-align: left;')
            li(v-for="item in reOrderedList" v-show="openItems['id' + item.parent_id] === true" :style='spacing(item)')
              span(v-if="selectable")
                span(v-if='edits.turnedOn && (edits.turnedOn.indexOf(item[idKey]) >=0)')
                  a(href='#' @click.prevent="deselectMe(item)")
                    span &nbsp; &nbsp;
                    //- v-btn.btn-danger(x-small)
                      v-icon(name='minus' small) remove
                    v-icon(color='green' small) check_circle
                span(v-else-if='edits.turnedOff && (edits.turnedOff.indexOf(item[idKey]) >=0)')
                  a(href='#' @click.prevent="selectMe(item)")
                    span &nbsp; &nbsp;
                    //- v-btn.btn-danger(x-small)
                      v-icon(name='minus' small) remove
                    v-icon(color='green' small) cancel
                span(v-else-if='select[item[idKey]]')
                  a(href='#' @click.prevent="deselectMe(item)")
                    span &nbsp; &nbsp;
                    //- v-btn.btn-danger(x-small)
                      v-icon(name='minus' small) remove
                    v-icon(color='green' small) check
                span(v-else-if='newItem[nameKey]')
                  a(href='#' @click.prevent="selectMe(item)")
                    span &nbsp; &nbsp;
                    v-icon.midline(name='plus' color='orange' small) help
                span(v-else)
                  a(href='#' @click.prevent="selectMe(item)")
                    span &nbsp; &nbsp;
                    //- v-btn.btn-primary(x-small)
                      v-icon(name='plus' small) add
                    v-icon(color='red' small) close

                span(v-if='edits.turnedOn.indexOf(item[idKey]) >= 0')
                  a(href='#' v-if='onClick' @click.prevent="myClick(item)")
                    b(style='color:green') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                  b(v-else style='color:green') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                span(v-else-if='edits.turnedOff.indexOf(item[idKey]) >= 0')
                  a(href='#' v-if='onClick' @click.prevent="myClick(item)")
                    b(style='color:orange') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                  b(v-else style='color:orange') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                span(v-else-if='select[item[idKey]]')
                  a(href='#' v-if='onClick' @click.prevent="myClick(item)")
                    b(style='color:blue') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                  b(v-else style='color:blue') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                span(v-else-if='isAlive[item.id]')
                  a(href='#' v-if='onClick' @click.prevent="myClick(item)")
                    span(style='color:green') &nbsp; &nbsp; {{item[nameKey]}} &nbsp; &nbsp;
                  span(v-else style='color:green') &nbsp; &nbsp; {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                span(v-else-if='newItem[nameKey]')
                  a(href='#' v-if='onClick' @click.prevent='myClick(item)')
                    b(style='color:orange') {{item[nameKey]}} &nbsp; &nbsp;
                  b(style='color:orange' v-else) go {{item.alias || item[nameKey]}} &nbsp; &nbsp;
                span(v-else)
                  a(v-if='onClick' @click.prevent='myClick(item)')
                    span(style='color:red') {{item[nameKey]}} &nbsp; &nbsp;
                  span(v-else style='color:red') go {{item.alias || item[nameKey]}} &nbsp; &nbsp;

                span(v-if='options.annotations && options.annotations[item[idKey]]' style='color: black') &nbsp; &nbsp; {{options.annotations[item[idKey]]}}
              //- Additional (optional) clickable link following item 
              span(v-if='onSecondarySelect && showSecondarySelect')
                a(href='#' v-if='onSecondarySelect' @click.prevent='mySecondarySelect(item)')
                  b {{selectText}}
              span(v-else-if='showSecondarySelect')
                a(href='#' v-if='onSecondarySelect' @click.prevent='mySecondarySelect(item)')
                  b {{selectText}}
              span(v-else-if='!selectable')
                //- Read only
                b {{item[nameKey]}}

              span &nbsp;
              a(href='#' @click.prevent="toggleProgeny(item.id)"  v-if="openItems['id' + item.id] && under[item.id]")
                v-icon.midline(name='compress') zoom_out
              a(href='#' @click.prevent="toggleProgeny(item.id)"  v-if="openItems['id' + item.id] === false  && under[item.id]")
                span.expand-text (+ {{choseUnder[item.id].length}}/{{under[item.id].length}}) &nbsp;
                  v-icon.midline(name='expand') zoom_in
        p &nbsp;
      v-row.justify-left
        v-btn.btn.btn-primary(v-if='saveOnChange && (edits.turnedOn.length || edits.turnedOff.length)' @click.prevent='saveChanges()' small) Save {{edits.turnedOn.length + edits.turnedOff.length}} Changes:
        v-btn.btn.btn-primary(v-else-if='myFormat==="Tree"' @click.prevent='setFormat("List")' x-small icon)
          v-icon() expand_less

      v-row.justify-space-around(v-if='!onChange')
        b.added(v-if='edits.turnedOn.length' style="color: green") Added: {{edits.turnedOnName.join(', ')}}
        b.removed(v-if='edits.turnedOff.length' style="color: orange") Removed: {{edits.turnedOffName.join(', ')}}

      v-row(v-if='debug')
        h6 L: {{list}}
        h6 S: {{select}}
      v-dialog(v-model='showListItem')
        v-card 
          v-card-text
            h3 Dialog: here
            slot
  </template>
<script>
import {Roarr as logger} from 'roarr'

export default {
  components: {
  },
  data () {
    return {
      formats: ['List', 'Tree'],
      myFormat: 'List',
      showListItem: false,

      openItems: {'idnull': true, 'id0': true},
      hash: {},
      seeds: [],
      under: {},
      choseUnder: {},
      rList: [],
      refIndex: {},
      parent: {},
      isAlive: {},
      reOrderedList: [],
      name2id: {},
      id2name: {},
      select: {},
      selectParent: {},
      depth: {},
      newItem: {},
      newItems: 0,
      currentList: ['nothing selected'],
      hideTree: false,
      selected_count: 0,
      total_count: 0,
      edits: {
        turnedOn: [],
        turnedOff: [],
        turnedOnName: [],
        turnedOffName: []
      },
      record: {},
      cascadeOff: true,
      blank: 'default/images/blank.png',
      selected: [],
      ids: []
    }
  },
  props: {
    title: { type: String },
    list: { type: Array },
    // selected: {
    //   type: Array,
    //   default () { return null }
    // },
    preset: {
      type: Array,
      default () { return null }
    },
    onClick: {
      type: Function,
      default () { return null }
    },
    onChange: {
      type: Function,
      default () { return null }
    },
    onSelect: {
      type: Function,
      default () { return null }
    },
    onDeSelect: {
      type: Function,
      default () { return null }
    },
    onSecondarySelect: {
      type: Function,
      default () { return null }
    },
    options: {
      type: Object,
      default () { return {} }
    }, // idKey, parentKey, open (defaults to: id, parent_id, false)
    secondaryPick: {
      type: Object
    },
    // Supply function to save revised list
    saveOnChange: {
      type: Function
    },
    // ... or supply separate methods for adding & removing items
    onAdd: {
      type: Function
    },
    onRemove: {
      type: Function
    },
    mode: {
      type: String
    },
    format: {
      type: String
    },
    toggle_formats: {
      type: Boolean,
      default: false
    },
    section: {
      type: String
    },
    debug: {
      type: Boolean,
      default: false
    },
    remoteSelect: {
      type: Object, // object remotely selected ({id: 123, name: 'abc'})
    },
    remoteDeSelect: {
      type: Object, // hash of functions
    },
    saved: {
      type: Number
    }
    // annotations: {
    //   type: Object
    // }
  },
  watch: {
    mode: function () {
      if (this.mode === 'List') {
        this.hideTree = true
      } else {
        this.hideTree = false
      }
    },
    list: function () {
      logger.debug('rebuilding meta data after list change')
      this.buildMetaData()
      if (this.hideTree) {
        this.initList()
      }
    },
    preset: function () {
      logger.debug('rebuilding meta data after selected change')
      this.initList()

      this.buildMetaData()
      // if (this.hideTree) {
      //   this.initList()
      // }
    },
    format: function () {
      this.setFormat(this.format)
    },
    remoteSelect: function () {
      if (this.remoteSelect && (this.ids.indexOf(this.remoteSelect.id) >=0)) {
        logger.debug('remote select ' + JSON.stringify(this.remoteSelect) + ' from ' + this.ids.join(', '))
        this.selectMe(this.remoteSelect) // true refers to remote flag (avoids redundant triggering of save)
      } else {
        logger.debug('ignore remote select ' + JSON.stringify(this.remoteDeSelect) + ' not in ' + this.ids.join(', '))
      }
    },
    remoteDeSelect: function () {
      if (this.remoteDeSelect && (this.ids.indexOf(this.remoteDeSelect.id) >=0)) {
        logger.debug('remote de-select ' + JSON.stringify(this.remoteDeSelect) + ' from ' + this.ids.join(', '))
        this.deselectMe(this.remoteDeSelect) // true refers to remote flag (avoids redundant triggering of save) 
      } else {
        logger.debug('ignore remote de-select ' + JSON.stringify(this.remoteDeSelect) + ' not in ' + this.ids.join(', '))
      }
    },
    saved: function () {
      this.resetEdits()
    }
  },
  created: function () {
  },
  mounted: function () {
    if (this.mode === 'List') {
      this.hideTree = true
    } else if (this.mode === 'Edit') {
      this.hideTree = false
    }
    this.buildMetaData()
    this.initList()

    if (this.format) {
      this.setFormat(this.format)
    }
  },
  computed: {
    autoupdate: function () {
      if (this.saveOnChange) {
        return true
      } else {
        return false
      }
    },
    hide: function () {
      if (this.options && this.options.hide) {
        return true
      } else {
        return false
      }
    },
    isSelected: function () {
      return this.select
    },
    toDepth: function () {
      if (this.open || this.options.open) {
        if (this.open.constructor === Number) {
          return this.open
        } else if (typeof parseInt(this.open) !== 'undefined') {
          return parseInt(this.open)
        } else {
          return null
        }
      } else {
        return null
      }
    },
    clear: function () {
      if (this.options && this.options.clear) {
        return true
      } else {
        return false
      }
    },
    editable: function () {
      if (this.options && this.options.editable) {
        return true
      } else {
        return false
      }
    },
    selectText: function () {
      if (this.options && this.options.selectText) {
        return this.options.selectText || 'toggle'
      } else { return 'choose' }
    },
    promptNew: function () {
      if (this.options && this.options.promptNew) {
        return true
      } else {
        return false
      }
    },
    nameKey: function () {
      if (this.options && this.options.nameKey) {
        return this.options.nameKey
      } else {
        return 'name'
      }
    },
    idKey: function () {
      if (this.options && this.options.idKey) {
        return this.options.idKey
      } else {
        return 'id'
      }
    },
    orderedList: function () {
      return null // fix this
    },
    showSecondarySelect: function () {
      if (this.options && this.options.showSecondarySelect) {
        return this.options.showSecondarySelect
      } else {
        return false
      }
    },
    selectOne: function () {
      if (this.options && this.options.selectOne) {
        return true
      } else {
        return false
      }
    },
    selectable: function () {
      if (this.options) {
        // Support legacy options 
        if (this.options.selectable || this.options.selectable) {
          return true
        } else {
          return false
        }
      } else {
        return false
      }
    },
    open: function () {
      if (this.options && this.options.open) {
        return this.options.open
      } else if (this.options.closed) {
        return 0
      } else {
        return 1
      }
    }
  },
  methods: {
    async mySelect (item) {
      logger.debug('select item : ' +  JSON.stringify(item))

      if (this.onSelect) {
        this.onSelect(item)
      } else if (this.options.onSelect) {
        this.options.onSelect(item)
      }

      await this.saveChanges(item, 'add')

    },
    async myDeSelect (item) {
      logger.debug('deselect item : ' + JSON.stringify(item))

      if (this.onDeSelect) {
        this.onDeSelect(item)
      } else if (this.options.onDeSelect) {
        this.options.onDeSelect(item)
      }

      await this.saveChanges(item, 'remove')
    },
    mySecondarySelect: function () {
      logger.log('secondary select here')
      if (this.onSecondarySelect) {
        return this.onSecondarySelect
      } else if (this.options.onSecondarySelect) {
        return this.options.onSecondarySelect
      } else {
        return null
      }
    },
    isOpen: function (interest) {
      var key = interest.parent_id

      var isOpen = this.openItems[key]
      return isOpen
    },
    spacing: function (item) {
      var padding = 0
      if (item && item.indents) {
        padding = item.indents * 2
      }
      return 'color: blue; padding-left: ' + padding + 'rem;'
    },
    setFormat: function (format) {
      this.myFormat = format
      if (format === 'List') {
        this.hideList = true
      }
    },
    buildMetaData: function (chosen) {
      this.selected_count = 0
      this.total_count = 0
      this.$set(this, 'select', {})
      this.$set(this, 'selectParent', {})
      this.$set(this, 'depth', {})
      this.$set(this, 'under', {})
      this.$set(this, 'choseUnder', {})
      this.$set(this, 'rList', [])
      this.$set(this, 'reOrderedList', [])

      if (this.preset && this.preset.length) {
        // Enables passing more complex information for specific data 
        if (this.preset[0].constructor === Number) {
          logger.log('parse from preset Number (?)')
          this.selected = JSON.parse(JSON.stringify(this.preset))
        } else if (this.preset[0].constructor === Object) {
          // parse id from object
          this.selected = this.preset.map(a => a.id)
        }
      }
      var list = this.list || []
      var seeds = []
      for (var i = 0; i < list.length; i++) {
        var nameKey = this.options.nameKey || this.nameKey || 'name'
        var idKey = this.options.idKey || this.idKey || 'id'
        var parentKey = this.options.parentKey || 'parent_id'
        this.total_count++

        var name = list[i][nameKey]
        var id = list[i][idKey]
        var parent = list[i][parentKey] || ''
        if (parent && typeof this.depth[parent] === 'undefined') {
          this.$set(this.depth, id, 0)
        } else if (parent) {
          this.$set(this.depth, id, this.depth[parent] + 1)
        }

        this.refIndex[id] = i

        this.$set(this.hash, id, list[i])
        if (!parent) {
          seeds.push(id)
        }

        var selected
        if (chosen) {
          selected = chosen.indexOf(name) >= 0
        } else if (this.selected) {
          selected = (this.selected.indexOf(id) >= 0 || list[i].selected) && !this.clear
        } else {
          selected = list[i].selected && !this.clear
        }

        this.$set(this.parent, id, parent)

        if (selected) {
          this.selected_count++
          this.$set(this.select, id, true)
          
          if (parent) {
            this.$set(this.selectParent, parent, true)
          
            this.makeAlive(parent)

            if (this.open === 'selected') {
              this.$set(this.openItems, 'id' + parent, true)
            }
          }
        } else if (selected === false) {
          this.$set(this.select, id, false)
        } else if (this.promptNew) {
          var pname = this.id2name[parent]
          if (!this.newItem[pname]) {
            this.$set(this.newItem, name, true)
            if (this.selectable) { this.newItems++ }
          }
        }

        if (this.open === 'all') {
          this.$set(this.openItems, 'id' + parent, true)
        } else if (this.toDepth) {
          if (this.depth[id] <= this.toDepth) {
            this.$set(this.openItems, 'id' + parent, true)
          } else {
            this.$set(this.openItems, 'id' + parent, false)
          }
        }

        this.$set(this.name2id, name, id)
        this.$set(this.id2name, id, name)

        if (this.list[i].parent_id) {
          if (!this.under[parent.toString()]) {
            this.$set(this.under, parent.toString(), [])
            this.$set(this.choseUnder, parent.toString(), [])
            // this.$set(this.openItems, 'id' + parent, false)
          }

          var currentLength = this.under[parent.toString()].length
          this.$set(this.under[parent.toString()], currentLength, id)
  
          if (this.select[id]) {
            var selectedLength = this.choseUnder[parent.toString()].length
            this.$set(this.choseUnder[parent.toString()], selectedLength, id)
          }
        } else {
          // this.$set(this.list[i], 'parent_id', 0)
        }
      }
      this.seeds = seeds
      // open parent of selected interests (if applicable)
      if (this.options && this.options.open === 'selected') {
        var PS = Object.keys(this.selectParent)
        for (var j = 0; j < PS.length; j++) {
          var pid = PS[j]
          this.$set(this.openItems, 'id' + pid, true)
        }
      }

      logger.debug('metadata parsed...')
      logger.debug('select: ' + JSON.stringify(this.select))
      for (var k = 0; k < seeds.length; k++) {
        this.addRecursive(seeds[k], 0)
      }
    },
    makeAlive: function (id) {
      // logger.log('make alive ' + id)
      this.isAlive[id] = true
      var parent = this.parent[id]
      if (parent) {
        this.makeAlive(parent)
      }
    },
    unmakeAlive: function (id) {
      this.isAlive[id] = false
      var parent = this.parent[id]

      if (parent) {
        var siblings = this.under[parent]
        var stillAlive = false

        for (var i = 0; i < siblings.length; i++) {
          if (this.isAlive[siblings[i]]) {
            stillAlive = true
            i = siblings.length
          }
        }
        if (this.isAlive[parent] && ! stillAlive) {
          this.unmakeAlive(parent)
        }
      }
    },
    clearList: function () {
      this.currentList = []
    },
    reopenTree: function () {

    },
    deselectMe: function (item) {
      logger.debug('deselect ' + JSON.stringify(item.name))
      if (this.autoupdate) {
        this.$set(this.select, item.id, false)
      }
      this.currentList = this.currentList.filter(a => (a.id !== item.id))
      this.trackOff(item.id)

      this.resetChoseUnder(item.parent_id)
      if (! this.choseUnder[item.id] || ! this.choseUnder[item.id].length) {
        this.toggleProgeny(item.id)
        this.unmakeAlive(item.id)
      }
      this.myDeSelect(item)
    },
    resetChoseUnder: function (id) {
      var underlist = this.under[id]
      if (underlist && underlist.length) {
        var selected = []
        for (var i = 0; i < underlist.length; i++) {
          if (this.select[underlist[i]]) {
            selected.push(underlist[i])
          }
        }
        this.$set(this.choseUnder, id, selected)
      } else {
        this.$set(this.choseUnder, id, [])
      }
    },
    selectMe: function (item) {
      logger.debug('select: ' + JSON.stringify(item.name))
      // logger.debug('SelectMe: ' + JSON.stringify(item.name))
      if (this.autoupdate) {
        this.$set(this.select, item.id, true)
      }
      this.currentList.push(item)
      this.trackOn(item.id)

      this.$set(this.newItem, item.name, false)
      this.$set(this.openItems, 'id' + item.id, true)

      var parent = item.parent_id ? item.parent_id.toString() : null
      if (parent && this.choseUnder[parent]) {
        this.$set(this.choseUnder[parent], this.choseUnder[parent].length, item.id)
      } else if (parent) {
        this.choseUnder[parent] = [item.id] 
      }
      this.mySelect(item)
      this.makeAlive(item.id)
    },
    toggleProgeny: function (id) {
      var key = 'id' + id
      // logger.debug('Toggle ' + id)
      this.$set(this.openItems, key, !this.openItems[key])

      if (!this.openItems[key]) {
        // close children
        var under = this.under[id] || []
        for (var i = 0; i < under.length; i++) {
          var underid = under[i]
          if (this.openItems['id' + underid]) {
            this.toggleProgeny(underid)
          }
        }
      }
    },
    myClick: function (record) {
      var key = 'id' + record.id
      this.$set(this.openItems, key, true)
      logger.debug('current list:' + JSON.stringify(this.reOrderedList))

      logger.debug('click on ' + JSON.stringify(record))
      if (this.onClick) {
        this.onClick(record)
      } else {
        logger.debug('no onClick option supplied to RecursiveList')
      }
    },
    toggle: function (item) {
      logger.log('Toggle: ' + item)
      var id = null
      if (item.constructor === Object) {
        id = item.id
      } else {
        id = item
        item = null
      }
      var ids = 'id' + id
      var name = this.id2name[id]
      var action

      if (id && name) {
        // first clear sub interests if applicable ..
        if (this.select[id]) {
          this.currentList = this.currentList.filter(a => (a.id !== id))
          logger.debug('DEselect ' + id)
          // deselect
          if (this.cascadeOff) {
            this.toggleOff(this.under[id])
          }
          if (this.autoupdate) {
            this.$set(this.select, id, false)
          }
          this.trackOff(id)
          // this.$set(this.openItems, ids, false)
          // this.toggleProgeny(id)
          action = 'remove'
        } else {
          this.currentList.push(item)
          logger.debug('select id: ' + id)
          // select
          if (this.autoupdate) {
            this.$set(this.select, id, true)
          }
          this.trackOn(id)
          this.$set(this.openItems, ids, true)
          action = 'add'
        }

        // this.$set(this.select, id, !this.select[id])
        if (this.selectOne) {
          logger.debug('save the list... ')
          this.saveChanges(item, action)
        }
      } else {
        logger.debug('error trying to toggle ' + id + ':' + name)
      }
    },
    toggleOff: function (ids) {
      // Turn off single item - or array of items
      if (ids && ids.constructor === Number) {
        ids = [ids]
      }

      if (ids && ids.length) {
        for (var i = 0; i < ids.length; i++) {
          var id = ids[i]
          if (this.select[id]) {
            if (this.cascadeOff) {
              this.toggleOff(this.under[id])
            }
                      
            if (this.autoupdate) {
              this.$set(this.select, id, false)
            }
            this.trackOff(id)
          }
        }
      }
    },
    trackOff: function (id) {
      // track changes when item is turned off
      var onIndex = this.edits.turnedOn.indexOf(id)
      var offIndex = this.edits.turnedOff.indexOf(id)
      if (onIndex >= 0) {
        this.edits.turnedOn.splice(onIndex, 1)
        this.edits.turnedOnName.splice(onIndex, 1)
      } else if (offIndex < 0) {
        this.$set(this.edits.turnedOff, this.edits.turnedOff.length, id)
        this.$set(this.edits.turnedOffName, this.edits.turnedOffName.length, this.id2name[id])
      } else {
        logger.log('already off (?)')
      }
    },
    trackOn: function (id) {
      // track changes when item is turned on
      var onIndex = this.edits.turnedOn.indexOf(id)
      var offIndex = this.edits.turnedOff.indexOf(id)
      if (offIndex >= 0) {
        this.edits.turnedOff.splice(offIndex, 1)
        this.edits.turnedOffName.splice(offIndex, 1)
      } else if (onIndex < 0) {
        this.$set(this.edits.turnedOn, this.edits.turnedOn.length, id)
        this.$set(this.edits.turnedOnName, this.edits.turnedOnName.length, this.id2name[id])
      } else {
        logger.log('already on (?)')
      }
    },
    addRecursive: function (id, level) {
      var refIndex = this.refIndex[id] // tmp
      if (id >= 0) {
        var ids = id.toString()

        var index = this.reOrderedList.length || 0

        if (this.rList.indexOf(ids) === -1) {
          this.$set(this.rList, this.rList.length, ids)
          var item = this.list[refIndex]
          item.indents = level
          this.$set(this.reOrderedList, index++, item)
        }

        if (this.under[ids]) {
          for (var i = 0; i < this.under[ids].length; i++) {
            var newid = this.under[ids][i]
            this.addRecursive(newid, level + 1)
          }
        }
      }
    },
    initList: function () {
      this.ids = this.list.map(a => a.id)
      if (this.preset) { 
        this.currentList = this.preset.filter(a => (this.ids.indexOf(a.id) >= 0))
      }
      logger.log('preset: ' + JSON.stringify(this.preset))
      logger.log('init: ' + JSON.stringify(this.currentList))
    },
    saveChanges: function (item, action) {
      this.hideTree = true
      logger.debug('on Save - ' + action + ' : ' + JSON.stringify(item))
      if (this.saveOnChange) {
        this.saveOnChange(item, action)
      }

      if (this.autoupdate) {
        if (this.onRemove) {
          this.onRemove(this.edits.turnedOff)
        }
        if (this.onAdd) {
          this.onAdd(this.edits.turnedOn)
        }

        this.resetEdits()
        logger.debug('auto-updated & cleared edit hash')
      } else {
        this.selected_count += this.edits.turnedOn.length
        this.selected_count -= this.edits.turnedOff.length
        logger.debug('update edits: ' + JSON.stringify(this.edits))
      }
      
      if (this.onChange) {
        this.onChange(this.currentList, this.edits, this.section)
      }

      if (this.selectOne) {
        this.setFormat("List")
      }
      return Promise.resolve()
    },
    resetEdits: function () {
      this.$set(this.edits, 'turnedOn', [])
      this.$set(this.edits, 'turnedOff', [])
      this.$set(this.edits, 'turnedOnName', [])
      this.$set(this.edits, 'turnedOffName', [])
    },
    showTree: function () {
      this.hideTree = false
    },
    editList: function () {
      logger.debug('Selected: ' + JSON.stringify(this.selected))
      this.hideTree = false
    }
  }
}
</script>

<style>
.btn {
  padding: 1rem;
}

.flex-container {
  flex-direction: row;
  width: 100%;
}
.flex-item {
  padding: 5px;
  width: 10%;
}

.selected {
  color:blue;
}
.unselected {
  color:grey;
}
.unknown {
  color:orange;
}
.newItem {
  color:red;
}
.recursiveList ul li {
    display: block;
}
/* .expand-text { */
  /* color:darkorange */
/* } */

.inline {
  display: inline-block;
}
.added {
  color:green
}
.removed {
  color:lightred
}
.selected {
  color:green
}
.deselected {
  color:lightred
}
</style>
