<template>
  <span class="mm-textarea-wrap">
    <textarea
      ref="textarea"
      v-model="localValue"
      v-bind="attrs"
      :style="textareaStyle"
      class="mm-textarea"
      v-on="listeners"
      @focus="handleFocus"
      @blur="handleBlur"
      @input="handleInput">
    </textarea>
  </span>
</template>

<script>
import pick from "lodash/pick"
import isBoolean from "lodash/isBoolean"
import has from "lodash/has"
import isObject from "lodash/isObject"
import { addListener, removeListener } from "resize-detector/dist"
import debounce from "lodash/debounce"

import input from "../../mixins/input"
import { calcTextareaHeight, calculateNodeStyling } from "./calcTextareaHeight"
import { getListeners } from "../../utils/helper"
import config from "../../managers/config"

config.defaults({
  "Textarea.autosize": true,
  "Textarea.composition": false,
  "Textarea.resize": false,
})

export default {
  name: "mm-textarea",
  mixins: [input],
  inheritAttrs: false,
  model: {
    prop: "value",
    event: "input",
  },
  props: {
    value: {
      type: String,
      default: "",
    },
    autosize: {
      type: [Boolean, Object],
      default: () => config.get("Textarea.autosize"),
      validator(val) {
        if (isBoolean(val)) {
          return true
        }
        if (isObject(val)) {
          return ["minRows", "maxRows"].some(key => has(val, key))
        }
        return false
      },
    },
    selectOnFocus: Boolean,
    composition: {
      type: Boolean,
      default: () => config.get("Textarea.composition"),
    },
    resize: {
      type: Boolean,
      default: () => config.get("Textarea.resize"),
    },
  },
  data() {
    return {
      localValue: this.value,
      focused: false,
      listeners: getListeners(["click", "keyup", "keydown", "keypress", "change"], this),
      textareaCalcStyle: {},
      lastHeight: null,
      manual: false,
      nodeStyle: {},
      observerResize: false
    }
  },
  computed: {
    attrs() {
      const props = ["placeholder", "autofocus", "resize", "selectOnFocus"]
      return {
        ...pick(this, props),
        ...this.$attrs,
        name: this.realName,
        disabled: this.realDisabled,
        readonly: this.realReadonly,
      }
    },
    textareaStyle() {
      return {
        ...this.textareaCalcStyle,
        resize: this.resize ? "vertical" : "none",
      }
    },
    debounceResizeHandler() {
      return debounce(this.resizeHandler, 60)
    },    
  },
  watch: {
    value(val) {
      this.localValue = val
    },
    localValue() {
      this.$nextTick(() => {
        this.resizeTextarea()
      })
    },
    autosize: {
      handler(val) {
        if(!!val && !this.observerResize) {
          this.$nextTick(() => {
            addListener(this.$refs.textarea, this.debounceResizeHandler)
            this.$once("hook:beforeDestroy", () => {
              removeListener(this.$refs.textarea, this.debounceResizeHandler)
            })
          })
          this.observerResize = true
        }
      },
      immediate: true
    },
  },
  mounted() {
    if (this.selectOnFocus) {
      this.$on("focus", e => e.target.select())
    }
    this.$nextTick(() => {
      this.debounceResizeHandler()
    })
  },
  methods: {
    getNodeStyle() {
      this.nodeStyle = calculateNodeStyling(this.$refs.textarea)
    },
    resizeTextarea() {
      const { autosize } = this
      if (!autosize || !this.$refs.textarea) {
        return
      }

      const styles = calcTextareaHeight(this.$refs.textarea, this.nodeStyle, autosize.minRows, autosize.maxRows)
      ;["height", "minHeight", "maxHeight"].forEach(k => {
        styles[k] = `${styles[k]}px`
      })

      this.textareaCalcStyle = styles
    },
    resizeHandler() {
      const { autosize } = this
      if (!autosize || !this.$refs.textarea) {
        return
      }     
      this.getNodeStyle()
      this.resizeTextarea()
    },
    handleFocus(e) {
      this.focused = true
      this.$emit("focus", e)
    },
    handleBlur(e) {
      this.focused = false
      this.$emit("blur", e)
    },
    handleInput($event) {
      if (this.composition || this.localValue !== this.value) {
        this.$emit("input", $event.target.value, $event)
      }
    },
    focus() {
      this.$refs.textarea.focus()
    },
    activate() {
      this.$refs.textarea.focus()
    },
    select() {
      this.$refs.textarea.select()
    },
  },
}
</script>
