<template>
  <div class="text-style-panel">
    <ElementPosition/>
    <el-divider style="margin: 12px 0"/>
    <el-row>
      <el-col :span="12">
        <el-select v-model="elementFontFamily" placement="left" @change="handleElementFontFamily">
          <el-option-group v-for="group in fontOptionGroups" :key="group.label" :label="group.label">
            <template v-if="group.type == 0">
              <el-option v-for="item in group.options" :key="item" :value="item.value" :label="item.label"
                         :style="{fontFamily: item.value}"></el-option>
            </template>
            <template v-else>
              <el-option v-for="item in group.options" :key="item.id" :value="item.fontFamily" :label="item.fontName">
                <el-image style="height: 18px; width: 90px" :src="item.fontThumbUrl"></el-image>
              </el-option>
            </template>
          </el-option-group>
        </el-select>
      </el-col>
      <el-col :span="12">
        <el-select
            v-model="handleElement.fontSize"
            filterable
            allow-create
            default-first-option
            :filterMethod="handleElementInputSize"
            placement="left"
            @change="handleElementFontSize"
        >
          <el-option v-for="item in FontSizeLibs" :key="item" :label="item" :value="item"></el-option>
        </el-select>
      </el-col>
    </el-row>

    <el-row class="mt-10">
      <el-col :span="6">
        <el-tooltip placement="top" content="文字颜色" :hide-after="0">
          <div @click.stop class="tooltip-popover">
            <el-popover trigger="click" placement="bottom" :width="265" @click.stop>
              <template #reference>
                <el-button class="font-color">
                  <TextColorButton :color="handleElement.color">
                    <IconText/>
                  </TextColorButton>
                </el-button>
              </template>
              <ColorPicker :modelValue="handleElement.color"
                           @update:modelValue="(color: string) => updateFontColor(color)"/>
            </el-popover>
          </div>
        </el-tooltip>
      </el-col>
      <el-col :span="6">
        <el-tooltip placement="top" content="背景颜色" :hide-after="0">
          <div @click.stop class="tooltip-popover">
            <el-popover trigger="click" placement="bottom" :width="265">
              <template #reference>
                <el-button class="high-light">
                  <TextColorButton :color="elementBackgrounColor">
                    <IconHighLight/>
                  </TextColorButton>
                </el-button>
              </template>
              <ColorPicker :modelValue="elementBackgrounColor"
                           @update:modelValue="(color: string) => updateBackgroundColor(color)"/>
            </el-popover>
          </div>
        </el-tooltip>
      </el-col>
      <el-col :span="12">
        <el-button-group class="full-group">
          <el-tooltip placement="top" content="增大字号" :hide-after="0">
            <el-button class="font-size" @click="handleElementFontsize('+')">
              <IconFontSize/>
              +
            </el-button>
          </el-tooltip>

          <el-tooltip placement="top" content="减小字号" :hide-after="0">
            <el-button @click="handleElementFontsize('-')">
              <IconFontSize/>
              -
            </el-button>
          </el-tooltip>
        </el-button-group>
      </el-col>
    </el-row>

    <el-row class="mt-10">
      <div class="full-checkbox">
        <el-tooltip placement="top" content="加粗" :hide-after="0">
          <el-checkbox-button :value="hasFontWeight" @change="handleElementBlod()">
            <IconTextBold/>
          </el-checkbox-button>
        </el-tooltip>
        <el-tooltip placement="top" content="斜体" :hide-after="0">
          <el-checkbox-button v-model="hasFontStyle" @change="handleElementItalic()">
            <IconTextItalic/>
          </el-checkbox-button>
        </el-tooltip>
        <el-tooltip placement="top" content="下划线" :hide-after="0">
          <el-checkbox-button v-model="hasUnderline" @change="handleElementUnderline()">
            <IconTextUnderline/>
          </el-checkbox-button>
        </el-tooltip>
        <el-tooltip placement="top" content="删除线" :hide-after="0">
          <el-checkbox-button v-model="hasLinethrough" @change="handleElementLinethrough()">
            <IconStrikethrough/>
          </el-checkbox-button>
        </el-tooltip>
      </div>
    </el-row>

    <el-row class="mt-10">
      <el-button-group class="full-group">
        <el-tooltip placement="top" content="横向" :hide-after="0">
          <el-button @click="handleElementArrange(false)" :type="elementGrapheme ? 'primary': ''">
            <IconTextRotationNone/>
          </el-button>
        </el-tooltip>
        <el-tooltip placement="top" content="纵向" :hide-after="0">
          <el-button @click="handleElementArrange(true)" :type="!elementGrapheme ? 'primary': ''">
            <IconTextRotationDown/>
          </el-button>
        </el-tooltip>
        <el-tooltip placement="top" content="减小缩进" :hide-after="0">
          <el-button @click="handleElementCharSpacing('-')">
            <IconIndentLeft/>
          </el-button>
        </el-tooltip>
        <el-tooltip placement="top" content="增大缩进" :hide-after="0">
          <el-button @click="handleElementCharSpacing('+')">
            <IconIndentRight/>
          </el-button>
        </el-tooltip>
      </el-button-group>
    </el-row>

    <el-row class="mt-10">
      <el-col :span="24">
        <el-radio-group class="full-ratio" v-model="textAlign" @change="handleTextAlign">
          <el-tooltip placement="top" content="左对齐" :hide-after="0">
            <el-radio-button value="justify-left">
              <IconAlignTextLeft/>
            </el-radio-button>
          </el-tooltip>
          <el-tooltip placement="top" content="居中" :hide-after="0">
            <el-radio-button value="justify-center">
              <IconAlignTextCenter/>
            </el-radio-button>
          </el-tooltip>
          <el-tooltip placement="top" content="右对齐" :hide-after="0">
            <el-radio-button value="justify-right">
              <IconAlignTextRight/>
            </el-radio-button>
          </el-tooltip>
          <el-tooltip placement="top" content="两边对齐" :hide-after="0">
            <el-radio-button value="justify">
              <IconAlignTextBoth/>
            </el-radio-button>
          </el-tooltip>
        </el-radio-group>
      </el-col>
    </el-row>

    <el-row class="mt-10">
      <el-col :span="12">
        <el-tooltip placement="top" content="转曲" :hide-after="0">
          <el-button class="full-button" @click="handleElementCurveEx">
            <IconTextStyleOne/>
          </el-button>
        </el-tooltip>
      </el-col>
      <el-col :span="12">
        <el-tooltip placement="top" content="变形" :hide-after="0">
          <el-button class="full-button"
                     :type="handleElement.type.toLowerCase() === ElementNames.ARCTEXT ? 'primary' : ''"
                     @click="handleElementDeformation">
            <i class="handler-item iconfont icon-text-path"/>
          </el-button>
        </el-tooltip>
      </el-col>
    </el-row>

    <el-row class="mt-10" v-show="handleElement.type.toLowerCase() === ElementNames.ARCTEXT">
      <el-col :span="4" class="flex-align">
        <el-radio-group class="full-ratio" v-model="(handleElement as ArcText).showCurvature"
                        @change="changeArcTextStatus">
          <el-tooltip placement="top" content="隐藏弧度" :hide-after="0"
                      v-if="(handleElement as ArcText).showCurvature">
            <el-radio-button :value="false">
              <IconPreviewClose/>
            </el-radio-button>
          </el-tooltip>
          <el-tooltip placement="top" content="显示弧度" :hide-after="0" v-else>
            <el-radio-button :value="true">
              <IconPreviewOpen/>
            </el-radio-button>
          </el-tooltip>
        </el-radio-group>
      </el-col>
      <el-col :span="1"></el-col>
      <el-col :span="12" class="flex-align">
        <el-slider :min="66" :max="1000" :step="1" v-model="(handleElement as ArcText).radius"
                   @change="changeArcTextRadius" size="small"></el-slider>
      </el-col>
      <el-col :span="1"></el-col>
      <el-col :span="6" class="flex-align">
        <el-input :min="1" :max="10" v-model="(handleElement as ArcText).radius" controls-position="right"
                  size="default"/>
      </el-col>
    </el-row>

    <el-divider style="margin: 12px 0"/>
    <ElementFill/>
    <el-divider style="margin: 12px 0"/>

    <div class="row">
      <div style="flex: 2;">行距：</div>
      <el-select style="flex: 3" suffix-icon="IconRowHeight" v-model="handleElement.lineHeight"
                 @change="changeLineHeight">
        <el-option v-for="item in LineHeightLibs" :key="item" :value="item" :label="item"></el-option>
      </el-select>
      <div style="flex: 1;"></div>
      <div style="flex: 2;">字距：</div>
      <el-select style="flex: 3" suffix-icon="IconFullwidth" v-model="handleElement.charSpacing"
                 @change="changeCharSpacing">
        <el-option v-for="item in CharSpaceLibs" :key="item" :value="item" :label="item"></el-option>
      </el-select>
    </div>

    <el-divider style="margin: 12px 0"/>
    <ElementEffects/>
    <el-divider style="margin: 12px 0"/>
    <ElementStroke :hasStroke="hasStroke"/>
    <el-divider style="margin: 12px 0"/>
    <ElementShadow :hasShadow="hasShadow"/>
    <el-divider style="margin: 12px 0"/>
    <ElementPatterns :hasPatterns="hasPatterns"/>
    <el-divider style="margin: 12px 0"/>
    <ElementOpacity/>
  </div>
