/// <reference path="leaf.ts" />

namespace Leaf {
    export interface EventHandlerInfo {
        owner?: any
        once?: boolean
        handler: Function
    }
    export interface EventHandlers {
        [index: string]: EventHandlerInfo[]
    }
    export class EventEmitter<E extends string>{
        private events: EventHandlers
        constructor() {
            this.events = {}
        }
        public addEventListener(event: E, handler: Function) {
            this.on(event, handler)
            return this
        }
        public on(event: E, handler: Function) {
            let ehs = this.getOrCreateEventHandlers(event)
            ehs.push({
                once: false,
                handler: handler
            })
            return this
        }
        public once(event: E, handler: Function) {
            let ehs = this.getOrCreateEventHandlers(event)
            ehs.push({
                once: true,
                handler: handler
            })
            return this
        }
        public listenByOnce(who: any, event: E, handler: Function) {
            let ehs = this.getOrCreateEventHandlers(event)
            ehs.push({
                owner: who,
                once: true,
                handler: handler
            })
            return this
        }
        public listenBy(who: any, event: E | E[], handler: Function) {
            if (event instanceof Array) {
                for (let e of event) {
                    this.listenBy(who, e, handler)
                }
                return this
            }
            let ehs = this.getOrCreateEventHandlers(event)
            ehs.push({
                owner: who,
                once: false,
                handler: handler
            })
            return this
        }
        public stopListenBy(who: any, event?: E, handler?: Function) {
            if (event) {
                let ehs = this.getOrCreateEventHandlers(event)
                ehs = ehs.filter((eh) => {
                    if (eh.owner === who && (!handler || handler === eh.handler)) return false
                    return true
                })
                this.events[event as string] = ehs
            } else {
                if (!who) {
                    return
                }
                for (let name in this.events) {
                    let ehs = this.events[name]
                    let change = false
                    ehs = ehs.filter((eh) => {
                        if (eh.owner === who && (!handler || handler === eh.handler)) {
                            change = true
                            return false
                        }
                        return true
                    })
                    if (change) {
                        this.events[name] = ehs
                    }
                }
            }
            return this
        }
        public removeEventListener(event?: E, handler?: Function) {
            if (!event) {
                this.events = {}
            }
            if (!handler) {
                this.events[event as string] = []
                return this
            }
            let ehs = this.getOrCreateEventHandlers(event)
            let change = false
            ehs = ehs.filter((eh) => {
                if (!eh.owner && (!handler || handler === eh.handler)) {
                    change = true
                    return false
                }
                return true
            })
            if (change) {
                this.events[event as string] = ehs
            }
            return this

        }
        public emit(event: E, ...values: any[]) {
            let todo: Function[] = []
            let ehs = this.getOrCreateEventHandlers(event)
            let change: boolean = false
            ehs = ehs.filter((eh) => {
                if (eh.owner)
                    todo.push(eh.handler.bind(eh.owner))
                else
                    todo.push(eh.handler)
                if (eh.once) {
                    change = true
                    return false
                }
                return true
            })
            if (change) {
                this.events[event as string] = ehs
            }
            todo.forEach((handler) => {
                handler(...values)
            })
            return this
        }
        private getOrCreateEventHandlers(event: E): EventHandlerInfo[] {
            let ehs = this.events[event as string]
            if (!ehs) {
                ehs = this.events[event as string] = []
            }
            return ehs
        }
    }
}
