import { Sprite, AnimatedSprite, Text, Rect, Circle, View } from './tags'
import { createStyle, setStyle, getStyle, replaceValue, parse, getValue, error, extend } from './utils'

/*
* 模板解析系统
* 解析标签模板，并绘制到画布上
*/

function assign(el, extra) {
    if (el) {
        const obj = {
            setAttribute(this: any, key: string, value: any) {
                this.node.attrs[key] = value
                extend(true, this, this.node.attrs)
                if (['class', 'className', 'id'].includes(key)) {
                    this.setStyle(createStyle(this.node))
                }
            },
            getAttribute(this: any, key?: string) {  
                if (key) {
                    return this.node.attrs[key] || this[key]
                } else {
                    return this.node.attrs
                }
            },
            addClass(this: any, cls: string) {
                const attrs = this.node.attrs
                if (attrs['class']) {
                    attrs['class'] += ` ${cls}`
                } else if (attrs['className']) {
                    attrs['className'] += ` ${cls}`
                } else {
                    attrs['class'] = cls
                }
                extend(true, this, this.node.attrs)
                this.setStyle(createStyle(this.node))
            },
            removeClass(this: any, cls: string) {
                const attrs = this.node.attrs
                const clsArr = cls.split(' ')
                if (attrs['class']) {
                    clsArr.map((c: string) => {
                        attrs['class'] = attrs['class'].replace(c, '')
                    })
                } else if (attrs['className']) {
                    clsArr.map((c: string) => {
                        attrs['className'] = attrs['className'].replace(c, '')
                    })
                }
                extend(true, this, this.node.attrs)
                this.setStyle(createStyle(this.node), {
                    replaceStyle: true,
                })
            },
            toggleClass(this: any, cls: string) {
                const attrs = this.node.attrs
                const clsArr = cls.split(' ')
                clsArr.map((c: string) => {
                    if (attrs['class']) {
                        if (attrs['class'].includes(c)) {
                            obj.removeClass.bind(this)(c)
                        } else {
                            obj.addClass.bind(this)(c)
                        }
                    } else if (attrs['className']) {
                        if (attrs['className'].includes(c)) {
                            obj.removeClass.bind(this)(c)
                        } else {
                            obj.addClass.bind(this)(c)
                        }
                    }
                })
            },
        }
        extend(true, el, obj, extra)
    }
}

function createNode(node: any, extra?: any) {
    let element: any = null
    if (node.type === 'tag') {
        const _style = createStyle(node, extra)
        const image = replaceValue(getValue(node, 'attrs.src'), extra)

        switch (node.name.toLowerCase()) {
            case 'sprite':
            case 'image':
            case 'img':
                element = Sprite(image, _style)
                break
            case 'animated-sprite':
            case 'animated-image':
            case 'animatedsprite':
            case 'animatedimage':
                element = AnimatedSprite(image, _style)
                break
            case 'text':
            case 'txt':
                let content = ''
                node.children.map((child) => {
                    if (child.type === 'text') {
                        content += child.content
                    }
                })
                element = Text(content, _style)
                break
            case 'view':
            case 'div':
                element = View(_style)
                break
            case 'rect':
            case 'rectangle':
                element = Rect(_style)
                break
            case 'circle': 
                element = Circle(_style)
                break
        }
    } else if (node.type === 'text' && node.content.trim()) {
        element = Text(node.content.trim())
    }
    assign(element, { node, ...node.attrs })
    return element
}

function addChild(rootTag, root, extra?: any) {
    // text 标签中的子元素不渲染
    if (root.type !== 'text' && getValue(rootTag, 'children.length')) {
        rootTag.children.map((childTag) => {
            const child = createNode(childTag, extra)
            if (child) {
                addChild(childTag, child)
                child.appendTo(root, false)
            }
        })
    }
}

/**
 * 接受样式数据
 * @param { Temp.styleObj } styles - 样式数据
 */
export function style(styles: Temp.styleObj) {
    setStyle(styles)
}

/**
 * 模板渲染函数
 * @param { string } template - 字符串标签模板
 * @param { any } mountedRoot - 渲染到的根节点
 * @param { Temp.extra } extra - 携带数据，用于向模板中传递数据
 */
export function render(template: string, mountedRoot: any, extra?: Temp.extra) {
    if (typeof template === 'string') {
        const ast = parse(template)
        if (ast && ast.length) {
            const root: any[] = []
            ast.map((astNode) => {
                const node = createNode(astNode, extra)
                // 构建节点树
                addChild(astNode, node, extra)
                
                if (mountedRoot && mountedRoot.addChild) {
                    node.appendTo(mountedRoot)
                    root.push(node)
                } else {
                    error('mountedRoot is invalid, it should be a pixi element.')
                }
            })
            
            return root.length > 1 ? root : root[0]
        } else {
            error('parse template error or template is empty!')
        }
    } else {
        error('the template should be string!')
    }
}
