<template>
<section class="w-full h-full">
  <p class="h2 ml-1 mb-2">Activity Select</p>
  <div class="flex flex-row justify-between items-start gap-4 w-full h-[calc(100%_-_48px)]">
    <!-- Attribute Name -->
    <div class="basis-1/3 bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm">
      <p class="h2 my-2">Attribute Name ({{ attTotal }})</p>
      <div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_56px)]">
        <DataTable v-model:selection="selectedAttName" :value="filterAttrs" dataKey="key" breakpoint="0" :tableClass="tableClass" @row-select="switchAttNameRadio">
          <Column selectionMode="single" :headerClass="headerModeClass" :bodyClass="bodyModeClass"></Column>
          <Column field="key" header="Attribute" :headerClass="headerClass" :bodyClass="bodyClass" sortable></Column>
        </DataTable>
      </div>
    </div>
    <!-- Range Selection -->
    <div class="basis-2/3 bg-neutral-10 border border-neutral-300 rounded-xl px-4 w-full h-full">
      <div class="flex justify-between items-center my-2 flex-wrap">
        <p class="h2">Range Selection {{ attRangeTotal }}</p>
      </div>
      <div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 w-full h-[calc(100%_-_70px)]">
        <!-- type: boolean -->
        <div v-if="selectedAttName.type === 'boolean'" class="w-full">
          <DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" :tableClass="tableClass" @row-select="onRowSelect" >
            <ColumnGroup type="header">
              <Row>
                  <Column selectionMode="single" :headerClass="headerModeClass" ></Column>
                  <Column field="value" header="Value" :headerClass="headerClass"  sortable />
                  <Column field="freq" header="Occurrences" :headerClass="headerClass" sortable :colspan="3" />
              </Row>
            </ColumnGroup>
            <Column selectionMode="single" :bodyClass="bodyModeClass"></Column>
            <Column field="label" header="Activity" :bodyClass="bodyClass"></Column>
            <Column header="進度條" bodyClass="!py-2 !border-0 min-w-[96px]">
              <template #body="slotProps">
                <div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
                  <div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_progress_bar)"></div>
                </div>
              </template>
            </Column>
            <Column field="occ_value" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
            <Column field="occ_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
          </DataTable>
        </div>
        <!-- type: string -->
        <div v-else-if="selectedAttName.type === 'string'" class="w-full">
          <DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm" @row-select="onRowSelect" @row-unselect="onRowUnselect" @row-select-all="onRowSelectAll($event)" @row-unselect-all="onRowUnelectAll">
            <ColumnGroup type="header">
              <Row>
                  <Column selectionMode="multiple" :headerClass="headerModeClass" ></Column>
                  <Column field="value" header="Value" :headerClass="headerClass"  sortable />
                  <Column field="freq" header="Occurrences" :headerClass="headerClass" sortable :colspan="3" />
              </Row>
            </ColumnGroup>
            <Column selectionMode="multiple" :bodyClass="bodyModeClass"></Column>
            <Column field="value" header="Activity" :bodyClass="bodyClass"></Column>
            <Column header="進度條" bodyClass="!py-2 !border-0 min-w-[96px]">
              <template #body="slotProps">
                <div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
                  <div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_progress_bar)"></div>
                </div>
              </template>
            </Column>
            <Column field="occ_value" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
            <Column field="occ_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
          </DataTable>
        </div>
        <!-- type: value -->
        <div v-else-if="valueTypes.includes(selectedAttName.type)" class="space-y-2 text-sm w-full">
          <!-- Chart.js -->
          <div class="h-3/5 relative">
            <Chart type="line" :data="chartData" :options="chartOptions" class="h-30rem" id="chartCanvasId"/>
            <div id="chart-mask-left" class="absolute bg-neutral-10/50"></div>
            <div id="chart-mask-right" class="absolute bg-neutral-10/50"></div>
          </div>
          <!-- Slider -->
          <div class="px-2 py-3">
            <Slider v-model="selectArea" :step="1" :min="0" :max="selectRange" range class="mx-2" @change="changeSelectArea($event)"/>
          </div>
          <!-- Calendar / InputNumber group -->
          <div>
            <div v-if="selectedAttName.type === 'date'" class="flex justify-center items-center space-x-2 w-full">
              <div>
                <span class="block mb-2">Start time</span>
                <Calendar v-model="startTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="startMinDate" :maxDate="startMaxDate" showTime showIcon hourFormat="24" @date-select="sliderValueRange($event, 'start')" id="startCalendar" />
              </div>
              <span class="block mt-4">~</span>
              <div>
                <span class="block mb-2">End time</span>
                <Calendar v-model="endTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="endMinDate" :maxDate="endMaxDate" showTime showIcon hourFormat="24" @date-select="sliderValueRange($event, 'end')" id="endCalendar"/>
              </div>
            </div>
            <div v-else class="flex justify-center items-center space-x-2 w-full">
              <InputNumber v-model="valueStart" :min="valueStartMin" :max="valueStartMax" :maxFractionDigits="2" inputClass="w-24 text-sm text-right" @blur="sliderValueRange($event, 'start')"></InputNumber>
              <span class="block px-2">~</span>
              <InputNumber v-model="valueEnd" :min="valueEndMin" :max="valueEndMax"  inputClass="w-24 text-sm text-right" :maxFractionDigits="2" @blur="sliderValueRange($event, 'end')"></InputNumber>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>