</template>

<script lang="ts" setup>
import {computed, ref, onMounted} from 'vue'
import {useMainStore, useTemplatesStore} from '@/store'
import {storeToRefs} from 'pinia'
import {ElMessage} from 'element-plus'
import {FabricObject, IText, Textbox} from 'fabric'
import {FontSizeLibs, LineHeightLibs, CharSpaceLibs} from '@/configs/texts'
import {WEB_FONTS} from '@/configs/fonts'
import {propertiesToInclude} from '@/configs/canvas'
import {TextboxElement} from '@/types/canvas'
import {ElementNames, FontGroupOption} from '@/types/elements'
import {loadFont} from '@/utils/fonts'
import {nanoid} from 'nanoid'
import {ArcText} from '@/extension/object/ArcText'
import {CurvedText} from '@/extension/object/CurvedText'
import {VerticalText} from '@/extension/object/VerticalText'
import opentype from "opentype.js"
import ElementPosition from '../Components/ElementPosition.vue'
import ElementStroke from '../Components/ElementStroke.vue'
import ElementShadow from '../Components/ElementShadow.vue'
import ElementOpacity from '../Components/ElementOpacity.vue'
import ElementPatterns from '../Components/ElementPatterns.vue'
import ElementEffects from '../Components/ElementEffects.vue'
import ElementFill from '../Backgrounds/ElementFill.vue'
import useHandleCreate from "@/hooks/useHandleCreate"
import useCanvas from '@/views/Canvas/useCanvas'


