import * as PIXI from 'pixi.js'
import { getValue, error } from '../../utils'

const isContainer = (target: any) => {
    return target instanceof PIXI.Container && !(target instanceof PIXI.Sprite ) && !(target instanceof PIXI.Graphics)
}

export function transform(uiDesignRatio: number = 1) {
    function rotation(angle: string | number) {
        if (typeof angle === 'string') {
            const _angle = angle.trim()
            const degreeUnit = Math.PI / 180
            const hasUnit = _angle.endsWith('deg')
            if (hasUnit) {
                const [ degree ] = _angle.split('deg')
                return parseFloat(degree) * degreeUnit
            } else {
                return parseFloat(_angle)
            }
        }
        return angle
    }

    function _parseUnit(parentSize: number, value: string) {
        value = value.trim()
        if (value.endsWith('px')) {
            return parseInt(value)
        } else if (value.endsWith('%')) {
            return parentSize * parseInt(value) / 100
        } else {
            return parseInt(value) / uiDesignRatio
        }
    }

    function length(parentSize: number, value: string | number) {
        let result: number = 0
        if (typeof value === 'string') result = _parseUnit(parentSize, value)
        if (typeof value === 'number') result = value / uiDesignRatio
        return Math.round(result)
    }

    function pos(parentSize: number, spriteSize: number, value: number | string) {
        if (['number', 'string'].includes(typeof value)) {
            let result: number = 0
            if (typeof value === 'number') {
                result = value / uiDesignRatio
            } else {
                if (value.includes(':')) {
                    const [key, number] = value.split(':')
                    switch (key.trim()) {
                        case 'left':
                        case 'top':
                            result = _parseUnit(parentSize, number)
                            break
                        case 'right':
                        case 'bottom':
                            result = parentSize - spriteSize
                            result -= _parseUnit(parentSize, number)
                            break
                        case 'center':
                            result = (parentSize - spriteSize) / 2
                            result += _parseUnit(parentSize, number)
                            break
                    }
                } else if (value === 'center') {
                    result = (parentSize - spriteSize) / 2
                } else {
                    result = _parseUnit(parentSize, value)
                }
            }
            return Math.round(result)
        } else {
            throw error('value must be number or string')
        }
    }
    function actualScale(parent: Layout.element) {
        const scale = {
            x: parent.scale.x,
            y: parent.scale.y,
        }
        let _parent = parent.parent
        while (_parent) {
            scale.x *= _parent.scale.x
            scale.y *= _parent.scale.y
            _parent = _parent.parent
        }
        return scale
    }

    function getPixiContainerSize(container: PIXI.Container) {
        if (container.parent) {
            if (container['isStage']) {
                return {
                    width: window.innerWidth,
                    height: window.innerHeight,
                }
            } else if (container.width > 0 && container.height > 0) {
                return {
                    width: container.width,
                    height: container.height,
                }
            } else if (isContainer(container.parent)) {
                return getPixiContainerSize(container.parent)
            } else {
                return {
                    width: container.parent['width'],
                    height: container.parent['height'],
                }   
            }
        } else {
            return {
                width: window.innerWidth,
                height: window.innerHeight,
            }
        }
    }

    function actualSize(target: any) {
        if (isContainer(target)) {
            return getPixiContainerSize(target)
        } else {
            const width = getValue(target, 'layout.pixilayout.width') || target.width
            const height = getValue(target, 'layout.pixilayout.height') || target.height
            if (target.parent) {
                const { x: scaleX, y: scaleY } = actualScale(target.parent)
                return {
                    width: ~~(width * scaleX),
                    height: ~~(height * scaleY),
                }
            } else {
                return {
                    width,
                    height,
                }
            }
        }
    }

    function getRatio(target: Layout.element) {
        if (['sprite', 'animatedsprite'].includes(target['type']) && target['_texture']) {
            return target['_texture'].width / target['_texture'].height
        } else {
            return 1
        }
    }

    function _getDefaultSize(target: any) {
        switch (target.type) {
            case 'animatedsprite':
            case 'sprite': {
                return target._texture
            }
            case 'text': {
                if (getValue(target, 'layout.container')) {
                    const { width, height } = target.layout.container
                    return {
                        width,
                        height,
                    }
                } else {
                    return {
                        width: 1,
                        height: 1,
                    }
                }
            }
            default: {
                return {
                    width: 1,
                    height: 1,
                }
            }
        }
    }

    function elementSize(target: Layout.element, style: Layout.style, parentSize: Layout.size) {        
        const { width, height } = style
        const { width: targetWidth, height: targetHeight } = _getDefaultSize(target)
        const { width: parentWidth, height: parentHeight } = parentSize

        const targetRatio = getRatio(target)

        let finalWidth = targetWidth / uiDesignRatio
        let finalHeight = targetHeight / uiDesignRatio
        if (width) {
            finalWidth = length(parentWidth, width)
            if (height) {
                finalHeight = length(parentHeight, height)
            } else if (targetRatio) {
                finalHeight = finalWidth / targetRatio
            }
        } else if (height) {
            finalHeight = length(parentHeight, height)
            finalWidth = finalHeight * targetRatio
        }

        return {
            width: Math.round(finalWidth),
            height: Math.round(finalHeight),
        }
    }

    function _getPos(style: any, keyArr: string[]) {
        let result: number | string = 0
        for (let i = 0; i < keyArr.length; i++) {
            const key = keyArr[i]
            const value = getValue(style, key)
            if (value !== undefined) {
                if (['x', 'y'].includes(key)) {
                    result = value
                    break
                } else if (key.includes('center')) {
                    result = `center: ${value}`
                    break
                } else {
                    result = `${key}: ${value}`
                    break
                }
            }
        }
        return result
    }

    function elementPos(targetSize: Layout.size, style: Layout.style, parentSize: Layout.size) {
        const { rotation: _rotation = 0 } = style
        const x = _getPos(style, ['x', 'left', 'centerX', 'right', 'center.x'])
        const y = _getPos(style, ['y', 'top', 'centerY', 'bottom', 'center.y'])
        const { width: parentWidth, height: parentHeight } = parentSize
        const { width: targetWidth, height: targetHeight } = targetSize

        return {
            x: pos(parentWidth, targetWidth, x),
            y: pos(parentHeight, targetHeight, y),
            rotation: rotation(_rotation),
        }
    }

    // 获取父级容器
    function container(element: Layout.element) {
        const { width, height } = actualSize(element)
        const scale = actualScale(element)
        return {
            element,
            width,
            height,
            ratio: width / height,
            scale,
        }
    }

    function rlayout2pixilayout(
        container: Layout.containerStatus,
        anchor: Layout.point,
        rlayout: Layout.layout,
    ): Layout.layout {
        const {
            width: containerWidth,
            height: containerHeight,
            scale: containerScale,
            element: parent,
        } = container

        const { x: scaleX, y: scaleY } = containerScale
        let { width: sw, height: sh, x: sx, y: sy } = rlayout
        
        sx += sw * anchor.x
        sy += sh * anchor.y

        if (parent['anchor']) {
            sx -= containerWidth * parent['anchor'].x
            sy -= containerHeight * parent['anchor'].y
        } else if (parent['pivot']) {
            sx -= containerWidth * parent['pivot'].x
            sy -= containerHeight * parent['pivot'].y
        }

        sw /= scaleX
        sh /= scaleY
        sx /= scaleX
        sy /= scaleY

        return {
            width: Math.round(sw),
            height: Math.round(sh),
            x: Math.round(sx),
            y: Math.round(sy),
            rotation: rlayout.rotation,
        }
    }

    // 反向同步布局信息
    function pixilayout2rlayout(
        container: Layout.containerStatus,
        anchor: Layout.point,
        pixilayout: Layout.layout,
    ): Layout.layout {
        const { 
            width: containerWidth, 
            height: containerHeight, 
            scale: { x: scaleX, y: scaleY },
            element: parent,
        } = container

        let { width: sw, height: sh, x: sx, y: sy } = pixilayout

        sw *= scaleX
        sh *= scaleY
        sx *= scaleX
        sy *= scaleY

        if (parent['anchor']) {
            sx += containerWidth * parent['anchor'].x
            sy += containerHeight * parent['anchor'].y
        } else if (parent['pivot']) {
            sx += containerWidth * parent['pivot'].x
            sy += containerHeight * parent['pivot'].y
        }

        sx -= sw * anchor.x
        sy -= sh * anchor.y

        return {
            width: Math.round(sw),
            height: Math.round(sh),
            x: Math.round(sx),
            y: Math.round(sy),
            rotation: pixilayout.rotation,
        }
    }

    return {
        rotation,
        length,
        pos,
        actualScale,
        actualSize,
        getRatio,
        elementSize,
        elementPos,
        container,
        rlayout2pixilayout,
        pixilayout2rlayout,
    }
}
