<template>
    <div class="uploader-file" :status="status">
        <slot
            :file="file"
            :list="list"
            :status="status"
            :paused="paused"
            :error="error"
            :response="response"
            :average-speed="averageSpeed"
            :formated-average-speed="formatedAverageSpeed"
            :current-speed="currentSpeed"
            :is-complete="isComplete"
            :is-uploading="isUploading"
            :size="size"
            :formated-size="formatedSize"
            :uploaded-size="uploadedSize"
            :progress="progress"
            :progress-style="progressStyle"
            :progressing-class="progressingClass"
            :time-remaining="timeRemaining"
            :formated-time-remaining="formatedTimeRemaining"
            :type="type"
            :extension="extension"
            :file-category="fileCategory"
        >
            <div class="uploader-file-progress" :class="progressingClass" :style="progressStyle"></div>
            <div class="uploader-file-info">
                <div class="uploader-file-name">
                    <i class="uploader-file-icon" :icon="fileCategory"/>
                    <span>{{ file.name }}</span>
                </div>
                <div class="uploader-file-size">{{ formatedSize }}</div>
                <div class="uploader-file-status" v-show="status === 'uploading'">
                    {{ progressStyle.progress }} / {{ formatedAverageSpeed}} / {{ formatedTimeRemaining}}
                </div>
                <div class="uploader-file-status text" :class="'hc-custom-status-' + file.id" v-show="status !== 'uploading'">{{ statusText }}</div>
                <div class="uploader-file-actions">
                    <span class="uploader-file-pause" @click="pause"></span>
                    <span class="uploader-file-resume" @click="resume">️</span>
                    <span class="uploader-file-retry" @click="retry"></span>
                    <span class="uploader-file-remove" @click="remove"></span>
                </div>
            </div>
        </slot>
    </div>
</template>

<script>
import {computed, ref, toRaw, watch, onMounted, onUnmounted, getCurrentInstance} from 'vue'
import Uploader from 'simple-uploader.js'
import {secondsToStr} from '../common/utils'
import events from '../common/file-events'

const COMPONENT_NAME = 'uploader-file'

