define(['yua'], function(){

    function Router(){
        this.table = {get: []};
        this.errorFn = null;
        this.history = null;
        this.hash = '';
        this.started = false;
        this.init = {};
    }

    var defaultOptions = {
        prefix: /^(#!|#)[\/]?/, //hash前缀正则
        historyOpen: true, //是否开启hash历史
        allowReload: true //连续点击同一个链接是否重新加载
    };
    var isMouseUp = true;

    var ruleRegExp = /(:id)|(\{id\})|(\{id:([A-z\d\,\[\]\{\}\-\+\*\?\!:\^\$]*)\})/g;

    Router.prototype = {
        error: function(callback){
            this.errorFn = callback;
        },
        config: function(opts){
            if(this.started)
                return console.error('Router config has been set');

            this.started = true;
            if(!opts.allowReload)
                opts.historyOpen = true;
            this.init = yua.mix({}, defaultOptions, opts);
        },
        _getRegExp: function(rule, opts){
            var re = rule.replace(ruleRegExp, function(m, p1, p2, p3, p4){
                var w = '([\\w.-]';
                if(p1 || p2){
                    return w + '+)';
                }else{
                    if(!/^\{[\d\,]+\}$/.test(p4)){
                        w = '(';
                    }
                    return w + p4 + ')';
                }
            })
            re = re.replace(/(([^\\])([\/]+))/g, '$2\\/').replace(/(([^\\])([\.]+))/g, '$2\\.').replace(/(([^\\])([\-]+))/g, '$2\\-').replace(/(\(.*)(\\[\-]+)(.*\))/g, '$1-$3');
            re = '^' + re + '$';
            opts.regexp = new RegExp(re)
            return opts;
        },
        _add: function(method, rule, callback){
            if(!this.started)
                this.config({});

            var table = this.table[method.toLowerCase()];
            if (rule.charAt(0) !== "/") {
                console.error('char "/" must be in front of router rule');
                return;
            }
            rule = rule.replace(/^[\/]+|[\/]+$|\s+/g, '');
            var opts = {};
            opts.rule = rule;
            opts.callback = callback;
            yua.Array.ensure(table, this._getRegExp(rule, opts));
        },
        _route: function(method, hash){
            var hash = hash.trim();
            var table = this.table[method];
            var init = this.init;

            if(!init.allowReload && hash === this.history)
                return;

            if(init.historyOpen){
                this.history = hash;
                if(yua.ls)
                    yua.ls('lastHash', hash);
            }

            for(var i = 0, obj; obj = table[i++];){
                var args = hash.match(obj.regexp);
                if(args){
                    args.shift();
                    return obj.callback.apply(obj, args)
                }
            }
            this.errorFn && this.errorFn(hash);
        },
        on: function(rule, callback){
            var _this = this
            if(Array.isArray(rule)){
                rule.forEach(function(it){
                    _this._add('get', it, callback);
                })
            }else{
                this._add('get', rule, callback);
            }
        }
    }

    yua.bind(window, 'load', function(){
        if(!yua.router.started)
            return;
        var prefix = yua.router.init.prefix;
        var hash = location.hash;
        hash = hash.replace(prefix, "").trim();
        yua.router._route('get', hash);
    });


    if('onhashchange' in window){
        window.addEventListener('hashchange', function(event){
            if(!isMouseUp)
                return;
            var prefix = yua.router.init.prefix;
            var hash = location.hash.replace(prefix, "").trim();
            yua.router._route('get', hash)
        })
    }

    //劫持页面上所有点击事件，如果事件源来自链接或其内部，
    //并且它不会跳出本页，并且以"#/"或"#!/"开头，那么触发updateLocation方法
    yua.bind(document, "mousedown", function(event){
        var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false
        if (defaultPrevented || event.ctrlKey || event.metaKey || event.which === 2)
            return
        var target = event.target
        while (target.nodeName !== "A") {
            target = target.parentNode
            if (!target || target.tagName === "BODY") {
                return
            }
        }

        if (targetIsThisWindow(target.target)){
            if(!yua.router.started)
                return;
            var href = target.getAttribute("href") || target.getAttribute("xlink:href"),
                prefix = yua.router.init.prefix;

            if (href === null || !prefix.test(href))
                return

            yua.router.hash = href.replace(prefix, "").trim();
            event.preventDefault();
            location.hash = href;
            isMouseUp = false;
        }
    })

    yua.bind(document, "mouseup", function(){
        if(!isMouseUp){
            yua.router._route('get', yua.router.hash);
            isMouseUp = true;
        }
        
    })
    

    //判定A标签的target属性是否指向自身
    //thanks https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L219
    function targetIsThisWindow(targetWindow) {
        if (!targetWindow || targetWindow === window.name || targetWindow === '_self' || (targetWindow === 'top' && window == window.top)) {
            return true
        }
        return false
    }

    yua.ui.router = '0.0.1'

    return yua.router = new Router;
})