<template>
  <div
    class="spinbox-root relative flex h-full w-full items-center box-border"
    :style="{ gap: `${buttonGap}px` }"
  >
    <div
      class="relative box-border flex shrink-0 items-center justify-center overflow-hidden"
      :style="buttonStyleMap.left"
    >
      <ImageBg
        v-if="styleMap?.leftButtonStyle?.imageSrc"
        :src="styleMap.leftButtonStyle.imageSrc"
        :imageStyle="styleMap.leftButtonStyle.imageStyle"
      />
      <span class="relative z-1 whitespace-pre!">
        <LuMinus size="16px" />
      </span>
    </div>

    <div
      class="h-full relative min-w-0 flex-1 overflow-hidden box-border"
      :style="styleMap?.mainStyle"
    >
      <ImageBg
        v-if="styleMap?.mainStyle?.imageSrc"
        :src="styleMap.mainStyle.imageSrc"
        :imageStyle="styleMap.mainStyle.imageStyle"
      />
      <div
        class="relative z-1 flex h-full w-full items-center"
        :style="{ justifyContent: textJustify }"
      >
        <div class="inline-flex max-w-full items-center whitespace-pre!">
          <span
            v-for="(char, index) in displayChars"
            :key="`${index}-${char}`"
            class="inline-block"
            :style="index === cursorIndex ? styleMap?.cursorStyle : undefined"
          >
            {{ char }}
          </span>
        </div>
      </div>
    </div>

    <div
      class="relative box-border flex shrink-0 items-center justify-center overflow-hidden"
      :style="buttonStyleMap.right"
    >
      <ImageBg
        v-if="styleMap?.rightButtonStyle?.imageSrc"
        :src="styleMap.rightButtonStyle.imageSrc"
        :imageStyle="styleMap.rightButtonStyle.imageStyle"
      />
      <span class="relative z-1 whitespace-pre!">
        <LuPlus size="16px" />
      </span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useWidgetStyle } from '../hooks/useWidgetStyle'
import ImageBg from '../ImageBg.vue'
import { LuPlus, LuMinus } from 'vue-icons-plus/lu'

const DEFAULT_HEIGHT = 40

const props = defineProps<{
  width: number
  height: number
  styles: any[]
  state?: string
  part?: string
  rangeMin: number
  rangeMax: number
  step: number
  integerDigits: number
  decimalPlaces: number
  value: number
  rollOver: boolean
}>()

const styleMap = useWidgetStyle({
  widget: 'lv_spinbox',
  props
})

const buttonGap = computed(() => 6)

const buttonSize = computed(() => {
  return Math.max(24, props.height || DEFAULT_HEIGHT)
})

const buttonStyleMap = computed(() => {
  const size = `${buttonSize.value}px`
  return {
    left: {
      ...styleMap.value?.leftButtonStyle,
      width: size,
      height: size
    },
    right: {
      ...styleMap.value?.rightButtonStyle,
      width: size,
      height: size
    }
  }
})

const normalizedRange = computed(() => {
  return {
    min: Math.min(props.rangeMin, props.rangeMax),
    max: Math.max(props.rangeMin, props.rangeMax)
  }
})

const normalizedValue = computed(() => {
  const { min, max } = normalizedRange.value
  const current = props.value ?? 0

  if (props.rollOver) {
    if (current < min) return max
    if (current > max) return min
    return current
  }

  return Math.max(min, Math.min(max, current))
})

const formattedValue = computed(() => {
  const decimals = Math.max(0, Math.min(10, props.decimalPlaces ?? 0))
  const integerDigits = Math.max(1, Math.min(5, props.integerDigits ?? 1))
  const value = normalizedValue.value
  const sign = value >= 0 ? '+' : '-'
  const absolute = Math.abs(value)
  const [integerPartRaw, fractionPart = ''] = absolute.toFixed(decimals).split('.')
  const integerPart = integerPartRaw.padStart(integerDigits, '0')

  return decimals > 0 ? `${sign}${integerPart}.${fractionPart}` : `${sign}${integerPart}`
})

const displayChars = computed(() => formattedValue.value.split(''))

const cursorIndex = computed(() => {
  for (let index = displayChars.value.length - 1; index >= 0; index -= 1) {
    if (/\d/.test(displayChars.value[index])) {
      return index
    }
  }
  return 0
})

const textJustify = computed(() => {
  switch (styleMap.value?.mainStyle?.textAlign) {
    case 'center':
      return 'center'
    case 'right':
      return 'flex-end'
    default:
      return 'flex-start'
  }
})
</script>

<style scoped>
.spinbox-root {
  user-select: none;
}
</style>