const mainStore = useMainStore()
const templatesStore = useTemplatesStore()
const {canvasObject, systemFonts, onlineFonts} = storeToRefs(mainStore)
const {createPathElement} = useHandleCreate()
const [canvas] = useCanvas()
const handleElement = computed(() => canvasObject.value as Textbox | ArcText)
const elementGrapheme = computed(() => handleElement.value.type.toLowerCase() !== ElementNames.VERTICALTEXT)
const elementBackgrounColor = computed(() => {
  if (handleElement.value.type.toLowerCase() === ElementNames.ARCTEXT) {
    return handleElement.value.textBackgroundColor
  }
  return handleElement.value.backgroundColor
})
const hasFontFamily = computed(() => handleElement.value.fontFamily)
const hasFontWeight = computed(() => handleElement.value.fontWeight !== 'normal')
const hasFontStyle = computed(() => handleElement.value.fontStyle !== 'normal')
const hasUnderline = computed(() => handleElement.value.underline)
const hasLinethrough = computed(() => handleElement.value.linethrough)
const textAlign = computed(() => handleElement.value.textAlign)
const hasStroke = computed(() => handleElement.value.stroke ? true : false)
const hasShadow = computed(() => handleElement.value.shadow ? true : false)
const hasPatterns = computed(() => (handleElement.value as TextboxElement).fillType === 1 ? true : false)
const elementFontFamily = ref<string>(hasFontFamily.value)
const fontOptionGroups = ref<FontGroupOption[]>([
  {
    type: 0,
    label: '系统字体',
    options: systemFonts.value
  },
  {
    type: 1,
    label: '在线字体',
    options: onlineFonts.value
  }
])

// 修改字体族
const handleElementFontFamily = (fontFamily: string) => {
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({fontFamily})
  } else {
    templatesStore.modifedElement(handleElement.value, {fontFamily})
  }
  canvas.renderAll()
}

// 修改输入字体大小
const handleElementInputSize = (val: string) => {
  val = val.replace(/[^\d]/g, '')
  if (val) {
    templatesStore.modifedElement(handleElement.value, {fontSize: val})
  }
}

// 修改字体大小
const handleElementFontSize = (fontSize: string) => {
  fontSize = fontSize.replace(/[^\d]/g, '')
  if (!fontSize) return
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({fontSize})
  } else {
    templatesStore.modifedElement(handleElement.value, {fontSize})
  }
  canvas.renderAll()
}

// 修改字体颜色
const updateFontColor = (fill: string) => {

  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({fill})
  } else {
    templatesStore.modifedElement(handleElement.value, {fill, color: fill})
  }
}

// 修改背景颜色
const updateBackgroundColor = (backgroundColor: string) => {
  let changeData: Record<string, any> = {backgroundColor}
  if (handleElement.value.type.toLowerCase() === ElementNames.ARCTEXT) {
    changeData = {'textBackgroundColor': backgroundColor}
  }
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles(changeData)
  } else {
    templatesStore.modifedElement(handleElement.value, changeData)
  }
}

