<template>
  <div class="image-style-panel">
    <div 
      class="origin-image"
      :style="{ backgroundImage: `url(${handleElement.src})` }"
    ></div>

    <ButtonGroup class="row">
      <Button style="flex: 5;" @click="clipImage()"><IconTailoring class="btn-icon" /> 裁剪图片</Button>
      <Popover trigger="click" v-model:visible="clipPanelVisible">
        <template #content>
          <div class="clip">
            <div class="title">按形状裁剪：</div>
            <div class="shape-clip">
              <div 
                class="shape-clip-item" 
                v-for="(item, index) in shapeClipPathOptions" 
                :key="index"
                @click="presetImageClip(index)"
              >
                <div class="shape" :style="{ backgroundImage: `url(${handleElement.src})`, clipPath: item.style }"></div>
              </div>
            </div>

            <template v-for="type in ratioClipOptions" :key="type.label">
              <div class="title" v-if="type.label">{{type.label}}：</div>
              <ButtonGroup class="row">
                <Button 
                  style="flex: 1;"
                  v-for="item in type.children"
                  :key="item.key"
                  @click="presetImageClip('rect', item.ratio)"
                >{{item.key}}</Button>
              </ButtonGroup>
            </template>
          </div>
        </template>
        <Button class="no-padding" style="flex: 1;"><IconDown /></Button>
      </Popover>
    </ButtonGroup>

    <Popover trigger="click">
      <template #content>
        <div class="filter">
          <div class="filter-item" v-for="filter in filterOptions" :key="filter.key">
            <div class="name">{{filter.label}}</div>
            <Slider
              class="filter-slider"
              :max="filter.max"
              :min="filter.min"
              :step="filter.step"
              :value="filter.value"
              @change="value => updateFilter(filter, value)"
            />
            <div class="value">{{`${filter.value}${filter.unit}`}}</div>
          </div>
        </div>
      </template>
      <Button class="full-width-btn"><IconColorFilter class="btn-icon" /> 设置滤镜</Button>
    </Popover>
    
    <div class="row">
      <div style="flex: 2;">水平翻转：</div>
      <div class="switch-wrapper" style="flex: 3;">
        <Switch 
          :checked="flip.x === 180" 
          @change="checked => updateImage({ flip: { x: checked ? 180 : 0, y: flip.y } })" 
        />
      </div>
    </div>
    <div class="row">
      <div style="flex: 2;">垂直翻转：</div>
      <div class="switch-wrapper" style="flex: 3;">
        <Switch 
          :checked="flip.y === 180" 
          @change="checked => updateImage({ flip: { x: flip.x, y: checked ? 180 : 0 } })" 
        />
      </div>
    </div>

    <Divider />
    <ElementOutline />
    <Divider />
    <ElementShadow />
    <Divider />
    
    <FileInput @change="files => replaceImage(files)">
      <Button class="full-width-btn"><IconTransform class="btn-icon" /> 替换图片</Button>
    </FileInput>
    <Button class="full-width-btn" @click="resetImage()"><IconUndo class="btn-icon" /> 重置样式</Button>
    <Button class="full-width-btn" @click="setBackgroundImage()"><IconTheme class="btn-icon" /> 设为背景</Button>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, ref, watch } from 'vue'
import { MutationTypes, useStore } from '@/store'
import { PPTImageElement, Slide } from '@/types/slides'
import { CLIPPATHS } from '@/configs/imageClip'
import { getImageDataURL } from '@/utils/image'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'

import ElementOutline from '../common/ElementOutline.vue'
import ElementShadow from '../common/ElementShadow.vue'

interface FilterOption {
  label: string;
  key: string;
  default: number;
  value: number;
  unit: string;
  max: number;
  step: number;
}

