<template>
  <div
    :aria-valuenow="percentage"
    :style="containerStyle"
    aria-valuemax="=100"
    aria-valuemin="0"
    role="progressbar"
    class="vc-progress"
  >
    <div class="vc-progress-bar">
      <div
        :style="outerBarStyle"
        class="vc-progress-bar__outer"
      >
        <div
          :style="barStyle"
          :class="{
            'is-indeterminate': indeterminate,
            'is-striped': striped
          }"
          class="vc-progress-bar__inner"
        >
          <div
            v-if="(showText || $slots.default) && textInside"
            class="vc-progress-bar__inner-text"
          >
            <slot :percentage="percentage">
              <span>{{ content }}</span>
            </slot>
          </div>
        </div>
      </div>
    </div>
    <div
      v-if="(showText || $slots.default) && !textInside"
      :style="{ fontSize: `${progressTextSize}px` }"
      class="vc-progress__text"
    >
      <slot
        :percentage="percentage"
      >
        <span
          v-if="statusIcon"
          :class="statusIcon.icon"
          :style="statusIcon.color"
        />
        <span v-else>{{ content }}</span>
      </slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import { convertToUnit } from '@/utils/helpers'
import { BarStyle, ProgressFn, StatusIconProgress, StatusProgress } from './VcProgress.types'
import {
  DEFAULT_DURATION,
  DEFAULT_HEIGHT,
  DEFAULT_WIDTH,
  STATUS_COLOR_MAP,
  STATUS_COLOR_VARS,
  STATUS_ICON_MAP
} from './progress.options'

export default defineComponent({
  name: 'VcProgress',
  props: {
    percentage: {
      type: Number,
      default: 0,
      validator: (val: number): boolean => val >= 0 && val <= 100
    },
    status: {
      type: String as PropType<StatusProgress>,
      default: '',
      values: ['', 'primary', 'success', 'warning', 'error']
    },
    color: {
      type: String,
      default: '',
      validator: (val: string): boolean => val ? val.startsWith('#') || val.startsWith('var(') : true
    },
    indeterminate: {
      type: Boolean,
      default: false
    },
    duration: {
      type: Number,
      default: DEFAULT_DURATION
    },
    height: {
      type: Number,
      default: DEFAULT_HEIGHT
    },
    textInside: {
      type: Boolean,
      default: false
    },
    width: {
      type: [String, Number],
      default: DEFAULT_WIDTH
    },
    showText: {
      type: Boolean,
      default: true
    },
    format: {
      type: Function as PropType<ProgressFn>,
      default: (percentage: number): string => `${percentage}%`
    },
    customColorMethod: {
      type: Function as PropType<ProgressFn | undefined>,
      default: undefined
    },
    showStatusIcon: {
      type: Boolean,
      default: false
    },
    striped: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    outerBarStyle (): {[key: string]: string} {
      return { height: convertToUnit(this.height) || '6px' }
    },
    progressTextSize (): number {
      return 12 + this.height * 0.4
    },
    content (): string {
      return this.format(this.percentage)
    },
    containerStyle (): {[key: string]: string} {
      return { width: convertToUnit(this.width) || `${DEFAULT_WIDTH}px` }
    },
    barStyle (): BarStyle {
      return {
        width: `${this.percentage}%`,
        animationDuration: `${this.duration}s`,
        backgroundColor: this.progressColor
      }
    },
    statusIcon (): StatusIconProgress | null {
      if (!this.status || !this.showStatusIcon) {
        return null
      }
      const { variable, color } = this.getColor()
      const _icon = STATUS_ICON_MAP[this.status] || STATUS_ICON_MAP.default
      return {
        icon: _icon,
        color: { color: `var(${variable}, ${color})` }
      }
    },
    progressColor (): string {
      if (this.customColorMethod) {
        return this.customColorMethod(this.percentage)
      } else if (this.color) {
        return this.color
      } else {
        const { variable, color } = this.getColor()
        return `var(${variable}, ${color})`
      }
    }
  },
  methods: {
    getColor (): { variable: string, color: string } {
      return {
        variable: STATUS_COLOR_VARS[this.status] || STATUS_COLOR_VARS.default,
        color: STATUS_COLOR_MAP[this.status] || STATUS_COLOR_MAP.default
      }
    }
  }
})
</script>

<style lang="scss" scoped>
  .vc-progress {
    @apply mb-3 relative leading-none flex items-center max-w-full;
    &-bar {
      @apply box-border flex-grow;
      &__outer {
        @apply overflow-hidden relative align-middle bg-progress-line-not-done leading-none;
        height: 6px;
        border-radius: 100px;
      }
      &__inner {
        @apply absolute left-0 top-0 h-full bg-primary text-right leading-none whitespace-nowrap;
        border-radius: 100px;
        transition: width .6s ease;
        &:after {
          @apply inline-block h-full align-middle;
          content: ''
        }
        &.is-indeterminate {
          transform: translateZ(0);
          animation: indeterminate 3s infinite;
        }
        &.is-striped {
          background-image: linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);
          background-size: 1rem 1rem;
        }
      }
      &__inner-text {
        @apply inline-block align-middle text-white text-xs mx-1;
      }
    }
    &__text {
      @apply mx-1 leading-none text-sm text-default-text;
      min-width: 50px;
    }
  }
  [dir='rtl'] {
    .vc-progress {
      &-bar {
        &__inner {
          @apply text-left left-auto right-0;
          &.is-striped {
            background-image: linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);
          }
        }
      }
    }
  }
  @keyframes indeterminate {
    0% {
      left: -100%;
    }

    100% {
      left: 100%;
    }
  }
</style>
