var SlideView = function(container, options) {
    this._initialize(container, options);
    this._initContainer();
    this._initNodes();
    this.reset(this.options.defaultIndex)
};
SlideView.prototype = {
    _initialize: function(container, options) {
        var container = this._container = $$(container);
        this._timerDelay = null;
        this._timerMove = null;
        this._time = 0;
        this._index = 0;
        var opt = this._setOptions(options);
        this.interval = opt.interval | 0;
        this.delay = opt.delay | 0;
        this.duration = opt.duration | 0;
        this.tween = opt.tween;
        this.autoClose = !!opt.autoClose;
        this.onShow = opt.onShow;
        this.onClose = opt.onClose;
        var pos = this._pos = /^(bottom|top|right|left)$/.test(opt.mode.toLowerCase()) ? RegExp.$1: "left";
        this._horizontal = /right|left/.test(this._pos);
        this._reverse = /bottom|right/.test(this._pos);
        var nodes = opt.nodes ? $$A.map(opt.nodes, 
        function(n) {
            return n
        }) : $$A.filter(container.childNodes, 
        function(n) {
            return n.nodeType == 1
        });
        this._nodes = $$A.map(nodes, 
        function(node) {
            var style = node.style;
            return {
                "node": node,
                "style": style[pos],
                "position": style.position,
                "zIndex": style.zIndex
            }
        });
        this._MOVE = $$F.bind(this._move, this);
        var CLOSE = $$F.bind(this.close, this);
        this._LEAVE = $$F.bind(function() {
            clearTimeout(this._timerDelay);
            $$CE.fireEvent(this, "leave");
            if (this.autoClose) {
                this._timerDelay = setTimeout(CLOSE, this.delay)
            }
        },
        this);
        $$CE.fireEvent(this, "init")
    },
    _setOptions: function(options) {
        this.options = {
            nodes: null,
            mode: "left",
            max: 0,
            min: 0,
            delay: 100,
            interval: 20,
            duration: 20,
            defaultIndex: null,
            autoClose: true,
            tween: function(t, b, c, d) {
                return - c * ((t = t / d - 1) * t * t * t - 1) + b
            },
            onShow: function(index) {},
            onClose: function() {}
        };
        return $$.extend(this.options, options || {})
    },
    _initContainer: function() {
        var container = this._container,
        style = container.style,
        position = $$D.getStyle(container, "position");
        this._style = {
            "position": style.position,
            "overflow": style.overflow
        };
        if (position != "relative" && position != "absolute") {
            style.position = "relative"
        }
        style.overflow = "hidden";
        $$E.addEvent(container, "mouseleave", this._LEAVE);
        var zIndex = 100,
        gradient = this._reverse ? -1: 1;
        this._each(function(o) {
            var style = o.node.style;
            style.position = "absolute";
            style.zIndex = zIndex += gradient
        });
        $$CE.fireEvent(this, "initContainer")
    },
    _initNodes: function() {
        var len = this._nodes.length,
        maxIndex = len - 1,
        type = this._horizontal ? "Width": "Height",
        offset = "offset" + type,
        clientSize = this._container["client" + type],
        defaultSize = Math.round(clientSize / len),
        getDefaultTarget = this._reverse ? 
        function(i) {
            return defaultSize * (maxIndex - i)
        }: function(i) {
            return defaultSize * i
        },
        max = this.options.max,
        min = this.options.min,
        getMax,
        getMin;
        if (max > 0 || min > 0) {
            if (max > 0) {
                max = Math.max(max <= 1 ? max * clientSize: Math.min(max, clientSize), defaultSize);
                min = (clientSize - max) / maxIndex
            } else {
                min = Math.min(min < 1 ? min * clientSize: min, defaultSize);
                max = clientSize - maxIndex * min
            }
            getMax = function() {
                return max
            };
            getMin = function() {
                return min
            }
        } else {
            getMax = function(o) {
                return Math.max(Math.min(o.node[offset], clientSize), defaultSize)
            };
            getMin = function(o) {
                return (clientSize - o.max) / maxIndex
            }
        }
        this._each(function(o, i) {
            var node = o.node,
            SHOW = $$F.bind(this.show, this, i);
            o.SHOW = $$F.bind(function() {
                clearTimeout(this._timerDelay);
                this._timerDelay = setTimeout(SHOW, this.delay);
                $$CE.fireEvent(this, "enter", i)
            },
            this);
            $$E.addEvent(node, "mouseenter", o.SHOW);
            o.current = o.defaultTarget = getDefaultTarget(i);
            o.max = getMax(o);
            o.min = getMin(o)
        });
        $$CE.fireEvent(this, "initNodes")
    },
    show: function(index) {
        this._setMove(index | 0);
        this.onShow(this._index);
        this._easeMove()
    },
    close: function() {
        this._setMove();
        this.onClose();
        this._easeMove()
    },
    reset: function(index) {
        clearTimeout(this._timerDelay);
        if (index == undefined) {
            this._defaultMove()
        } else {
            this._setMove(index);
            this.onShow(this._index);
            this._targetMove()
        }
    },
    _setMove: function(index) {
        var setTarget;
        if (index == undefined) {
            getTarget = function(o) {
                return o.defaultTarget
            }
        } else {
            var nodes = this._nodes,
            maxIndex = nodes.length - 1;
            this._index = index = index < 0 || index > maxIndex ? 0: index | 0;
            var nodeShow = nodes[index],
            min = nodeShow.min,
            max = nodeShow.max;
            getTarget = function(o, i) {
                return i <= index ? min * i: min * (i - 1) + max
            };
            if (this._reverse) {
                var get = getTarget;
                index = maxIndex - index;
                getTarget = function(o, i) {
                    return get(o, maxIndex - i)
                }
            }
        }
        this._each(function(o, i) {
            o.target = getTarget(o, i);
            o.begin = o.current;
            o.change = o.target - o.begin
        });
        $$CE.fireEvent(this, "setMove", index)
    },
    _easeMove: function() {
        this._time = 0;
        this._move()
    },
    _move: function() {
        if (this._time < this.duration) {
            this._tweenMove();
            this._time++;
            this._timerMove = setTimeout(this._MOVE, this.interval)
        } else {
            this._targetMove();
            $$CE.fireEvent(this, "finish")
        }
    },
    _tweenMove: function() {
        this._setPos(function(o) {
            return this.tween(this._time, o.begin, o.change, this.duration)
        });
        $$CE.fireEvent(this, "tweenMove")
    },
    _targetMove: function() {
        this._setPos(function(o) {
            return o.target
        });
        $$CE.fireEvent(this, "targetMove")
    },
    _defaultMove: function() {
        this._setPos(function(o) {
            return o.defaultTarget
        });
        $$CE.fireEvent(this, "defaultMove")
    },
    _setPos: function(method) {
        clearTimeout(this._timerMove);
        var pos = this._pos;
        this._each(function(o, i) {
            o.node.style[pos] = (o.current = Math.round(method.call(this, o))) + "px"
        })
    },
    _each: function(callback) {
        $$A.forEach(this._nodes, callback, this)
    },
    dispose: function() {
        clearTimeout(this._timerDelay);
        clearTimeout(this._timerMove);
        $$CE.fireEvent(this, "dispose");
        var pos = this._pos;
        this._each(function(o) {
            var style = o.node.style;
            style[pos] = o.style;
            style.zIndex = o.zIndex;
            style.position = o.position;
            $$E.removeEvent(o.node, "mouseenter", o.SHOW);
            o.SHOW = o.node = null
        });
        $$E.removeEvent(this._container, "mouseleave", this._LEAVE);
        $$D.setStyle(this._container, this._style);
        this._container = this._nodes = this._MOVE = this._LEAVE = null;
        $$CE.clearEvent(this)
    }
};
SlideView.prototype._initialize = (function() {
    var init = SlideView.prototype._initialize,
    reset = SlideView.prototype.reset,
    methods = {
        "init": function() {
            this.autoDelay = this.options.autoDelay | 0;
            this._autoTimer = null;
            this._autoPause = false;
            this._NEXT = $$F.bind(function() {
                this.show(this._index + 1)
            },
            this)
        },
        "leave": function() {
            this.autoClose = this._autoPause = false;
            this._autoNext()
        },
        "enter": function() {
            clearTimeout(this._autoTimer);
            this._autoPause = true
        },
        "finish": function() {
            this._autoNext()
        },
        "dispose": function() {
            clearTimeout(this._autoTimer)
        }
    },
    prototype = {
        _autoNext: function() {
            if (!this._autoPause) {
                clearTimeout(this._autoTimer);
                this._autoTimer = setTimeout(this._NEXT, this.autoDelay);
                if (this._index + 1 == this._nodes.length) {
                    this.options.auto = false
                }
            }
        },
        reset: function(index) {
            reset.call(this, index == undefined ? this._index: index);
            this._autoNext()
        }
    };
    return function() {
        var options = arguments[1];
        if (options && options.auto) {
            $$.extend(options, {
                autoDelay: 2000
            },
            false);
            $$.extend(this, prototype);
            $$A.forEach(methods, 
            function(method, name) {
                $$CE.addEvent(this, name, method)
            },
            this)
        }
        init.apply(this, arguments)
    }
})();
SlideView.prototype._initialize = (function() {
    var init = SlideView.prototype._initialize,
    methods = {
        "init": function() {
            this._tipPos = /^(bottom|top|right|left)$/.test(this.options.tipPos.toLowerCase()) ? RegExp.$1: "bottom"
        },
        "initNodes": function() {
            var opt = this.options,
            tipTag = opt.tipTag,
            tipClass = opt.tipClass,
            re = tipClass && new RegExp("(^|\\s)" + tipClass + "(\\s|$)"),
            getTipNode = function(node) {
                var nodes = node.getElementsByTagName(tipTag);
                if (tipClass) {
                    nodes = $$A.filter(nodes, 
                    function(n) {
                        return re.test(n.className)
                    })
                }
                return nodes[0]
            };
            var tipShow = opt.tipShow,
            tipClose = opt.tipClose,
            offset = /right|left/.test(this._tipPos) ? "offsetWidth": "offsetHeight";
            this._each(function(o) {
                var node = o.node,
                tipNode = getTipNode(node);
                node.style.overflow = "hidden";
                tipNode.style.position = "absolute";
                o.tip = {
                    "node": tipNode,
                    "show": tipShow != undefined ? tipShow: 0,
                    "close": tipClose != undefined ? tipClose: -tipNode[offset]
                }
            })
        },
        "setMove": function(index) {
            var maxIndex = this._nodes.length - 1;
            this._each(function(o, i) {
                var tip = o.tip;
                if (this._reverse) {
                    i = maxIndex - i
                }
                tip.target = index == undefined || index != i ? tip.close: tip.show;
                tip.begin = tip.current;
                tip.change = tip.target - tip.begin
            })
        },
        "tweenMove": function() {
            this._setTipPos(function(tip) {
                return this.tween(this._time, tip.begin, tip.change, this.duration)
            })
        },
        "targetMove": function() {
            this._setTipPos(function(tip) {
                return tip.target
            })
        },
        "defaultMove": function() {
            this._setTipPos(function(tip) {
                return tip.close
            })
        },
        "dispose": function() {
            this._each(function(o) {
                o.tip = null
            })
        }
    },
    prototype = {
        _setTipPos: function(method) {
            var pos = this._tipPos;
            this._each(function(o, i) {
                var tip = o.tip;
                tip.node.style[pos] = ((tip.current = Math.round(method.call(this, tip))) * -1 - 125) + "px"
            })
        }
    };
    return function() {
        var options = arguments[1];
        if (options && options.tip == true) {
            $$.extend(options, {
                tipPos: "top",
                tipTag: "*",
                tipClass: "",
                tipShow: null,
                tipClose: null
            },
            false);
            $$.extend(this, prototype);
            $$A.forEach(methods, 
            function(method, name) {
                $$CE.addEvent(this, name, method)
            },
            this)
        }
        init.apply(this, arguments)
    }
})();