const defaultFilters: FilterOption[] = [
  { label: '模糊', key: 'blur', default: 0, value: 0, unit: 'px', max: 10, step: 1 },
  { label: '亮度', key: 'brightness', default: 100, value: 100, unit: '%', max: 200, step: 5 },
  { label: '对比度', key: 'contrast', default: 100, value: 100, unit: '%', max: 200, step: 5 },
  { label: '灰度', key: 'grayscale', default: 0, value: 0, unit: '%', max: 100, step: 5 },
  { label: '饱和度', key: 'saturate', default: 100, value: 100, unit: '%', max: 200, step: 5 },
  { label: '色相', key: 'hue-rotate', default: 0, value: 0, unit: 'deg', max: 360, step: 10 },
  { label: '不透明度', key: 'opacity', default: 100, value: 100, unit: '%', max: 100, step: 5 },
]

const shapeClipPathOptions = CLIPPATHS
const ratioClipOptions = [
  {
    label: '纵横比（方形）',
    children: [
      { key: '1:1', ratio: 1 / 1 },
    ],
  },
  {
    label: '纵横比（纵向）',
    children: [
      { key: '2:3', ratio: 3 / 2 },
      { key: '3:4', ratio: 4 / 3 },
      { key: '3:5', ratio: 5 / 3 },
      { key: '4:5', ratio: 5 / 4 },
    ],
  },
  {
    label: '纵横比（横向）',
    children: [
      { key: '3:2', ratio: 2 / 3 },
      { key: '4:3', ratio: 3 / 4 },
      { key: '5:3', ratio: 3 / 5 },
      { key: '5:4', ratio: 4 / 5 },
    ],
  },
  {
    children: [
      { key: '16:9', ratio: 9 / 16 },
      { key: '16:10', ratio: 10 / 16 },
    ],
  },
]

