import { Namespace } from '../../utils/namespace';
import { Touch } from '../../utils/touch';
import { Inertia } from '../../utils/inertia';
import { DOMExtension } from '../../utils/extensions/dom';
import { MathExtension } from '../../utils/extensions/math';

const namespace = Namespace.Create('picker-column');
export default {
  name: namespace.name,
  props: {
    readonly: {
      type: Boolean,
      default: false
    },
    itemHeight: {
      type: Number,
      required: true
    },
    visibleItemCount: {
      type: Number | String,
      required: true
    },
    defaultIndex: {
      type: Number,
      default: 0
    },
    swipeDuration: {
      type: Number,
      required: true
    },
    items: {
      type: Array,
      default: () => []
    },
    suffix: {
      type: String,
      default: ''
    },
    formatter: {
      type: Function,
      default: null
    }
  },
  data() {
    return {
      selectedIndex: this.defaultIndex,
      offset: 0,
      duration: 0,

      moving: false,
      startOffset: 0,
      correctAfterTransition: null,

      inertia: Inertia.Create(),
      touch: Touch.Create()
    };
  },
  computed: {
    wrapperCls(){
        return namespace.derive('wrapper');
    },
    itemWrapperCls(){
      return namespace.derive('item-wrapper');
    },
    cls() {
      return namespace.derive();
    },
    suffixWrapperCls(){
      return namespace.derive('suffix-wrapper')
    },
    suffixCls(){
      return namespace.derive('suffix')
    },
    itemCount() {
      return this.items.length;
    },
    baseOffset() {
      return (this.itemHeight * (this.visibleItemCount - 1)) / 2;
    },
    wrapperStyle() {
      return {
        transform: `translate3d(0, ${this.offset + this.baseOffset}px, 0)`,
        transitionDuration: `${this.duration}ms`,
        transitionProperty: this.duration ? 'all' : 'none'
      };
    },
    itemStyle() {
      return {
        height: `${this.itemHeight}px`,
        lineHeight: `${this.itemHeight}px`
      };
    },
    suffixStyle(){
      return {
        height: `${this.itemHeight}px`,
        lineHeight: `${this.itemHeight}px`
      }
    }
  },
  watch:{
    defaultIndex(val){
      this.setIndex(val)
    }
  },
  created() {
    this.setIndex(this.selectedIndex);
  },
  updated() {
    if (this.selectedIndex >= this.itemCount) {
      this.setIndex(this.itemCount - 1);
    }
  },
  methods: {
    getTouchInfo(e) {
      return e.touches[0];
    },
    getValue() {
      return this.items[this.selectedIndex];
    },
    setIndex(index, emitChange) {
      const nIndex = this.correctIndex(index) || 0;
      const offset = -nIndex * this.itemHeight;
      const trigger = () => {
        if (nIndex !== this.selectedIndex) {
          this.selectedIndex = nIndex;

          if (emitChange) {
            this.emitChange(nIndex);
          }
        }
      };
      if (this.moving && offset !== this.offset) {
        this.correctAfterTransition = trigger;
      } else {
        trigger();
      }
      this.offset = offset;
    },
    emitChange(index) {
      this.$emit('change', index);
    },
    correctIndex(index) {
      const nIndex = MathExtension.Clamp(index, 0, this.itemCount);
      for (let i = nIndex; i < this.itemCount; i++) {
        if (!this.isOptionDisabled(this.items[i])) return i;
      }
      for (let i = nIndex - 1; i >= 0; i--) {
        if (!this.isOptionDisabled(this.items[i])) return i;
      }
    },
    isOptionDisabled(option) {
      return typeof option === 'object' && option.disabeld;
    },
    onTouchStart(e) {
      if (this.readonly) {
        return;
      }
      const touchInfo = this.getTouchInfo(e);
      this.touch.start(touchInfo);

      this.duration = 0;
      this.startOffset = this.offset;
      this.correctAfterTransition = null;

      this.inertia.start(this.offset);
    },
    onTouchMove(e) {
      if (this.readonly) {
        return;
      }
      DOMExtension.PreventDefault(e);
      const touchInfo = this.getTouchInfo(e);
      this.touch.move(touchInfo);
      if (!this.touch.isVertical) {
        return;
      }
      this.moving = true;
      this.offset = MathExtension.Clamp(this.startOffset + this.touch.deltaY, -this.itemCount * this.itemHeight, this.itemHeight);
      const now = Date.now();
      if (now - this.touchStartTime > Inertia.LIMIT_TIME) {
        this.inertia.start(this.offset, now);
      }
    },
    onTouchEnd(e) {
      if (this.readonly) {
        return;
      }
      this.inertia.end(this.offset);
      if (this.inertia.shouldActive) {
        this.startInertia();
      } else {
        const index = this.getIndexByOffset(this.offset);
        this.duration = 200;
        this.setIndex(index, true);
        setTimeout(() => (this.moving = false), 0);
      }
    },
    onTransitionEnd() {
      this.stopInertia();
    },
    getIndexByOffset(offset) {
      return MathExtension.Clamp(Math.round(-offset / this.itemHeight), 0, this.itemCount - 1);
    },
    startInertia() {
      const speed = Math.abs(this.inertia.speed);
      const offset = this.offset + (speed / 0.003) * (this.inertia.distance < 0 ? -1 : 1);
      const index = this.getIndexByOffset(offset);
      this.duration = this.swipeDuration;
      this.setIndex(index, true);
    },
    stopInertia() {
      this.moving = false;
      this.duration = 0;
      if (this.correctAfterTransition) {
        this.correctAfterTransition();
        this.correctAfterTransition = null;
      }
    },
    onItemClick(e, index) {
      if (this.moving || this.readonly) {
        return;
      }
      this.correctAfterTransition = null;
      this.duration = 200;
      this.setIndex(index, true);
    }
  },
  render(){
      const renderItems = ()=> this.items.map((item,i)=>{
          const cls = namespace.derive('item',{selected: i === this.selectedIndex})
          return (
            <li class={cls} key={i} style={this.itemStyle} vOn:click_stop={e=>this.onItemClick(e,i)}>
              {this.formatter ? this.formatter(item.text) : item.text}
            </li>
          )
      })

      const renderSuffix = ()=>{
        if(this.suffix){
          return (
            <div class={this.suffixWrapperCls}>
              <div class={this.suffixCls} style={this.suffixStyle}>{this.suffix}</div>
            </div>
          )
        }
      }

      return (
        <div class={this.wrapperCls}>
          <div class={this.itemWrapperCls} onTouchstart={this.onTouchStart} onTouchmove={this.onTouchMove} onTouchend={this.onTouchEnd} onTouchcancel={this.onTouchEnd}>
              <ul class={this.cls} style={this.wrapperStyle} onTransitionend={this.onTransitionEnd}>
                  {renderItems()}
              </ul>
          </div>
          {renderSuffix()}
        </div>
      )
  }
}