<template>
  <ul :class="'form-item ' + groupClass">
    <component :is="(type === 'checkbox-filter') ? 't-checkbox-filter' : ((type === 'radiobox') ? 't-radiobox' : 't-checkbox')" v-for="(item, index) in labelStates" :label="item" :count="countStates[index]" :idx="index" :checked="checkedStates[index]" :disabled="disabledStates[index]" :value="valueStates[index]"></component>
  </ul>
</template>
<script>
/*
 * 本文件实现了复选框组、单选框组和复选按钮组三类组件
 */
import Vue from 'vue';
import eventBus from './bus';
import TCheckbox from './checkbox';
import TRadiobox from './radiobox';
import TCheckboxFilter from './checkbox-filter';

const CSS_INLINE = 'form-inline';
const CSS_MARGIN_TOP = 'form-row';
const SIZE_NORMAL = 'normal';
const TYPE_CHECKBOX = 'checkbox';
const TYPE_RADIOBOX = 'radiobox';
const TYPE_CHECKBOX_FILTER = 'checkbox-filter';

var seed = 0;

function checkStringValue (newValue) {
  if (typeof (newValue) !== 'string' || newValue === '') {
    return 'All';
  }
  return newValue;
}

function checkBooleanValue (newValue) {
  if (typeof (newValue) === 'string') {
    return (newValue === 'true');
  }
  return !!newValue;
}

function nextName () {
  return 'taurus-box-name' + seed++;
}

function addToHead (arr, value) {
  var i;
  for (i = arr.length - 1; i > 0; i--) {
    Vue.set(arr, i + 1, arr[i]);
  }
  Vue.set(arr, 0, value);
}