// 修改字体大小
const handleElementFontsize = (mode: string) => {
  if (handleElement.value.fontSize <= 6) return
  const fontSize = mode === '+' ? handleElement.value.fontSize + 1 : handleElement.value.fontSize - 1
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({fontSize})
  } else {
    templatesStore.modifedElement(handleElement.value, {fontSize})
  }
  canvas.renderAll()
}

// 修改字体加粗
const handleElementBlod = () => {
  const fontBold = 'bold', fontNormal = 'normal'
  if (handleElement.value.isEditing) {
    const blodState = handleElement.value.getSelectionStyles().find(item => item.fontWeight !== fontBold)
    if (!blodState || (JSON.stringify(blodState) === '{}' && handleElement.value.fontWeight === fontBold)) {
      handleElement.value.setSelectionStyles({'fontWeight': fontNormal})
    } else {
      handleElement.value.setSelectionStyles({'fontWeight': fontBold})
    }
  } else {
    const elementStyle = handleElement.value.styles
    if (handleElement.value.fontWeight === fontBold) {
      templatesStore.modifedElement(handleElement.value, {fontWeight: fontNormal})
      for (let i in elementStyle) {
        for (let j in elementStyle[i]) {
          (elementStyle[i][j] as TextboxElement).set({fontWeight: fontNormal})
        }
      }
    } else {
      templatesStore.modifedElement(handleElement.value, {fontWeight: fontBold})
      for (let i in elementStyle) {
        for (let j in elementStyle[i]) {
          (elementStyle[i][j] as TextboxElement).set({fontWeight: fontBold})
          // elementStyle[i][j].fontWeight = fontBold
        }
      }
    }
  }
}

// 修改斜体
const handleElementItalic = () => {
  const fontStyle = handleElement.value.fontStyle === 'italic' ? 'normal' : 'italic'

  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({fontStyle})
  } else {
    templatesStore.modifedElement(handleElement.value, {fontStyle})
  }

}

// 修改删除线
const handleElementLinethrough = () => {
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({linethrough: !handleElement.value.linethrough})
  } else {
    templatesStore.modifedElement(handleElement.value, {linethrough: !handleElement.value.linethrough})
  }
}

// 修改中划线
const handleElementUnderline = () => {
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({underline: !handleElement.value.underline})
  } else {
    templatesStore.modifedElement(handleElement.value, {underline: !handleElement.value.underline})
  }
}

// 修改字体居中
const handleTextAlign = (textAlign: string) => {
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({textAlign})
  } else {
    templatesStore.modifedElement(handleElement.value, {textAlign})
  }
}

// 修改缩进
const handleElementCharSpacing = (mode: '+' | '-') => {
  const handleCharSpacing = handleElement.value.charSpacing
  const charSpacing = mode === '+' ? handleCharSpacing + 10 : handleCharSpacing - 10
  templatesStore.modifedElement(handleElement.value, {charSpacing})
}

const changeLineHeight = (lineHeight: number) => {
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({lineHeight: lineHeight})
  } else {
    templatesStore.modifedElement(handleElement.value, {lineHeight: lineHeight})
  }
}

const changeCharSpacing = (charSpacing: number) => {
  if (handleElement.value.isEditing) {
    handleElement.value.setSelectionStyles({charSpacing: charSpacing})
  } else {
    templatesStore.modifedElement(handleElement.value, {charSpacing: charSpacing})
  }

  canvas.renderAll()
}

const handleElementArrange = (status: boolean) => {
  const options = (handleElement.value as any).toObject(propertiesToInclude as any[])
  options.lineHeight = 12
  delete options.type
  options.id = nanoid(10)
  let textElement: FabricObject = new Textbox(handleElement.value.text, options)
  if (status) {
    textElement = new VerticalText(handleElement.value.text, options)
  }
  const activeObject = canvas.getActiveObject()
  if (activeObject) canvas.remove(activeObject)
  canvas.discardActiveObject()
  canvas.add(textElement)
  templatesStore.addElement(textElement)
  canvas.setActiveObject(textElement)
  mainStore.setCanvasObject(textElement)
  canvas.renderAll()
}

