import { Touch } from '../../utils/touch'
import { DOMExtension } from '../../utils/extensions/dom'
import { MathExtension } from '../../utils/extensions/math'

import { Namespace } from "../../utils/namespace"

const namespace = Namespace.Create("slider")

export default {
  name: namespace.name,
  props: {
    linear: {
      type: Array,
      default: () => []
    },
    value: {
      type: Number,
      default: 0
    },
    min: {
      type: Number,
      default: 0
    },
    max: {
      type: Number,
      default: 100
    },
    step: {
      type: Number,
      default: 1
    },
    trackHeight: {
      type: Number,
      default: -1
    },
    buttonSize: {
      type: Number,
      default: -1
    },
    trackColor: {
      type: String
    },
    barColor: {
      type: String
    },
    scaleColor: {
      type: String
    },
    buttonColor: {
      type: String
    },
    theme: {
      type: String,
      default: 'brand'
    },
    readonly: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  model: {
    prop: 'value',
    event: 'input'
  },
  data () {
    return {
      touch: Touch.Create(),
      current: 0,
      startValue: 0,
      dragStatus: ''
    }
  },
  computed: {
    wrapperCls () {
      return namespace.derive('wrapper', {
        disabled: this.disabled
      })
    },
    cls () {
      return namespace.derive()
    },
    trackCls () {
      return namespace.derive('track')
    },
    barCls () {
      return namespace.derive('bar', [
        this.theme
      ])
    },
    buttonCls () {
      return namespace.derive('button', [
        this.theme
      ])
    },
    scaleCls () {
      return namespace.derive('scale-wrapper')
    },
    scaleTextCls () {
      return namespace.derive('scale-text')
    },
    sliderStyle () {
      const style = {}
      if (this.buttonSize > 0) {
        style.height = `${this.buttonSize}px`
      }
      return style
    },
    trackStyle () {
      const style = {}
      if (this.trackColor) {
        style.background = this.trackColor
      }
      if (this.trackHeight) {
        style.height = `${this.trackHeight}px`
      }
      return style
    },
    barStyle () {
      const style = {
        width: `${((this.value - this.min) * 100) / this.scope}%`
      }
      if (this.dragStatus) {
        style.transition = 'none'
      }
      if (this.linear.length > 0) {
        style.background = `linear-gradient(to right, ${this.linear.join(',')})`
      }
      if (this.barColor) {
        style.background = this.barColor
      }
      return style
    },
    buttonStyle () {
      const style = {}
      if (this.linear.length > 0) {
        style.background = this.linear[this.linear.length - 1]
      }
      if (this.buttonColor) {
        style.background = this.buttonColor
      }
      if (this.buttonSize > 0) {
        style.width = `${this.buttonSize}px`
        style.height = `${this.buttonSize}px`
        style.borderRadius = `${this.buttonSize / 2} px`
      }
      return style
    },
    scaleStyle () {
      const style = {}
      if (this.scaleColor) {
        style.color = this.scaleColor
      }
      return style
    },
    canInteract () {
      return !this.disabled && !this.readonly
    },
    scope () {
      return this.max - this.min
    }
  },
  methods: {
    onClick (e) {
      e.stopPropagation()

      if (!this.canInteract) {
        return
      }

      const rect = this.$refs.slider.getBoundingClientRect()
      const delta = e.clientX - rect.left
      const total = rect.width
      const value = this.min + (delta / total) * this.scope

      this.update(value, true)
    },
    onTouchStart (e) {
      if (!this.canInteract) {
        return
      }

      this.touch.start(e)
      this.current = this.value

      this.startValue = this.format(this.current)
      this.dragStatus = 'start'
    },
    onTouchMove (e) {
      if (!this.canInteract) {
        return
      }

      if (this.dragStatus === 'start') {
        this.$emit('dragstart', e)
      }

      DOMExtension.PreventDefault(e)
      this.touch.move(e)
      this.dragStatus = 'draging'

      const rect = this.$refs.slider.getBoundingClientRect()

      const delta = this.touch.deltaX
      const total = rect.width
      const diff = (delta / total) * this.scope

      this.current = this.startValue + diff

      this.update(this.current)
    },
    onTouchEnd (e) {
      if (!this.canInteract) {
        return
      }

      if (this.dragStatus === 'draging') {
        this.update(this.current, true)
        this.$emit('dragend', e)
      }
      this.dragStatus = ''
    },
    format (value) {
      const min = +this.min
      const max = +this.max
      const step = + this.step

      const val = MathExtension.Clamp(value, min, max)
      const diff = Math.round((val - min) / step) * step

      return min + diff
    },
    update (value, end = false) {
      const val = this.format(value)
      if (val !== this.value) {
        this.$emit('input', val)
      }
      if (end && val !== this.startValue) {
        this.$emit('change', val)
      }
    }
  },
  render () {
    const renderSlider = () => (
      <div ref={'slider'} class={this.cls} style={this.sliderStyle} onClick={this.onClick}>
        <div class={this.trackCls} style={this.trackStyle}>
          <div class={this.barCls} style={this.barStyle}>
            <div class={this.buttonCls}
              style={this.buttonStyle}
              onTouchstart={this.onTouchStart}
              onTouchmove={this.onTouchMove}
              onTouchend={this.onTouchEnd}
              onTouchcancel={this.onTouchEnd}
              onClick={e => e.stopPropagation()}
            >
            </div>
          </div>
        </div>
      </div>
    )

    const renderScale = () => {
      if (this.$slots.default) {
        return (
          <div class={this.scaleCls} style={this.scaleStyle}>
            {this.$slots.default}
          </div>
        )
      }
    }

    return (
      <div class={this.wrapperCls}>
        {renderSlider()}
        {renderScale()}
      </div>
    )
  }
}