export default {
  name: 'taurus-box-group',
  components: {
    TCheckbox,
    TRadiobox,
    TCheckboxFilter
  },
  created: function () {
    var count = 0;
    var i;

    // 将props复制到计算属性
    this._mtop = false;
    this._inline = false;
    this._labels = ['Untitled'];
    this._counts = [0];
    this._values = [''];
    this._checked = [false];
    this._disabled = [false];
    this._totalButtonVisibled = false;
    this._totalButtonChecked = false;
    this._totalButtonLabel = '';
    this.totalButtonIsShowing = this.totalButtonVisibled;
    this.totalButtonIsChecked = this.totalButtonChecked;
    this.totalButtonText = this.totalButtonLabel;
    this.marginTopState = this.mtop;
    this.inlineState = this.inline;
    this.labelStates = this.labels;
    this.countStates = this.counts;
    this.valueStates = this.values;
    this.checkedStates = this.checked;
    this.disabledStates = this.disabled;

    // console.log('this.labelStates.length = ' + this.labelStates.length);
    // console.log('this.countStates.length = ' + this.countStates.length);
    // console.log('this.valueStates.length = ' + this.valueStates.length);
    // console.log('this.checkedStates[0] = ' + this.checkedStates[0]);
    // console.log('this.disabledStates.length = ' + this.disabledStates.length);

    if (this.type === TYPE_CHECKBOX_FILTER && this.totalButtonIsShowing === true) {
      this.countStates.forEach(function (item, index) {
        count += item;
      });
      addToHead(this.labelStates, this.totalButtonText);
      addToHead(this.countStates, count);
      addToHead(this.disabledStates, false);
      if (this.totalButtonIsChecked) {
        for (i = this.labelStates.length - 1; i >= 1; i--) {
          this.checkedStates[i] = false;
        }
        this.checkedStates[0] = true;
      } else {
        this.checkedStates[0] = false;
      }
    }
    eventBus.$on('item-changed', this._fireChangedEvent);
    eventBus.$on('item-checked', this._fireCheckedEvent);
    eventBus.$on('item-unchecked', this._fireUncheckedEvent);
    eventBus.$on('radio-item-check', this._fireRadioItemCheck);
    eventBus.$on('refresh-total', this._refreshTotal);
  },
  beforeDestroy: function () {
    eventBus.$off('item-changed');
    eventBus.$off('item-checked');
    eventBus.$off('item-unchecked');
    eventBus.$off('radio-item-check');
    eventBus.$off('refresh-total');
  },
  mounted: function () {
    var children = this.$children;
    var i;
    var count = children.length;
    var itemName = nextName();
    for (i = count - 1; i >= 0; i--) {
      children[i].itemName = itemName;
      children[i].size = this.size || SIZE_NORMAL;
    }
  },
  props: {
    type: {
      default: TYPE_CHECKBOX,
      type: String
    },
    size: {
      defautl: SIZE_NORMAL,
      type: String
    },
    totalButtonVisibled: String,
    totalButtonLabel: String,
    totalButtonChecked: String,
    disabled: String,
    mtop: String,
    inline: String,
    labels: String,
    counts: String,
    values: String,
    checked: String
  },
  computed: {
    marginTopState: {
      set: function (newValue) {
        this._mtop = checkBooleanValue(newValue);
      },
      get: function () {
        return this._mtop;
      }
    },
    inlineState: {
      set: function (newValue) {
        this._inline = checkBooleanValue(newValue);
      },
      get: function () {
        return this._inline;
      }
    },
    labelStates: {
      set: function (newValue) {
        if (typeof (newValue) === 'string' && newValue !== '') {
          this._labels = newValue.split(',');
        } else if (Array.isArray(newValue)) {
          this._labels = newValue;
        }
      },
      get: function () {
        return this._labels;
      }
    },
    countStates: {
      set: function (newValue) {
        var arr, i;
        var count = this.labelStates.length;
        if (newValue === undefined) {
          arr = [];
          for (i = 0; i < count; i++) {
            arr[i] = 0;
          }
          this._counts = arr;
        } else {
          if (typeof newValue === 'string' && newValue !== '') {
            arr = newValue.split(',');
            for (i = 0; i < count; i++) {
              arr[i] = (arr[i] ? parseInt(arr[i], 10) : 0);
            }
            this._counts = arr;
          } else if (Array.isArray(newValue)) {
            this._counts = [].concat(newValue);
          }
        }
      },
      get: function () {
        return this._counts;
      }
    },
    valueStates: {
      set: function (newValue) {
        var arr, i;
        var count = this.labelStates.length;
        if (typeof newValue === 'string' && newValue !== '') {
          arr = newValue.split(',');
          for (i = 0; i < count; i++) {
            arr[i] = arr[i] || '';
          }
          if (this.totalButtonIsShowing) {
            this._values = [''].concat(arr);
          } else {
            this._values = arr;
          }
        }
      },
      get: function () {
        return this._values;
      }
    },
    checkedStates: {
      set: function (newValue) {
        var arr, i, item;
        var count = this.labelStates.length;
        if (typeof newValue === 'string' && newValue !== '') {
          arr = newValue.split(',');
          for (i = 0; i < count; i++) {
            item = arr[i];
            arr[i] = (item && (item === 'true')) || false;
          }
          this._checked = arr;
        }
      },
      get: function () {
        return this._checked;
      }
    },
    disabledStates: {
      set: function (newValue) {
        var arr, i, item;
        var count = this.labelStates.length;
        if (newValue === undefined) {
          arr = [];
          for (i = 0; i < count; i++) {
            arr[i] = false;
          }
          this._disabled = arr;
        } else {
          if (typeof newValue === 'string' && newValue !== '') {
            arr = newValue.split(',');
            for (i = 0; i < count; i++) {
              item = arr[i];
              arr[i] = (item && (item === 'true')) || false;
            }
            this._disabled = arr;
          }
        }
      },
      get: function () {
        return this._disabled;
      }
    },
    totalButtonText: {
      set: function (newValue) {
        this._totalButtonLabel = checkStringValue(newValue);
      },
      get: function () {
        return this._totalButtonLabel;
      }
    },
    totalButtonIsChecked: {
      set: function (newValue) {
        this._totalButtonChecked = checkBooleanValue(newValue);
      },
      get: function () {
        return this._totalButtonChecked;
      }
    },
    totalButtonIsShowing: {
      set: function (newValue) {
        this._totalButtonVisibled = checkBooleanValue(newValue);
      },
      get: function () {
        return this._totalButtonVisibled;
      }
    },
    groupClass: function () {
      var css = '';
      if (this.type === TYPE_CHECKBOX_FILTER) {
        css = CSS_INLINE + ' ';
      } else {
        css = (this.inlineState === true) ? (CSS_INLINE + ' ') : '';
      }
      css += (this.marginTopState === true) ? CSS_MARGIN_TOP : '';
      return css;
    }
  },
  methods: {
    _refreshTotal: function (source) {
      var i;
      var num = 0;
      var children = this.$children;
      var count = children.length;
      for (i = count - 1; i >= 1; i--) {
        num += children[i].countState;
      }
      children[0].countState = num;
    },
    _fireChangedEvent: function (source, index, checked) {
      if (source.$parent === this) {
        this.$emit('changed', index, checked);
      }
    },
    _fireCheckedEvent: function (source, index) {
      if (source.$parent === this) {
        this.$emit('checked', index);
      }
    },
    _fireUncheckedEvent: function (source, index) {
      if (source.$parent === this) {
        this.$emit('unchecked', index);
      }
    },
    _fireRadioItemCheck: function (source, index) {
      if (source.$parent === this) {
        this._changeCheckState(index, true);
      }
    },
    /**
     * 切换勾选状态
     * @private
     * @param {Number} index 复选框序号，序号从0开始，顺序是从左至右、从上至下
     * @param {Boolean} [checked] 为true表示勾选，为false表示取消勾选，不传表示当前勾选状态取反
     */
    _changeCheckState: function (index, checked) {
      var children;
      if (index === undefined) {
        this.$children.forEach(function (item, index) {
          if (checked === true) {
            item.check();
          } else if (checked === false) {
            item.uncheck();
          } else {
            item.toggle();
          }
        });
      } else {
        children = this.$children[index];
        if (children) {
          eventBus.$emit('toggle-check-state', this, children, checked);
        }
      }
    },
    /**
     * 取反指定索引对应的项
     * @param  {Number} index 索引值
     */
    toggle: function (index) {
      this._changeCheckState(index);
    },
    /**
     * 选择指定索引对应的项
     * @param  {Number} index 索引值
     */
    check: function (index) {
      this._changeCheckState(index, true);
    },
    /**
     * 取消选择指定索引对应的项
     * @param  {Number} index 索引值
     */
    uncheck: function (index) {
      this._changeCheckState(index, false);
    },
    /**
     * 判断给定索引值是否在
     * @private
     * @param  {Number} index 索引值
     * @return {Boolean} 如果索引值在子组件数组下标索引范围内返回true，否则返回false
     */
    _inRange: function (index) {
      var len = this.$children.length;
      var firstSeq = (this.type === TYPE_CHECKBOX_FILTER && this.totalButtonIsShowing === true) ? 1 : 0;
      return (index >= firstSeq && index < len);
    },
    /**
     * 设置或返回总数，仅在checkbox-filter类型时有效
     *
     * 1. 当没传任何参数时返回所有复选框总数数组；如：
     *
     * this.$refs.myTag.count();
     *
     * 2. 当仅传递index时，如果index是数值，那么返回该索引对应的总数，
     * 如果index是数组，那么返回此数组内每一个有效的索引对应的总数；如：
     *
     * this.$refs.myTag.count(1); // 返回第二个复选框对应的总数
     * this.$refs.myTag.count([0, 1, 2]); // 返回前三个复选框对应的总数数组
     *
     * 3. 当传递了有效索引index和数值num时，将设置该索引对应复选框的总数；如：
     *
     * this.$refs.myTag.count(2, 99); // 将第三个复选框的总数设置为99
     *
     * 4. 当传递了索引数组index和总数数组num时，将设置索引数组内每一个有效索引
     * 对应的总数；如：
     *
     * // 分别将第三、四、五个复选框的总数设置为99、98和10
     * this.$refs.myTag.count([2, 3, 4], [99, 98, 10]);
     *
     * @param  {Number|Array} [index] 复选框索引或索引数组，注意，索引值从0开始，
     * 在显示总数复选按钮的时候，总数复选按钮的索引值将被忽略，索引值将从总数复选框
     * 按钮后续按钮开始算起，同样是从0开始。
     * @param  {Number|Array} [num]   复选框显示总数值或总数值数组
     * @return {Array} 返回左右除总数按钮以外的按钮对应的总数值，返回值情况参见描述，
     * 特别的，如果当前不是checkbox-filter类型，那么返回null
     */
    count: function (index, num) {
      var i, seq, len, children, result;
      if (this.type === TYPE_CHECKBOX_FILTER) {
        children = this.$children;
        len = children.length;
        if (index !== undefined) {
          if (typeof index === 'number') {
            if (this.totalButtonIsShowing === true) {
              index++;
            }
            if (this._inRange(index)) {
              if (num === undefined) {
                return [children[index].countState];
              } else if (typeof num === 'number') {
                children[index].countState = num;
                eventBus.$emit('refresh-total', this);
              }
            }
          } else if (Array.isArray(index) === true) {
            if (num === undefined) { // 返回给定索引对应的总数
              result = [];
              len = index.length;
              for (i = 0; i < len; i++) {
                seq = index[i];
                if (this.totalButtonIsShowing === true) {
                  seq++;
                }
                if (this._inRange(seq)) { // 序号不在范围内将被跳过
                  result.push(children[seq].countState);
                }
              }
              return result;
            } else if (Array.isArray(num) === true && index.length === num.length) {
              for (i = index.length - 1; i >= 0; i--) {
                seq = index[i];
                if (typeof seq === 'number' && typeof num[i] === 'number') {
                  if (this.totalButtonIsShowing === true) {
                    seq++;
                  }
                  if (this._inRange(seq)) { // 序号不在范围内将被跳过
                    children[seq].countState = num[i];
                  }
                } else {
                  continue; // 非数值项将被跳过
                }
              }
              eventBus.$emit('refresh-total', this);
            }
          }
        } else if (num === undefined) { // 返回所有总数，总数按钮除外
          result = [];
          i = (this.totalButtonIsShowing === true) ? 1 : 0;
          for (; i < len; i++) {
            result.push(children[i].countState);
          }
          return result;
        }
      }
      return null;
    },
    /**
     * 改变指定索引对应的可用状态
     * @private
     * @param  {Number} index 索引值
     * @param  {Boolean} disabled 是否禁用，true为禁用，false为启用
     */
    _changeUsability: function (index, disabled) {
      var children;
      if (index === undefined) {
        this.$children.forEach(function (item, index) {
          if (disabled === true) {
            item.disable();
          } else {
            item.enable();
          }
        });
      } else {
        children = this.$children[index];
        if (children) {
          eventBus.$emit('toggle-usability', this, children, disabled);
        }
      }
    },
    /**
     * 启用索引值对应的项
     * @param  {Number} index 索引值
     */
    enable: function (index) {
      this._changeUsability(index, false);
    },
    /**
     * 禁用索引值对应的项
     * @param  {Number} index 索引值
     */
    disable: function (index) {
      this._changeUsability(index, true);
    },
    /**
     * 获得索引值对应项的选择状态
     * @param  {Number}  index 索引值
     * @return {Boolean} 如果index对应的项为选中状态，那么返回true，否则返回false
     */
    isChecked: function (index) {
      var children;
      var result = [];
      if (index === undefined) {
        this.$children.forEach(function (item, index) {
          result.push(item.checkedState);
        });
      } else {
        children = this.$children[index];
        if (children) {
          result.push(children.checkedState);
        }
      }
      return result;
    },
    /**
     * 设置或返回值，当在checkbox-filter类型时只返回除总数复选按钮以外的复选按钮值
     *
     * 1. 当没传任何参数时返回所有复选框值数组；如：
     *
     * this.$refs.myTag.value();
     *
     * 2. 当仅传递index时，如果index是数值，那么返回该索引对应的值，
     * 如果index是数组，那么返回此数组内每一个有效的索引对应的值；如：
     *
     * this.$refs.myTag.value(1); // 返回第二个复选框对应的值
     * this.$refs.myTag.value([0, 1, 2]); // 返回前三个复选框对应的值数组
     *
     * 3. 当传递了有效索引index和值value时，将设置该索引对应复选框的值；如：
     *
     * this.$refs.myTag.value(2, 'abc'); // 将第三个复选框的总数设置为'abc'
     *
     * 4. 当传递了索引数组index和值数组value时，将设置索引数组内每一个有效索引
     * 对应的值；如：
     *
     * // 分别将第三、四、五个复选框的总数设置为'abc'、'def'和'ghi'
     * this.$refs.myTag.value([2, 3, 4], ['abc','def','ghi']);
     *
     * @param  {Number|Array} [index] 复选框索引或索引数组，注意，索引值从0开始，
     * 在显示总数复选按钮的时候，总数复选按钮的索引值将被忽略，索引值将从总数复选框
     * 按钮后续按钮开始算起，同样是从0开始。
     * @param  {Number|Array} [value]   复选框按钮对应的值或值数组
     * @return {Array} 返回左右除总数按钮以外的按钮对应的值，返回值情况参见描述，
     * 当没有指定索引但指定了值时返回null
     */
    value: function (index, value) {
      var i, seq, len, children, result;
      var hasTotal = (this.type === TYPE_CHECKBOX_FILTER && this.totalButtonIsShowing === true);
      children = this.$children;
      len = children.length;
      if (index !== undefined) {
        if (typeof index === 'number') {
          if (hasTotal === true) {
            index++;
          }
          if (this._inRange(index)) {
            if (value === undefined) {
              return [children[index].valueState];
            } else if (typeof value === 'string') {
              children[index].valueState = value;
            }
          }
        } else if (Array.isArray(index) === true) {
          if (value === undefined) { // 返回给定索引对应的总数
            result = [];
            len = index.length;
            for (i = 0; i < len; i++) {
              seq = index[i];
              if (hasTotal === true) {
                seq++;
              }
              if (this._inRange(seq)) { // 序号不在范围内将被跳过
                result.push(children[seq].valueState);
              }
            }
            return result;
          } else if (Array.isArray(value) === true && index.length === value.length) {
            for (i = index.length - 1; i >= 0; i--) {
              seq = index[i];
              if (typeof seq === 'number' && typeof value[i] === 'string') {
                if (hasTotal === true) {
                  seq++;
                }
                if (this._inRange(seq)) { // 序号不在范围内将被跳过
                  children[seq].valueState = value[i];
                }
              } else {
                continue; // 非数值项将被跳过
              }
            }
          }
        }
      } else if (value === undefined) { // 返回所有值，总数按钮除外
        result = [];
        i = (hasTotal === true) ? 1 : 0;
        console.log('i = ' + i);
        for (; i < len; i++) {
          result.push(children[i].valueState);
        }
        return result;
      }
      return null;
    }
  },
  'TYPE_CHECKBOX': TYPE_CHECKBOX,
  'TYPE_RADIOBOX': TYPE_RADIOBOX,
  'TYPE_CHECKBOX_FILTER': TYPE_CHECKBOX_FILTER
};
</script>
