<template>
  <div class="sel-single-color">
    <span v-if="readonly" class="color-view" :style="{ background: useValue }"></span>
    <s-popper v-else placement="right">
      <template #reference>
        <span class="color-view" :style="{ background: useValue }"></span>
      </template>
      <template #popper>
        <div class="sel-tooltip">
          <div class="tab" v-if="use==='all'">
            <div
              class="item"
              v-if="use==='all'"
              :class="{ active: colorType === 'pure' }"
              @click="colorType = 'pure'"
            >纯色
            </div>
            <div
              class="item"
              v-if="use==='all'"
              :class="{ active: colorType === 'gradient' }"
              @click="colorType = 'gradient'"
            >
              渐变色
            </div>
          </div>
          <color-picker
            v-if="(use==='all'||use==='pure')&&colorType === 'pure'"
            :color="pureColor"
            :on-end-change="color => onChangeColor(color)"
          />
          <color-picker
            v-if="(use==='all'||use==='gradient')&&colorType === 'gradient'"
            :gradient="gradientColor"
            :is-gradient="true"
            :on-change="color => onChangeColor(color)"
          />
        </div>
      </template>
    </s-popper>
  </div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, PropType } from 'vue'
import ColorPicker from './color-picker'
import { colorToRgb, generateGradientStyle } from './color-picker/helpers'
import gradient from 'gradient-parser'
import { ColorGradient, ColorRGBA, EChartGradient } from '../color.type'
import SPopper from '../../Popper/src/index.vue'

defineOptions({
  name: 'SSingeColor'
})

interface Props {
  modelValue: string | []
  type: 'echarts' | 'css'
  readonly: boolean
  use: 'pure' | 'gradient' | 'all'
}

const props = withDefaults(defineProps<Props>(), {
  type: 'css',
  readonly: false,
  use: 'all'
})
const emits = defineEmits<{
  ( e: 'update:modelValue', value: any ): void
  ( e: 'change', value: any ): void
}>()
const colorType = ref('pure')
const useValue = computed(() => {
  if (typeof props.modelValue === 'string') {
    return props.modelValue
  } else if (props.modelValue && Object.keys(props.modelValue).length > 0) {
    const colorObj = parseEChartGradientToColorGradient(
      props.modelValue as unknown as EChartGradient
    )
    return generateGradientStyle(
      colorObj.points,
      colorObj.type,
      colorObj.degree
    )
  } else {
    return ''
  }
})
const pureColor = computed<ColorRGBA>(() => {
  if (typeof useValue.value === 'object' || !useValue.value || useValue.value?.indexOf('gradient') >= 0) {
    return {
      red: 0,
      green: 0,
      blue: 0,
      alpha: 1
    }
  } else {
    return colorToRgb(useValue.value)
  }
})

const gradientColor = computed(() => {
  if (typeof useValue.value === 'object' && Object.keys(useValue.value).length > 0) {
    return parseEChartGradientToColorGradient(
      useValue.value as unknown as EChartGradient
    )
  } else {
    if (!useValue.value || useValue.value.indexOf('gradient') < 0) {
      return getDefaultColorGradient()
    } else {
      return parseCssGradientToColorGradient(useValue.value)
    }
  }
})

function getDefaultColorGradient<ColorGradient> () {
  return {
    type: 'linear',
    degree: 0,
    points: [
      {
        left: 0,
        red: 0,
        green: 0,
        blue: 0,
        alpha: 1
      },
      {
        left: 100,
        red: 0,
        green: 0,
        blue: 0,
        alpha: 1
      }
    ]
  }
}

function parseCssGradientToColorGradient ( color: string ): ColorGradient {
  const colorObj: any = gradient.parse(color)[0]
  return {
    type: colorObj.type.split('-')[0],
    degree: colorObj.orientation && colorObj.orientation.value,
    points: colorObj.colorStops?.map(( item: any ) => {
      return {
        left: Number(item.length.value),
        red: item.value[0],
        green: item.value[1],
        blue: item.value[2],
        alpha: item.value[3] ?? 1
      }
    })
  }
}

// parse echarts gradient to color gradient
function parseEChartGradientToColorGradient (
  colorObj: EChartGradient
): ColorGradient {
  let x = Number(colorObj.x)
  let y = Number(colorObj.y)
  let x2 = Number(colorObj.x2)
  let y2 = Number(colorObj.y2)
  if (x === 1) {
    x = x - 1
    y = y - 1
    x2 = x2 - 1
    y2 = y2 - 1
  }
  const dtX = x2 - x
  const dtY = y2 - y
  const calcDeg = (Math.atan(dtY / dtX) * 180) / Math.PI
  const deg = x2 >= 0 ? 90 + calcDeg : 270 + calcDeg
  return {
    type: colorObj.type,
    degree: Math.floor(deg),
    points: colorObj.colorStops?.map(( item: any ) => {
      return {
        left: item.offset * 100,
        ...colorToRgb(item.color)
      }
    })
  }
}

function parseColorGradientToEChartGradient (
  colorObj: ColorGradient
): EChartGradient {
  let deg = Number(colorObj.degree ?? 0)
  let x = 0
  let y = 0
  let x2 = Math.cos(((deg - 90) * Math.PI) / 180)
  let y2 = Math.sin(((deg - 90) * Math.PI) / 180)
  if (x2 < 0) {
    x = x + 1
    y = y + 1
    x2 = x2 + 1
    y2 = y2 + 1
  }
  return {
    type: colorObj.type,
    global: false,
    x: Number(x.toFixed(4)),
    y: Number(y.toFixed(4)),
    x2: Number(x2.toFixed(4)),
    y2: Number(y2.toFixed(4)),
    colorStops: colorObj.points?.map(( item: any ) => {
      return {
        offset: item.left / 100,
        color: `rgba(${ item.red },${ item.green },${ item.blue },${ item.alpha })`
      }
    })
  }
}

onMounted(() => {
  if (typeof useValue.value === 'string') {
    colorType.value = useValue.value.indexOf('gradient') >= 0 ? 'gradient' : 'pure'
  } else {
    colorType.value = 'gradient'
  }
})

function onChangeColor ( color ) {
  if (colorType.value === 'pure') {
    emits('update:modelValue', color.style)
    emits('change', color.style)
  } else if (props.type === 'css') {
    emits('update:modelValue', color.style)
    emits('change', color.style)
  } else {
    emits('update:modelValue', parseColorGradientToEChartGradient(color))
    emits('change', parseColorGradientToEChartGradient(color))
  }
}
</script>
<style lang="scss">
.vc-colorpicker {
  box-shadow: none !important;
  padding-bottom: 0;

  .vc-colorpicker--container {
    padding-top: 0;

    .vc-gradient-picker__body {
      margin-top: 5px;
    }
  }
}
</style>
<style lang="scss">
.sel-tooltip {
  .tab {
    font-size: 12px;
    display: flex;
    justify-content: space-between;
    margin-top: 5px;

    .item {
      padding: 5px 10px;
      line-height: 14px;
      border-bottom: 2px solid transparent;
      flex: 1;
      margin: 0 10px;
      text-align: center;
      color: #999;

      &.active {
        color: #00a0e9;
        border-bottom: 2px solid #00a0e9;
      }
    }
  }
}

.sel-single-color {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 24px;

  .color-view {
    width: 20px;
    height: 20px;
    display: inline-block;
    border: 2px solid #fff;
    box-shadow: 0 0 1px #000;
    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
    background-size: 10px;
  }
}

.single-color + .single-color {
  margin-left: 5px;
}
</style>