export default {
    name: COMPONENT_NAME,
    props: {
        file: {
            type: Object,
            default() {
                return {}
            }
        },
        list: {
            type: Boolean,
            default: false
        }
    },
    setup(props) {
        const instance = getCurrentInstance()
        let handlers = {}
        let tid = 0
        const response = ref(null)
        const paused = ref(false)
        const error = ref(false)
        const averageSpeed = ref(0)
        const currentSpeed = ref(0)
        const isComplete = ref(false)
        const isUploading = ref(false)
        const size = ref(0)
        const formatedSize = ref('')
        const uploadedSize = ref(0)
        const progress = ref(0)
        const timeRemaining = ref(0)
        const type = ref('')
        const extension = ref('')
        const progressingClass = ref('')
        const fileCategory = computed(() => {
            const isFolder = props.file.isFolder
            let type = isFolder ? 'folder' : 'unknown'
            const categoryMap = props.file.uploader.opts.categoryMap
            const typeMap = categoryMap || {
                image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
                video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
                audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
                document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
            }
            Object.keys(typeMap).forEach((_type) => {
                const extensions = typeMap[_type]
                if (extensions.indexOf(extension.value) > -1) {
                    type = _type
                }
            })
            return type
        })
        const progressStyle = computed(() => {
            progress.value = Math.floor(progress.value * 100)
            const style = `translateX(${Math.floor(progress.value - 100)}%)`
            return {
                progress: `${progress.value}%`,
                webkitTransform: style,
                mozTransform: style,
                msTransform: style,
                transform: style
            }
        })
        const formatedAverageSpeed = computed(() => {
            return `${Uploader.utils.formatSize(averageSpeed.value)}/s`
        })
        const status = computed(() => {
            let isError = error
            if (isComplete.value) {
                return 'success'
            } else if (isError.value) {
                return 'error'
            } else if (isUploading.value) {
                return 'uploading'
            } else if (paused.value) {
                return 'paused'
            } else {
                return 'waiting'
            }
        })
        const statusText = computed(() => {
            const fileStatusText = props.file.uploader.fileStatusText
            let txt = status.value
            if (typeof fileStatusText === 'function') {
                txt = fileStatusText(status.value, response.value)
            } else {
                txt = fileStatusText[status.value]
            }
            return txt || status
        })
        const formatedTimeRemaining = computed(() => {
            const file = props.file
            if (timeRemaining.value === Number.POSITIVE_INFINITY || timeRemaining.value === 0) {
                return ''
            }
            let parsedTimeRemaining = secondsToStr(timeRemaining.value)
            const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
            if (parseTimeRemaining) {
                parsedTimeRemaining = parseTimeRemaining(timeRemaining.value, parsedTimeRemaining)
            }
            return parsedTimeRemaining
        })
        const actionCheck = () => {
            paused.value = props.file.paused
            error.value = props.file.error
            isUploading.value = props.file.isUploading()
        }
        const pause = () => {
            props.file.pause()
            actionCheck()
            fileProgress()
        }
        const resume = () => {
            props.file.resume()
            actionCheck()
        }
        const remove = () => {
            props.file.cancel()
        }
        const retry = () => {
            props.file.retry()
            actionCheck()
        }
        const processResponse = (message) => {
            let res = message
            try {
                res = JSON.parse(message)
            } catch (e) {
            }
            response.value = res
        }
        const fileEventsHandler = (event, args) => {
            const rootFile = args[0]
            const file = args[1]
            const target = props.list ? rootFile : file
            if (toRaw(props.file) === toRaw(target)) {
                if (props.list && event === 'fileSuccess') {
                    processResponse(args[2])
                    return
                }
                instance.setupState[event](...args)
            }
        }
        const fileProgress = () => {
            progress.value = props.file.progress()
            averageSpeed.value = props.file.averageSpeed
            currentSpeed.value = props.file.currentSpeed
            timeRemaining.value = props.file.timeRemaining()
            uploadedSize.value = props.file.sizeUploaded()
            actionCheck()
        }
        const fileSuccess = (rootFile, file, message) => {
            if (rootFile) {
                processResponse(message)
            }
            fileProgress()
            error.value = false
            isComplete.value = true
            isUploading.value = false
        }
        const fileComplete = () => {
            fileSuccess()
        }
        const fileError = (rootFile, file, message) => {
            fileProgress()
            processResponse(message)
            error.value = true
            isComplete.value = false
            isUploading.value = false
        }
        watch(status, (newStatus, oldStatus) => {
            if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
                tid = setTimeout(() => {
                    progressingClass.value = 'uploader-file-progressing'
                }, 200)
            } else {
                clearTimeout(tid)
                progressingClass.value = ''
            }
        })
        onMounted(() => {
            paused.value = props.file['paused']
            error.value = props.file['error']
            averageSpeed.value = props.file['averageSpeed']
            currentSpeed.value = props.file['currentSpeed']
            isComplete.value = props.file.isComplete()
            isUploading.value = props.file.isUploading()
            size.value = props.file.getSize()
            formatedSize.value = props.file.getFormatSize()
            uploadedSize.value = props.file.sizeUploaded()
            progress.value = props.file.progress()
            timeRemaining.value = props.file.timeRemaining()
            type.value = props.file.getType()
            extension.value = props.file.getExtension()
            const eventHandler = (event) => {
                handlers[event] = (...args) => {
                    fileEventsHandler(event, args)
                }
                return handlers[event]
            }
            events.forEach((event) => {
                props.file.uploader.on(event, eventHandler(event))
            })
        })
        onUnmounted(() => {
            events.forEach((event) => {
                props.file.uploader.off(event, handlers[event])
            })
            handlers = null
        })
        return {
            response,
            paused,
            error,
            averageSpeed,
            currentSpeed,
            isComplete,
            isUploading,
            size,
            formatedSize,
            uploadedSize,
            progress,
            timeRemaining,
            type,
            extension,
            progressingClass,
            fileCategory,
            progressStyle,
            formatedAverageSpeed,
            status,
            statusText,
            formatedTimeRemaining,
            actionCheck,
            pause,
            resume,
            remove,
            retry,
            processResponse,
            fileEventsHandler,
            fileProgress,
            fileSuccess,
            fileComplete,
            fileError
        }
    }
}
</script>

<style lang="scss" scoped>
.uploader-file-info {
    i, em {
        font-style: normal;
    }
    &:hover {
        background-color: #e9faf4;
    }
}
</style>