export default defineComponent({
  name: 'image-style-panel',
  components: {
    ElementOutline,
    ElementShadow,
  },
  setup() {
    const store = useStore()
    const handleElement = computed<PPTImageElement>(() => store.getters.handleElement)
    const currentSlide = computed<Slide>(() => store.getters.currentSlide)

    const clipPanelVisible = ref(false)

    const flip = ref({
      x: 0,
      y: 0,
    })

    const filterOptions = ref<FilterOption[]>(JSON.parse(JSON.stringify(defaultFilters)))

    watch(handleElement, () => {
      if(!handleElement.value || handleElement.value.type !== 'image') return

      if(handleElement.value.flip) {
        flip.value = {
          x: handleElement.value.flip.x || 0,
          y: handleElement.value.flip.y || 0,
        }
      }
      else flip.value = { x: 0, y: 0 }

      const filters = handleElement.value.filters
      if(filters) {
        filterOptions.value = defaultFilters.map(item => {
          if(filters[item.key] !== undefined) return { ...item, value: parseInt(filters[item.key]) }
          return item
        })
      }
      else filterOptions.value = JSON.parse(JSON.stringify(defaultFilters))
    }, { deep: true, immediate: true })

    const { addHistorySnapshot } = useHistorySnapshot()

    const updateImage = (props: Partial<PPTImageElement>) => {
      store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
      addHistorySnapshot()
    }

    const updateFilter = (filter: FilterOption, value: number) => {
      const originFilters = handleElement.value.filters || {}
      const filters = { ...originFilters, [filter.key]: `${value}${filter.unit}` }
      const props = { filters }
      store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
      addHistorySnapshot()
    }

    const clipImage = () => {
      store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, handleElement.value.id)
      clipPanelVisible.value = false
    }

    const getImageElementDataBeforeClip = () => {
      // 图片当前宽高位置、裁剪范围
      const imgWidth = handleElement.value.width
      const imgHeight = handleElement.value.height
      const imgLeft = handleElement.value.left
      const imgTop = handleElement.value.top
      const originClipRange = handleElement.value.clip ? handleElement.value.clip.range : [[0, 0], [100, 100]]

      // 图片原本未裁剪过时的宽高位置
      const originWidth = imgWidth / ((originClipRange[1][0] - originClipRange[0][0]) / 100)
      const originHeight = imgHeight / ((originClipRange[1][1] - originClipRange[0][1]) / 100)
      const originLeft = imgLeft - originWidth * (originClipRange[0][0] / 100)
      const originTop = imgTop - originHeight * (originClipRange[0][1] / 100)

      return {
        originClipRange,
        originWidth,
        originHeight,
        originLeft,
        originTop,
      }
    }

    const presetImageClip = (shape: string, ratio = 0) => {
      const {
        originClipRange,
        originWidth,
        originHeight,
        originLeft,
        originTop,
      } = getImageElementDataBeforeClip()
      
      // 设置形状和纵横比
      if(ratio) {
        const imageRatio = originHeight / originWidth

        const min = 0
        const max = 100
        let range

        if(imageRatio > ratio) {
          const distance = ((1 - ratio / imageRatio) / 2) * 100
          range = [[min, distance], [max, max - distance]]
        }
        else {
          const distance = ((1 - imageRatio / ratio) / 2) * 100
          range = [[distance, min], [max - distance, max]]
        }
        store.commit(MutationTypes.UPDATE_ELEMENT, {
          id: handleElement.value.id,
          props: {
            clip: { ...handleElement.value.clip, shape, range },
            left: originLeft + originWidth * (range[0][0] / 100),
            top: originTop + originHeight * (range[0][1] / 100),
            width: originWidth * (range[1][0] - range[0][0]) / 100,
            height: originHeight * (range[1][1] - range[0][1]) / 100,
          },
        })
      }
      // 仅设置形状（维持目前的裁剪范围）
      else {
        store.commit(MutationTypes.UPDATE_ELEMENT, {
          id: handleElement.value.id,
          props: {
            clip: { ...handleElement.value.clip, shape, range: originClipRange }
          },
        })
      }
      clipImage()
      addHistorySnapshot()
    }

    const replaceImage = (files: File[]) => {
      const imageFile = files[0]
      if(!imageFile) return
      getImageDataURL(imageFile).then(dataURL => {
        const props = { src: dataURL }
        store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
      })
      addHistorySnapshot()
    }

    const resetImage = () => {
      if(handleElement.value.clip) {
        const {
          originWidth,
          originHeight,
          originLeft,
          originTop,
        } = getImageElementDataBeforeClip()

        store.commit(MutationTypes.UPDATE_ELEMENT, {
          id: handleElement.value.id,
          props: {
            left: originLeft,
            top: originTop,
            width: originWidth,
            height: originHeight,
          },
        })
      }

      store.commit(MutationTypes.REMOVE_ELEMENT_PROPS, {
        id: handleElement.value.id,
        propName: ['clip', 'outline', 'flip', 'shadow', 'filters'],
      })
      addHistorySnapshot()
    }

    const setBackgroundImage = () => {
      const background = {
        ...currentSlide.value.background,
        type: 'image',
        image: handleElement.value.src,
        imageSize: 'cover',
      }
      store.commit(MutationTypes.UPDATE_SLIDE, { background })
      addHistorySnapshot()
    }

    return {
      clipPanelVisible,
      shapeClipPathOptions,
      ratioClipOptions,
      filterOptions,
      flip,
      handleElement,
      updateImage,
      updateFilter,
      clipImage,
      presetImageClip,
      replaceImage,
      resetImage,
      setBackgroundImage,
    }
  },
})
</script>

<style lang="scss" scoped>
.row {
  width: 100%;
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}
.switch-wrapper {
  text-align: right;
}
.origin-image {
  height: 100px;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  background-color: $lightGray;
  margin-bottom: 10px;
}
.full-width-btn {
  width: 100%;
  margin-bottom: 10px;
}
.btn-icon {
  margin-right: 3px;
}

.filter {
  width: 280px;
  font-size: 12px;
}
.filter-item {
  padding: 8px 5px;
  display: flex;
  justify-content: center;
  align-items: center;

  .name {
    width: 60px;
  }
  .filter-slider {
    flex: 1;
    margin: 0 6px;
  }
  .value {
    width: 40px;
    text-align: right;
  }
}

.clip {
  width: 260px;
  font-size: 12px;

  .title {
    margin-bottom: 5px;
  }
}
.shape-clip {
  margin-bottom: 10px;

  @include grid-layout-wrapper();
}
.shape-clip-item {
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;

  @include grid-layout-item(5, 16%);

  .shape {
    width: 40px;
    height: 40px;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
  }
}
</style>