class Vue {
  constructor(options) {
    
    if (typeof options.beforeCreate === 'function') {
      options.beforeCreate.bind(this)()
    }
    this.$data = options.data()
    
    this.dataReact()
    this.thisData(this.$data)
    if (typeof options.created === 'function') {
      options.created.bind(this)()
    }
    if (typeof options.beforeMount === 'function') {
      options.beforeMount.bind(this)()
    }
    this.$el = document.querySelector(options.el)
    this.methods = options.methods
    if (typeof options.mounted === 'function') {
      options.mounted.bind(this)()
    }
    
    this.compliedNode(this.$el)
  }
  
  thisData(data) {
    for (let key in data) {
      this[key] = data[key]
    }
  }
  
  dataReact() {
    Object.keys(this.$data).forEach(key => {
      
      Observe.bind(this)(this.$data, key, this.$data[key])
    })
    
    function Observe(data, key, value, that = this) { 
      Object.defineProperty(data, key, {
        get() {
          return value
        },
        set(newVal) {
          value = newVal
          
          that[key] = newVal
          that.Dep.trigger(key)
        }
      })
    }
  }
  
  Dep = {
    map: {},
    
    collect(eventName, fn) {
      if (!this.map[eventName]) {
        this.map[eventName] = []
      }
      this.map[eventName].push(fn)
    },
    
    trigger(eventName) {
      this.map[eventName].forEach(fn => fn())
    }
  }
  
  compliedNode(node) {
    
    let childNodes = node.childNodes
    
    let tagNodes = Array.from(childNodes).filter(item => {
      return item.nodeType === 1
    })
    
    this.recursiveNode(tagNodes)
  }
  
  recursiveNode(nodeParent) {
    nodeParent.forEach(node => {
      if (node.childNodes.length > 0) {
        this.recursiveNode(node.childNodes)
      }
      if (node.nodeType === 1) {
        
        let attrs = node.attributes
        
        Array.from(attrs).forEach((item) => {
          if (item.nodeName === '@click') {
            
            node.addEventListener('click', this.methods[item.nodeValue].bind(this))
          }
          if (item.nodeName === 'v-model') {
            
            node.value = this.$data[item.nodeValue]
            this.Dep.collect(item.nodeValue, () => node.value = this.$data[item.nodeValue])
            
            node.addEventListener('input', (e) => {
              node.value = this.$data[item.nodeValue] = e.target.value
            })
          }
        })
        
        if (node.innerHTML.includes('{{')) {
          
          let reg = /{{(.*?)}}/g
          let match = node.innerHTML.match(reg)
          
          match.forEach((item) => {
            
            let key = item.slice(2, -2).trim()
            if (this.$data[key]) {
              
              node.innerHTML = node.innerHTML.replace(item, this.$data[key])
              
              let lastValue = JSON.parse(JSON.stringify(this.$data[key]))
              
              this.Dep.collect(key, () => {
                if (lastValue !== this.$data[key]) {
                  
                  node.innerHTML = node.innerHTML.replace(lastValue, this.$data[key])
                  lastValue = JSON.parse(JSON.stringify(this.$data[key]))
                }
              })
            }
          })
        }
      }
    })
  }
}