// AmyTron4000
// Gerenator for text

// Main object
var amytron = {}

// Discrete distribution
// The distribution object defines a discrete distribution
amytron.Distribution = function () {
  this.total = 0 // Total number of elements in the distribution
  this.items = new Map() // Map of item -> number of elements
}
// Add 'occur' number of 'elem' elements into the distribution
amytron.Distribution.prototype.addTo = function (elem, occur) {
  this.total += occur
  if (this.items.has(elem)) {
    this.items.set(elem, occur + this.items.get(elem))
  } else {
    this.items.set(elem, occur)
  }
}
// Pick a random element from the distribution
amytron.Distribution.prototype.pickFrom = function () {
  var idx = Math.floor(Math.random() * this.total)
  var acc = 0
  var pick = undefined
  for (var [elem, occur] of this.items)
  {
    acc += occur
    pick = elem
    if (acc > idx) {
      break
    }
  }
  return pick
}

// Rule generator
// This generator makes an array of elements from rules describing how to generate the array
// Each rule is associated to a distribution of patterns indicating how to replace a rule identifier
amytron.RuleGenerator = function () {
  this.rules = new Map()
}
// Populate the rules table from a description
amytron.RuleGenerator.prototype.initializeFromDescription = function (description) {
  for (var rule of description) {
    var dist = new amytron.Distribution()
    for (var pat of rule.distribution) {
      dist.addTo(pat.pattern, pat.occurences)
    }
    this.rules.set(rule.id, dist)
  }
}
// Generate an array of elements
amytron.RuleGenerator.prototype.generate = function () {
  var replacePattern = function (rules, input) {
    var output = new Array()
    for (var i of input) {
      if (rules.has(i)) {
        output = output.concat(replacePattern(rules, rules.get(i).pickFrom()))
      } else {
        output.push(i)
      }
    }
    return output
  }
  return replacePattern(this.rules, this.rules.get('@output@').pickFrom())
}

// Model of the amytron generator
amytron.model = {}
amytron.model.generators = new Map()

// Generate text from the generator id
amytron.generateText = function () {
  var selector = document.getElementById('generator-selector')
  var generatorId = selector.options[selector.selectedIndex].value
  var generator = amytron.model.generators.get(generatorId)
  document.getElementById('generated-text').innerHTML = generator.generate()
}

// Transform a list of strings to a formatted strings
amytron.formatText = function (input, isSentence) {
  // Construct the output string from the list of strings.
  var text = ""
  if (isSentence) {
    var first = true
    for (var s of input) {
      if (first) {
        text += s.charAt(0).toUpperCase() + s.slice(1)
        first = false
      } else if (s.charAt(0).match(/\p{General_Category=Punctuation}/gu) != null) {
        // No space before a punctuation
        text += s
      } else {
        text += " " + s
      }
    }
    text = text.trim() + "."
  } else {
    for (var s of input) {
      text += s
    }
  }

  // Construct the HTML content
  return "<p>" + text + "</p>"
}

// Make a rule generator
amytron.makeRuleBasedGenerator = function (rules, isSentence) {
  var ruleGenerator = new amytron.RuleGenerator()
  ruleGenerator.initializeFromDescription(rules)
  return function () {
    var output = ruleGenerator.generate()
    return amytron.formatText(output, isSentence)
  }
}

// Parse a raw generator description
amytron.parseGenerator = function (gen) {
  var generator = {}
  generator.name = gen.name
  generator.description = gen.description
  generator.moredescription = gen.moredescription
  generator.generate = amytron.makeRuleBasedGenerator(gen.rules, gen.issentence)
  return generator
}

// Draw the amytron div
amytron.drawDiv = function (model) {
  var contents = ''
  contents += '<select id="generator-selector" onchange="amytron.updateDescription()">'
  for (var [id, gen] of model.generators) {
    contents += '<option value="' + id + '">' + gen.name + '</option>'
  }
  contents += '</select>'
  contents += '<button onclick="amytron.generateText()">Generate</button>'
  contents += '<div id="generator-description"><br/></div>'
  contents += '<hr/><div id="generated-text"><br/></div><hr/>'
  document.getElementById('amytron').innerHTML = contents
  // Update the description
  amytron.updateDescription()
}

// Draw the description
amytron.updateDescription = function () {
  var selector = document.getElementById('generator-selector')
  var generatorId = selector.options[selector.selectedIndex].value
  var generator = amytron.model.generators.get(generatorId)
  document.getElementById('generator-description').innerHTML = generator.description + "<br/>" + generator.moredescription
}

// Load the model
amytron.loadModel = function (gen) {
  // Parse the raw model
  amytron.model.generators.set(gen.id, amytron.parseGenerator(gen))
  // Draw the amytron <div>
  amytron.drawDiv(amytron.model)
}

// Loading function for Phonagen-htmljs
amytron.load = function (jsonFile) {
  fetch(jsonFile)
  .then(function(response) {
    return response.json()
  })
  .then(amytron.loadModel)
}

// Load several files
amytron.loadAll = function (jsonFiles) {
  for (file of jsonFiles) {
    amytron.load(file)
  }
}

// Export info
//module.exports = amytron
