import { History } from "../component/history"
import * as urlModule from "../component/url"
import { Service } from "../concept/service"
export interface Matcher {
    reg: RegExp
    keywords: string[]
    source: string
}
export interface RouteInfo {
    matcher: Matcher
    callback: (params: any) => void
}
export class RouteService extends Service {
    public name: string = "RouteService"
    public routes: RouteInfo[] = []
    public readonly history = new History()
    public events: Leaf.EventEmitter<"unhandledUrl"> = new Leaf.EventEmitter()
    constructor() {
        super()
        this.history.events.listenBy(this, "goto", (url) => {
            this.handle(url)
        })
    }
    public route(route: string, callback: (params: any) => void) {
        this.routes.push({
            matcher: this.genRouteMatcher(route)
            , callback: callback
        })
    }
    public goto(url: string, silent: boolean = false) {
        this.history.goto(url, silent)
    }
    private parseQuery(query): any {
        if (query[0] === "?") {
            query = query.substring(1)
        }
        let kvs = {}
        query.split("&").forEach((item: string) => {
            if (!item) return
            let kv = item.split("=")
            kvs[kv[0]] = kv[1]
        })
        return kvs
    }
    private parsePath(matcher: Matcher, url: string) {
        let match = url.match(matcher.reg)
        let kv = {}
        if (!match) return
        matcher.keywords.forEach((key, index) => {
            kv[key] = match[index + 1]
        })
        return kv
    }
    private genRouteMatcher(url): Matcher {
        let paramReg = /(:[^\/]+)|(\*.+)/ig
        let lastIndex = 0
        let regStr = "^"
        let keywords = []
        while (true) {
            let result = paramReg.exec(url)

            if (!result) break
            regStr += url.substring(lastIndex, result.index)
            if (result[0][0] === ":") {
                regStr += "([^/]*)"
                keywords.push(result[0].replace(":", ""))
            }
            else if (result[0][0] === "*") {
                regStr += "(.*)"
                keywords.push(result[0].replace("*", ""))
            }
            lastIndex = result.index + result[0].length
        }
        regStr += url.substring(lastIndex)
        if (regStr[regStr.length - 1] === "/") {
            regStr = regStr.slice(-1)
        }
        regStr += "/?"
        regStr += "$"
        return { reg: new RegExp(regStr), keywords: keywords, source: url }
    }
    public handle(url) {
        let urlObject = urlModule.parse(url)
        let path = urlObject.pathname
        let found = this.routes.some((route) => {
            if (route.matcher.reg.test(path)) {
                let params = this.parsePath(route.matcher, path);
                route.callback(params)
                return true
            }
        })
        if (!found) {
            this.events.emit("unhandledUrl", url)
        }
    }
    public getRouteParameter(url: string) {
        if (!url) url = window.location.toString()
        let result = null
        let urlObject = urlModule.parse(url)
        let path = urlObject.pathname
        this.routes.some((route) => {
            if (route.matcher.reg.test(path)) {
                let params = this.parsePath(route.matcher, path);
                result = (params)
                return true
            }
        })
        return result
    }
}