</template>
<script>
import { storeToRefs } from 'pinia';
import AllMapDataStore from '@/stores/allMapData.js';
import { setLineChartData } from '@/module/setChartData.js';
import getMoment from 'moment';
import InputNumber from 'primevue/inputnumber';
import { Decimal } from 'decimal.js';

export default {
  setup() {
    const allMapDataStore = AllMapDataStore();
    const { filterAttrs } = storeToRefs(allMapDataStore);

    return { filterAttrs }
  },
  data() {
    return {
      selectedAttName: {},
      selectedAttRange: null,
      valueTypes: ['int', 'float', 'date'],
      classTypes: ['boolean', 'string'],
      chartData: {},
      chartOptions: {},
      chartComplete: null, // 已出圖的 chart.js 資料
      selectArea: null,
      selectRange: 1000, // 更改 select 的切分數
      startTime: null, // PrimeVue Calendar v-model
      endTime: null, // PrimeVue Calendar v-model
      startMinDate: null,
      startMaxDate: null,
      endMinDate: null,
      endMaxDate: null,
      valueStart: null, // PrimeVue InputNumber v-model
      valueEnd: null, // PrimeVue InputNumber v-model
      valueStartMin: null,
      valueStartMax: null,
      valueEndMin: null,
      valueEndMax: null,
      tableClass: 'w-full h-full !border-separate !border-spacing-x-2 !table-auto text-sm',
      headerModeClass: 'w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10',
      headerClass: '!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10',
      bodyModeClass: '!p-2 !border-0',
      bodyClass: 'break-words !py-2 !border-0',
      panelProps: {
        onClick: (event) => {
          event.stopPropagation();
        },
      },
    }
  },
  computed: {
    attTotal: function() {
      return this.filterAttrs.length;
    },
    attRangeTotal: function() {
      const type = this.selectedAttName.type;

      return !this.classTypes.includes(type) ? null
      : this.attRangeData ? `(${this.attRangeData.length})`
      : null;
    },
    attRangeData: function() {
      let data = [];
      const type = this.selectedAttName.type;
      let sum = this.selectedAttName.options.map(item => item.freq).reduce((acc, cur) => acc + cur, 0);
      data = this.selectedAttName.options.map((item, index) => {
        let result;
        let ratio = item.freq / sum;
        result = {
          id: index + 1,
          key: this.selectedAttName.key,
          type: type,
          value: item.value,
          occ_progress_bar: ratio * 100,
          occ_value: item.freq.toLocaleString('en-US'),
          occ_ratio: this.getPercentLabel(ratio),
          freq: item.freq
        };
        result.label = type !== 'boolean' ? null : item.value ? 'Yes' : 'No';
        return result;
      })
      return data.sort((x, y) => y.freq - x.freq);
    },
    // 取出選取的 Attribute radio 數值型的資料
    valueData: function() {
      // filter 回傳陣列，find 回傳遞一個找到的元素，因此使用 find 方法。
      if(this.valueTypes.includes(this.selectedAttName.type)){
        const valueData = this.filterAttrs.find(item => item.type === this.selectedAttName.type && item.key === this.selectedAttName.key);
        return valueData
      }
    },
    // 找出 slidrData，時間格式:毫秒時間戳
    sliderData: function() {
      let xAxisMin;
      let xAxisMax;
      let min = this.valueData.min;
      let max = this.valueData.max;
      const type = this.valueData.type;
      switch (type) {
        case 'date':
          xAxisMin = new Date(min).getTime();
          xAxisMax = new Date(max).getTime();
          break;
        default:
          xAxisMin = min;
          xAxisMax = max;
          break;
      }
      let range = xAxisMax - xAxisMin;
      let step = range / this.selectRange;
      let sliderData = []

      for (let i = 0; i <= this.selectRange; i++) {
        sliderData.push(xAxisMin + (step * i));
      }
      switch (type) {
        case 'int':
          sliderData = sliderData.map(value => Math.round(value));
          break;
        case 'float':
          sliderData = sliderData.map(value => new Decimal(value.toFixed(2)).toNumber());
          break;
        default:
          break;
      }
      return sliderData;
    },
    // user select value type start and end
    attValueTypeStartEnd: function() {
      let start;
      let end;
      let data;
      const type = this.selectedAttName.type;

      switch (type) {
        case 'date':
          start = getMoment(this.startTime).format('YYYY-MM-DDTHH:mm:00');
          end = getMoment(this.endTime).format('YYYY-MM-DDTHH:mm:00');
          break;
        default:
          start = this.valueStart;
          end = this.valueEnd;
          break;
      }
      data = { // 傳給後端的資料
        type: type,
        data: {
          key: this.selectedAttName.key,
          min: start,
          max: end,
        }
      }
      this.$emit('select-attribute', data);

      return [start, end];
    },
    labelsData: function() {
      let min = new Date(this.valueData.min).getTime();
      let max = new Date(this.valueData.max).getTime();
      let numPoints = 11;
      let step = (max - min) / (numPoints - 1);
      let data = [];
      for(let i = 0; i< numPoints; i++) {
        const x = min + i * step;
        data.push(x);
      }
      return data;
    },
  },
  methods: {
    /**
     * 選取類別型 table 的選項
     */
    onRowSelect() {
      const type = this.selectedAttName.type;
      const data = {
        type: type,
        data: this.selectedAttRange,
      };
      this.$emit('select-attribute', data);
    },
    /**
     * 取消類別型 table 的選項
     */
    onRowUnselect() {
      const type = this.selectedAttName.type;
      const data = {
        type: type,
        data: this.selectedAttRange,
      };
      this.$emit('select-attribute', data);
    },
    /**
     * 選取類別型 table 的全選項
     * @param {event} e
     */
    onRowSelectAll(e) {
      this.selectedAttRange = e.data;
      const type = this.selectedAttName.type;
      const data = {
        type: type,
        data: this.selectedAttRange,
      };
      this.$emit('select-attribute', data);
    },
    /**
     * 取消類別型 table 的全選項
     */
    onRowUnelectAll() {
      this.selectedAttRange = null;
      const type = this.selectedAttName.type;
      const data = {
        type: type,
        data: this.selectedAttRange,
      };
      this.$emit('select-attribute', data)
    },
    /**
     * 切換 Attribute Name Radio
     * @param {event} e
     */
    switchAttNameRadio(e) {
      this.selectedAttRange = null;
      this.startTime = null;
      this.endTime = null;
      this.valueStart = null;
      this.valueEnd = null;
      if(this.valueData) { // 切換 Attribute Name
        // 初始化雙向綁定
        this.selectArea = [0, this.selectRange];
        const min = this.valueData.min;
        const max = this.valueData.max;
        switch (this.selectedAttName.type) {
          case 'date':
            // 除了 date 外雙向綁定為空
            this.valueStart = null;
            this.valueEnd = null;
            // 初始化: Calendar
            this.startMinDate = new Date(min);
            this.startMaxDate = new Date(max);
            this.endMinDate = new Date(min);
            this.endMaxDate = new Date(max);
            // 初始化: 讓日曆的範圍等於時間軸的範圍
            this.startTime = new Date(min);
            this.endTime = new Date(max);
            break;
          default:
            // date 雙向綁定為空
            this.startTime = null;
            this.endTime = null;
            // 初始化：InputNumber
            this.valueStartMin = min;
            this.valueStartMax = max;
            this.valueEndMin = min;
            this.valueEndMax = max;
            // 初始化: 讓 InputNumber 的範圍等於時間軸的範圍
            this.valueStart = min;
            this.valueEnd = max;
            break;
        }
        // 傳給後端
        this.attValueTypeStartEnd;
        // 建立圖表
        this.createChart();
      }
    },
    /**
     * set progress bar width
     * @param   {number} value
     * @returns {string}      樣式的寬度設定
     */
     progressWidth(value){
      return `width:${value}%;`
    },
    /**
     * Number to percentage
     * @param   {number} val
     * @returns {string}     轉換完成的百分比字串
     */
     getPercentLabel(val){
      if((val * 100).toFixed(1) >= 100) return `100%`;
      else return `${(val * 100).toFixed(1)}%`;
    },
    /**
     * 調整遮罩大小
     * @param {object} chart 取得 chart.js 資料
     */
    resizeMask(chart) {
      let from = (this.selectArea[0] * 0.01) / (this.selectRange * 0.01);
      let to = (this.selectArea[1] * 0.01) / (this.selectRange * 0.01);
      this.resizeLeftMask(chart, from);
      this.resizeRightMask(chart, to);
    },
    /**
     * 調整左邊遮罩大小
     * @param {object} chart 取得 chart.js 資料
     * @param {number} chart 取得 chart.js 資料
     */
    resizeLeftMask(chart, from) {
      const canvas = document.querySelector('#chartCanvasId canvas');
      const mask = document.getElementById("chart-mask-left");
      mask.style.left = `${canvas.offsetLeft + chart.chartArea.left}px`;
      mask.style.width = `${chart.chartArea.width * from}px`;
      mask.style.top = `${canvas.offsetTop + chart.chartArea.top}px`;
      mask.style.height = `${chart.chartArea.height}px`;
    },
    /**
     * 調整又邊遮罩大小
     * @param {object} chart 取得 chart.js 資料
     */
    resizeRightMask(chart, to) {
      const canvas = document.querySelector('#chartCanvasId canvas');
      const mask = document.getElementById("chart-mask-right");
      mask.style.left = `${canvas.offsetLeft + chart.chartArea.left + chart.chartArea.width * to}px`;
      mask.style.width = `${chart.chartArea.width * (1 - to)}px`;
      mask.style.top = `${canvas.offsetTop + chart.chartArea.top}px`;
      mask.style.height = `${chart.chartArea.height}px`;
    },
    /**
     * create chart
     * @param   {object} valueData 選取的 Attribute，包含後端原始數據的 type、key
     */
    createChart() {
      const valueData = this.valueData;
      const max = valueData.chart.y_axis.max * 1.1;
      const data = setLineChartData(valueData.chart.data, valueData.chart.x_axis.max, valueData.chart.x_axis.min);
      const isDateType = valueData.type === 'date';
      const minX = valueData.chart.x_axis.min;
      const maxX = valueData.chart.x_axis.max;
      let setChartData= {};
      let setChartOptions= {};
      let setLabels = [];

      switch (valueData.type) {
        case 'int':
          setLabels = data.map(item => Math.round(item.x));
          break;
        case 'float':
          setLabels = data.map((item, index) => {
            let x = index === 0 ? Math.floor(item.x * 100) / 100 :
            index === data.length - 1 ? item.x = Math.ceil(item.x * 100) / 100 :
            Math.round(item.x * 100) / 100;
            return x
          });
          break;
        case 'date':
          setLabels = this.labelsData;
          break;
        default:
          break;
      }
      setChartData = {
        datasets: [
          {
            label: 'Attribute Value',
            data: data,
            fill: 'start',
            showLine: false,
            tension: 0.4,
            backgroundColor: 'rgba(0,153,255)',
            pointRadius: 0,
          }
        ],
        labels: setLabels,
      };
      setChartOptions = {
        responsive: true,
        maintainAspectRatio: false,
        layout: {
          padding: {
            top: 16,
            left: 8,
            right: 8,
          }
        },
        plugins: {
          legend: false, // 圖例
          filler: {
            propagate: false
          },
          title: false
        },
        animation: {
          onComplete: e => {
            this.chartComplete = e.chart;
            this.resizeMask(e.chart);
          }
        },
        interaction: {
          intersect: true,
        },
        scales: {
          y: {
            beginAtZero: true, // scale 包含 0
            max: max,
            ticks: { // 設定間隔數值
              display: false, // 隱藏數值，只顯示格線
              stepSize: max / 4,
            },
            grid: {
              color: 'rgba(100,116,139)',
              z: 1,
            },
            border: {
              display: false, // 隱藏左側多出來的線
            }
          },
        },
      };
      if(isDateType) {
        setChartOptions.scales.x = {
          type: 'time',
          ticks: {
            min: minX,
            max: maxX,
            autoSkip: true, // 依比例判斷要不要換算時間單位
            maxRotation: 0, // 不旋轉 lable 0~50
            color: '#334155',
            display: true,
            source: 'labels', // 依比例彈性顯示 label 數量
          },
          grid: {
            display: false, // 隱藏 x 軸網格
          },
          time: {
            minUnit: 'day', // 顯示最小單位
            // displayFormats: {
            //   minute: 'HH:mm MMM d',
            //   hour: 'HH:mm MMM d',
            // }
          }
        }
      } else {
        setChartOptions.scales.x = {
          bounds: 'data',
          type: 'linear',
          min: minX,
          max: maxX,
          ticks: {
            autoSkip: true,
            maxRotation: 0, // 不旋轉 lable 0~50
            color: '#334155',
            callback: ((value, index, values) => {
              switch (valueData.type) {
                case 'int':
                  return Math.round(value);
                case 'float':
                  let x = index === 0 ? Math.floor(value * 100) / 100 :
                  index === values.length - 1 ? value = Math.ceil(value * 100) / 100 :
                  Math.round(value * 100) / 100;
                  return x
              }
            })
          },
          grid: {
            display: false, // 隱藏 x 軸網格
          },
        }
      }
      this.chartData = setChartData;
      this.chartOptions = setChartOptions;
    },
    /**
     * 滑塊改變的時候
     * @param {array} e [1, 100]
     */
     changeSelectArea(e) {
      // 日曆改變時，滑塊跟著改變
      let sliderData = this.sliderData;
      let start = sliderData[e[0].toFixed()];
      let end = sliderData[e[1].toFixed()]; // 取得 index，須為整數。

      switch (this.selectedAttName.type) {
        case 'date':
          this.startTime = new Date(start);
          this.endTime = new Date(end);
          // 重新設定 start end 日曆選取範圍
          this.endMinDate = new Date(start);
          this.startMaxDate = new Date(end);
          break;
          default:
            this.valueStart = start;
          this.valueEnd = end;
          // 重新設定 start end 日曆選取範圍
          this.valueEndMin = start;
          this.valueStartMax = end;
          break;
      }
      // 重新算圖
      this.resizeMask(this.chartComplete);
      // 執行 timeFrameStartEnd 才會改變數據
      this.attValueTypeStartEnd;
    },
    /**
     * 選取開始或結束時間時，要改變滑塊跟圖表
     * @param {object} e Tue Jan 25 2022 00:00:00 GMT+0800 (台北標準時間) | Blur Event
     * @param {string} direction start or end
     */
     sliderValueRange(e, direction) {
      // 找到最鄰近的 index，時間格式: 毫秒時間戳
      let sliderData = this.sliderData;
      let isDateType = this.selectedAttName.type === 'date';
      let targetTime = [];
      let inputValue;

      if(isDateType) targetTime = [new Date(this.attValueTypeStartEnd[0]).getTime(), new Date(this.attValueTypeStartEnd[1]).getTime()];
      else targetTime = [this.attValueTypeStartEnd[0], this.attValueTypeStartEnd[1]]

      const closestIndexes = targetTime.map(target => {
        let closestIndex = 0;
        closestIndex = ((target - sliderData[0])/(sliderData[sliderData.length-1]-sliderData[0])) * sliderData.length;
        let result = Math.round(Math.abs(closestIndex));
        result = result > this.selectRange ? this.selectRange : result;
        return result
      });
      // 改變滑塊
      this.selectArea = closestIndexes;
      // 重新設定 start end 日曆選取範圍
      if(!isDateType) inputValue = Number(e.value.replace(/,/g, '')) ;
      if(direction === 'start') isDateType ? this.endMinDate = e : this.valueEndMin = inputValue;
      else if(direction === 'end') isDateType ? this.startMaxDate = e : this.valueStartMax = inputValue;
      // 重新算圖
      if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) this.resizeMask(this.chartComplete);
      else return;
    },
  },
  created() {
    this.$emitter.on('map-filter-reset', value => {
      if(value) {
        this.selectedAttRange = null;
        if(this.valueData && this.valueTypes.includes(this.selectedAttName.type)){
          const min = this.valueData.min;
          const max = this.valueData.max;
          this.startTime = new Date(min);
          this.endTime = new Date(max);
          this.valueStart = min;
          this.valueEnd = max;
          this.selectArea = [0, this.selectRange];
          this.resizeMask(this.chartComplete);
        }
      }
    });
  },
  mounted() {
    // Slider
    this.selectArea = [0, this.selectRange]; // 初始化滑塊
  },
  beforeUnmount() {
    this.selectedAttName = {};
  }
}
</script>