const handleElementCurve = async () => {
  // ElMessage
  let fontElement: opentype.Font | undefined
  if (WEB_FONTS.filter(item => item.value === hasFontFamily.value)[0]) {
    const fontURL = import.meta.env.MODE === 'production' ? `/assets/${hasFontFamily.value}.ttf` : `/src/assets/fonts/${hasFontFamily.value}.ttf`
    fontElement = await opentype.load(fontURL)
  } else {
    const fontData = await loadFont(hasFontFamily.value)
    if (!fontData) return
    const fontBlob = await fontData.blob()
    const fontBuffer = await fontBlob.arrayBuffer()
    fontElement = opentype.parse(fontBuffer)
  }
  if (!fontElement) return
  const path = fontElement.getPath(handleElement.value.text, 0, 0, handleElement.value.fontSize);
  createPathElement(path.toPathData(2), handleElement.value.left, handleElement.value.top)
  canvas.remove(handleElement.value)
  canvas.renderAll()
}

const handleElementCurveEx = async () => {
  // ElMessage
  let fontElement: opentype.Font | undefined
  const currentFont = onlineFonts.value.filter(item => item.fontFamily === hasFontFamily.value)[0]
  if (currentFont) {
    const fontURL = currentFont.fontFilePath
    fontElement = await opentype.load(fontURL)
  } else {
    const fontData = await loadFont(hasFontFamily.value)
    if (!fontData) return
    const fontBlob = await fontData.blob()
    const fontBuffer = await fontBlob.arrayBuffer()
    fontElement = opentype.parse(fontBuffer)
  }
  if (!fontElement) return
  const path = fontElement.getPath(handleElement.value.text, 0, 0, handleElement.value.fontSize);
  createPathElement(path.toPathData(2), handleElement.value.left, handleElement.value.top)
  canvas.remove(handleElement.value)
  canvas.renderAll()
}

const handleElementDeformation = () => {
  const options = (handleElement.value as any).toObject(propertiesToInclude as any[]) as any
  options.originType = options.type
  delete options.type
  options.id = nanoid(10)
  let text
  if (handleElement.value.type.toLowerCase() === ElementNames.ARCTEXT) {
    text = new IText(options.text, options)
  } else {
    text = new ArcText(options.text, options)
  }
  canvas.add(text)
  handleElement.value.set({visible: false})
  templatesStore.addElement(text)
  canvas.setActiveObject(text)
  canvas.renderAll()
}

const changeArcTextRadius = (val: number) => {
  (handleElement.value as ArcText).setRadius(val)
  templatesStore.modifedElement(handleElement.value, {radius: val})
}

const changeArcTextStatus = (showCurvature: boolean) => {
  (handleElement.value as ArcText).set({showCurvature})
  templatesStore.modifedElement(handleElement.value, {showCurvature})
}

</script>

<style lang="scss" scoped>
.text-style-panel {
  user-select: none;
}

.row {
  width: 100%;
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.preset-style {
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.preset-style-item {
  width: 50%;
  height: 50px;
  border: solid 1px #d6d6d6;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  cursor: pointer;
  transition: all $transitionDelay;

  &:hover {
    border-color: $themeColor;
    color: $themeColor;
    z-index: 1;
  }

  &:nth-child(2n) {
    margin-left: -1px;
  }

  &:nth-child(n+3) {
    margin-top: -1px;
  }
}

.font-size-btn {
  padding: 0;
}

.link-popover {
  width: 240px;

  .btns {
    margin-top: 10px;
    text-align: right;
  }
}

.mt-10 {
  margin-top: 10px;
}

.full-group {
  display: flex;
  flex: 1;

  .el-button {
    width: 50%;
  }
}

.tooltip-popover {
  .el-button {
    width: 100%;
    border-radius: 0;
  }

  .font-color {
    border-top-left-radius: 4px;
    border-bottom-left-radius: 4px;
    border-right: 0;
  }

  .high-light {
    border-right: 0;
  }
}

.font-size {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.full-ratio {
  display: flex;
  flex: 1;

  .el-radio-button {
    position: relative;
    display: inline-flex;
    outline: 0;
    flex: 1;
  }

  .el-radio-button__inner {
    width: 100%
  }
}

.flex-align {
  display: flex;
  align-items: center;
}

.full-checkbox {
  display: flex;
  flex: 1;
}

.full-button {
  width: 100%;

  .iconfont {
    font-size: 32px;
  }
}

</style>

<style scoped>
:deep(.full-ratio .el-radio-button__inner) {
  width: 100%;
}

:deep(.full-ratio .el-radio-button) {
  position: relative;
  display: inline-flex;
  outline: 0;
  flex: 1;
}

:deep(.full-checkbox .el-checkbox-button) {
  position: relative;
  display: inline-flex;
  outline: 0;
  flex: 1;
}

:deep(.full-checkbox .el-checkbox-button__inner) {
  width: 100%;
}
</style>