//                  Solid-UI preferences
//                  =====================
//

import * as $rdf from 'rdflib' // pull in first avoid cross-refs
import { store } from 'solid-logic'
import * as debug from './debug'
import { ensureLoadedPreferences } from './login/login'
import * as ns from './ns'
import * as participation from './participation' // @ts-ignore
import * as widgets from './widgets'

const kb = store

// This was tabulator . preferences in the tabulator
//  Is this functionality used anywhere?
//

// used for storing user name
// @@ Deprocate these functions.  They were used for
// communication around the tabulator functionality about the user session

export const value = []
export function get (k) {
  return value[k]
}

export function set (k, v) {
  if (typeof v !== 'string') {
    debug.log('Non-string value of preference ' + k + ': ' + v)
    throw new Error('Non-string value of preference ' + k + ': ' + v)
  }
  this.value[k] = v
}

// In a solid world, Preferences are stored in the web
//
// Make an RDF node for recording the common view preferences for any object
// (maybe make it in a separate file?)
export function recordSharedPreferences (subject, context) {
  return new Promise(function (resolve, reject) {
    const sharedPreferences = kb.any(subject, ns.ui('sharedPreferences'))
    if (!sharedPreferences) {
      if (!kb.updater.editable(subject.doc())) {
        debug.log(` Cant make shared preferences, may not change ${subject.doc}`)
        resolve(context)
      }
      const sp = $rdf.sym(subject.doc().uri + '#SharedPreferences')
      const ins = [
        $rdf.st(subject, ns.ui('sharedPreferences'), sp, subject.doc())
      ]
      debug.log('Creating shared preferences ' + sp)
      kb.updater.update([], ins, function (uri, ok, errorMessage) {
        if (!ok) {
          reject(new Error('Error creating shared prefs: ' + errorMessage))
        } else {
          context.sharedPreferences = sp
          resolve(context)
        }
      })
    } else {
      context.sharedPreferences = sharedPreferences
      resolve(context)
    }
  })
}

// Construct a personal defaults node in the preferences file for a given class of object
//
export function recordPersonalDefaults (theClass, context) {
  return new Promise(function (resolve, reject) {
    ensureLoadedPreferences(context).then(
      context => {
        if (!context.preferencesFile) {
          debug.log(
            'Not doing private class preferences as no access to preferences file. ' +
              context.preferencesFileError
          )
          return
        }
        const regs = kb.each(
          null,
          ns.solid('forClass'),
          theClass,
          context.preferencesFile
        )
        let ins = []
        let prefs
        let reg
        if (regs.length) {
          // Use existing node if we can
          regs.forEach(r => {
            prefs = prefs || kb.any(r, ns.solid('personalDefaults'))
          })
          if (prefs) {
            context.personalDefaults = prefs // Found one
            resolve(context)
            return
          } else {
            prefs = widgets.newThing(context.preferencesFile)
            reg = regs[0]
          }
        } else {
          // no regs fo class
          reg = widgets.newThing(context.preferencesFile)
          ins = [
            $rdf.st(
              reg,
              ns.rdf('type'),
              ns.solid('TypeRegistration'),
              context.preferencesFile
            ),
            $rdf.st(reg, ns.solid('forClass'), theClass, context.preferencesFile)
          ]
        }
        prefs = widgets.newThing(context.preferencesFile)
        ins.push(
          $rdf.st(
            reg,
            ns.solid('personalDefaults'),
            prefs,
            context.preferencesFile
          )
        )
        kb.updater.update([], ins, function (uri, ok, errm) {
          if (!ok) {
            reject(new Error('Setting preferences for ' + theClass + ': ' + errm))
          } else {
            context.personalDefaults = prefs
            resolve(context)
          }
        })
      },
      err => {
        reject(err)
      }
    )
  })
}

export function renderPreferencesForm (subject, theClass, preferencesForm, context) {
  const prefContainer = context.dom.createElement('div')
  participation.participationObject(subject, subject.doc(), context.me).then(
    participation => {
      const dom = context.dom
      function heading (text) {
        prefContainer.appendChild(dom.createElement('h5')).textContent = text
      }
      heading('My view of this ' + context.noun)
      widgets.appendForm(
        dom,
        prefContainer,
        {},
        participation,
        preferencesForm,
        subject.doc(),
        (ok, mes) => {
          if (!ok) widgets.complain(context, mes)
        }
      )

      heading("Everyone's  view of this " + context.noun)
      recordSharedPreferences(subject, context).then(context => {
        const sharedPreferences = context.sharedPreferences
        widgets.appendForm(
          dom,
          prefContainer,
          {},
          sharedPreferences,
          preferencesForm,
          subject.doc(),
          (ok, mes) => {
            if (!ok) widgets.complain(context, mes)
          }
        )

        heading('My default view of any ' + context.noun)
        recordPersonalDefaults(theClass, context).then(
          context => {
            widgets.appendForm(
              dom,
              prefContainer,
              {},
              context.personalDefaults,
              preferencesForm,
              context.preferencesFile,
              (ok, mes) => {
                if (!ok) widgets.complain(context, mes)
              }
            )
          },
          err => {
            widgets.complain(context, err)
          }
        )
      })
    },
    err => {
      // parp object fails
      prefContainer.appendChild(widgets.errorMessageBlock(context.dom, err))
    }
  )
  return prefContainer
}

// This should be part of rdflib.js ad part of the RDFJS Standard!!

function toJS (term) {
  if (!term.datatype) return term // Objects remain objects
  if (term.datatype.equals(ns.xsd('boolean'))) {
    return term.value === '1'
  }
  if (
    term.datatype.equals(ns.xsd('dateTime')) ||
    term.datatype.equals(ns.xsd('date'))
  ) {
    return new Date(term.value)
  }
  if (
    term.datatype.equals(ns.xsd('integer')) ||
    term.datatype.equals(ns.xsd('float')) ||
    term.datatype.equals(ns.xsd('decimal'))
  ) {
    return Number(term.value)
  }
  return term.value
}

// This is the function which acuakly reads and combines the preferences
//
//  @@ make it much more tolerant of missing buts of prefernces
export function getPreferencesForClass (subject, theClass, predicates, context) {
  return new Promise(function (resolve, reject) {
    recordSharedPreferences(subject, context).then(context => {
      const sharedPreferences = context.sharedPreferences
      if (context.me) {
        participation
          .participationObject(subject, subject.doc(), context.me)
          .then(participation => {
            recordPersonalDefaults(theClass, context).then(context => {
              const results = []
              const personalDefaults = context.personalDefaults
              predicates.forEach(pred => {
                // Order of preference: My settings on object, Global settings on object, my settings on class
                const v1 =
                  kb.any(participation, pred) ||
                  kb.any(sharedPreferences, pred) ||
                  kb.any(personalDefaults, pred)
                if (v1) {
                  results[pred.uri] = toJS(v1)
                }
              })
              resolve(results)
            }, reject)
          }, reject)
      } else {
        // no user defined, just use common prefs
        const results = []
        predicates.forEach(pred => {
          const v1 = kb.any(sharedPreferences, pred)
          if (v1) {
            results[pred.uri] = toJS(v1)
          }
        })
        resolve(results)
      }
    })
  })
}

// ends
