/**
 * @ignore
 * KISSY Class System
 * @author yiminghe@gmail.com
 */
KISSY.add('base', function (S, Attribute, CustomEvent) {
    var ATTRS = 'ATTRS',
        ucfirst = S.ucfirst,
        ON_SET = '_onSet',
        noop = S.noop,
        RE_DASH = /(?:^|-)([a-z])/ig;

    function replaceToUpper() {
        return arguments[1].toUpperCase();
    }

    function CamelCase(name) {
        return name.replace(RE_DASH, replaceToUpper);
    }

    function __getHook(method, reverse) {
        return function (origFn) {
            return function wrap() {
                var self = this;
                if (reverse) {
                    origFn.apply(self, arguments);
                } else {
                    self.callSuper.apply(self, arguments);
                }
                // can not use wrap in old ie
                var extensions = arguments.callee.__owner__.__extensions__ || [];
                if (reverse) {
                    extensions.reverse();
                }
                callExtensionsMethod(self, extensions, method, arguments);
                if (reverse) {
                    self.callSuper.apply(self, arguments);
                } else {
                    origFn.apply(self, arguments);
                }
            };
        }
    }

    /**
     * @class KISSY.Base
     * @extend KISSY.Event.CustomEvent.Target
     *
     * A base class which objects requiring attributes, extension, plugin, custom event support can
     * extend.
     * attributes configured
     * through the static {@link KISSY.Base#static-ATTRS} property for each class
     * in the hierarchy will be initialized by Base.
     */
    function Base(config) {
        var self = this,
            c = self.constructor;
        Base.superclass.constructor.apply(this, arguments);
        self.__attrs = {};
        self.__attrVals = {};
        // save user config
        self.userConfig = config;
        // define
        while (c) {
            addAttrs(self, c[ATTRS]);
            c = c.superclass ? c.superclass.constructor : null;
        }
        // initial attr
        initAttrs(self, config);
        // setup listeners
        var listeners = self.get("listeners");
        for (var n in listeners) {
            self.on(n, listeners[n]);
        }
        // initializer
        self.initializer();
        // call plugins
        constructPlugins(self);
        callPluginsMethod.call(self, 'pluginInitializer');
        // bind attr change
        self.bindInternal();
        // sync attr
        self.syncInternal();
    }

    S.augment(Base, Attribute);

    S.extend(Base, CustomEvent.Target, {
        initializer: noop,

        '__getHook': __getHook,

        __callPluginsMethod: callPluginsMethod,

        'callSuper': function () {
            var method, obj,
                self = this,
                args = arguments;

            if (typeof self == 'function' && self.__name__) {
                method = self;
                obj = args[0];
                args = Array.prototype.slice.call(args, 1);
            } else {
                method = arguments.callee.caller;
                if (method.__wrapped__) {
                    method = method.caller;
                }
                obj = self;
            }

            var name = method.__name__;
            if (!name) {
                //S.log('can not find method name for callSuper [' + self.constructor.name + ']: ' + method.toString());
                return undefined;
            }
            var member = method.__owner__.superclass[name];
            if (!member) {
                //S.log('can not find method [' + name + '] for callSuper: ' + method.__owner__.name);
                return undefined;
            }

            return member.apply(obj, args || []);
        },

        /**
         * bind attribute change event
         * @protected
         */
        bindInternal: function () {
            var self = this,
                attrs = self['getAttrs'](),
                attr, m;

            for (attr in attrs) {
                m = ON_SET + ucfirst(attr);
                if (self[m]) {
                    // 自动绑定事件到对应函数
                    self.on('after' + ucfirst(attr) + 'Change', onSetAttrChange);
                }
            }
        },

        /**
         * sync attribute change event
         * @protected
         */
        syncInternal: function () {
            var self = this,
                cs = [],
                i,
                c = self.constructor,
                attrs = self.getAttrs();

            while (c) {
                cs.push(c);
                c = c.superclass && c.superclass.constructor;
            }

            cs.reverse();

            // from super class to sub class
            for (i = 0; i < cs.length; i++) {
                var ATTRS = cs[i].ATTRS || {};
                for (var attributeName in ATTRS) {
                    if (attributeName in attrs) {
                        var attributeValue,
                            onSetMethod;
                        var onSetMethodName = ON_SET + ucfirst(attributeName);
                        // 存在方法,并且用户设置了初始值或者存在默认值,就同步状态
                        if ((onSetMethod = self[onSetMethodName]) &&
                            // 用户如果设置了显式不同步,就不同步,
                            // 比如一些值从 html 中读取,不需要同步再次设置
                            attrs[attributeName].sync !== 0 &&
                            (attributeValue = self.get(attributeName)) !== undefined) {
                            onSetMethod.call(self, attributeValue);
                        }
                    }
                }
            }
        },

        /**
         * plugin a new plugins to current instance
         * @param {Function|Object} plugin
         * @chainable
         */
        'plug': function (plugin) {
            var self = this;
            if (typeof plugin === 'function') {
                plugin = new plugin();
            }
            // initialize plugin
            if (plugin['pluginInitializer']) {
                plugin['pluginInitializer'](self);
            }
            self.get('plugins').push(plugin);
            return self;
        },

        /**
         * unplug by pluginId or plugin instance.
         * if called with no parameter, then destroy all plugins.
         * @param {Object|String} [plugin]
         * @chainable
         */
        'unplug': function (plugin) {
            var plugins = [],
                self = this,
                isString = typeof plugin == 'string';

            S.each(self.get('plugins'), function (p) {
                var keep = 0, pluginId;
                if (plugin) {
                    if (isString) {
                        // user defined takes priority
                        pluginId = p.get && p.get('pluginId') || p.pluginId;
                        if (pluginId != plugin) {
                            plugins.push(p);
                            keep = 1;
                        }
                    } else {
                        if (p != plugin) {
                            plugins.push(p);
                            keep = 1;
                        }
                    }
                }

                if (!keep) {
                    p.pluginDestructor(self);
                }
            });

            self.setInternal('plugins', plugins);
            return self;
        },

        /**
         * get specified plugin instance by id
         * @param {String} id pluginId of plugin instance
         * @return {Object}
         */
        'getPlugin': function (id) {
            var plugin = null;
            S.each(this.get('plugins'), function (p) {
                // user defined takes priority
                var pluginId = p.get && p.get('pluginId') || p.pluginId;
                if (pluginId == id) {
                    plugin = p;
                    return false;
                }
                return undefined;
            });
            return plugin;
        },

        destructor: S.noop,

        destroy: function () {
            var self = this;
            if (!self.get('destroyed')) {
                callPluginsMethod.call(self, 'pluginDestructor');
                self.destructor();
                self.set('destroyed', true);
                self.fire('destroy');
                self.detach();
            }
        }
    });

    S.mix(Base, {
        __hooks__: {
            initializer: __getHook(),
            destructor: __getHook('__destructor', true)
        },

        ATTRS: {
            /**
             * Plugins for current component.
             * @cfg {Function[]/Object[]} plugins
             */
            /**
             * @ignore
             */
            plugins: {
                value: []
            },

            destroyed: {
                value: false
            },

            /**
             * Config listener on created.
             *
             * for example:
             *
             *      {
             *          click:{
             *              context:{x:1},
             *              fn:function(){
             *                  alert(this.x);
             *              }
             *          }
             *      }
             *      // or
             *      {
             *          click:function(){
             *              alert(this.x);
             *          }
             *      }
             *
             * @cfg {Object} listeners
             */
            /**
             * @ignore
             */
            listeners: {
                value: []
            }
        },

        /**
         * create a new class from extensions and static/prototype properties/methods.
         * @param {Function[]} [extensions] extension classes
         * @param {Object} [px] key-value map for prototype properties/methods.
         * @param {Object} [sx] key-value map for static properties/methods.
         * @param {String} [sx.name] new Class's name.
         * @return {Function} new class which extend called, it also has a static extend method
         * @static
         *
         * for example:
         *
         *      var Parent = Base.extend({
         *          isParent: 1
         *      });
         *      var Child = Parent.extend({
         *          isChild: 1,
         *          isParent: 0
         *      })
         */
        extend: function extend(extensions, px, sx) {
            var SuperClass = this,
                name,
                SubClass;
            if (!S.isArray(extensions)) {
                sx = px;
                px = /**@type {Object}
                 @ignore*/extensions;
                extensions = [];
            }
            sx = sx || {};
            name = sx.name || 'BaseDerived';
            px = S.merge(px);
            if (px.hasOwnProperty('constructor')) {
                SubClass = px.constructor;
            } else {
                // debug mode, give the right name for constructor
                // refer : http://limu.iteye.com/blog/1136712
                if ('@DEBUG@') {
                    eval("SubClass = function " + CamelCase(name) + "(){ " +
                        "this.callSuper.apply(this, arguments);}");
                } else {
                    SubClass = function () {
                        this.callSuper.apply(this, arguments);
                    };
                }
            }
            px.constructor = SubClass;
            // wrap method to get owner and name
            var hooks = SuperClass.__hooks__;
            if (hooks) {
                sx.__hooks__ = S.merge(hooks, sx.__hooks__);
            }
            SubClass.__extensions__ = extensions;
            wrapProtoForSuper(px, SubClass, sx.__hooks__ || {});
            var sp = SuperClass.prototype;
            // process inheritedStatics
            var inheritedStatics = sp['__inheritedStatics__'] = sp['__inheritedStatics__'] || sx['inheritedStatics'];
            if (sx['inheritedStatics'] && inheritedStatics !== sx['inheritedStatics']) {
                S.mix(inheritedStatics, sx['inheritedStatics']);
            }
            if (inheritedStatics) {
                S.mix(SubClass, inheritedStatics);
            }
            delete sx['inheritedStatics'];
            // extend
            S.extend(SubClass, SuperClass, px, sx);
            // merge extensions
            if (extensions.length) {
                var attrs = {},
                    prototype = {};
                // [ex1,ex2],扩展类后面的优先,ex2 定义的覆盖 ex1 定义的
                // 主类最优先
                S.each(extensions['concat'](SubClass), function (ext) {
                    if (ext) {
                        // 合并 ATTRS 到主类
                        // 不覆盖主类上的定义(主类位于 constructors 最后)
                        // 因为继承层次上扩展类比主类层次高
                        // 注意:最好 value 必须是简单对象,自定义 new 出来的对象就会有问题
                        // (用 function return 出来)!
                        // a {y:{value:2}} b {y:{value:3,getter:fn}}
                        // b is a extension of a
                        // =>
                        // a {y:{value:2,getter:fn}}
                        S.each(ext[ATTRS], function (v, name) {
                            var av = attrs[name] = attrs[name] || {};
                            S.mix(av, v);
                        });
                        // 方法合并
                        var exp = ext.prototype,
                            p;
                        for (p in exp) {
                            // do not mess with parent class
                            if (exp.hasOwnProperty(p)) {
                                prototype[p] = exp[p];
                            }
                        }
                    }
                });
                SubClass[ATTRS] = attrs;
                prototype.constructor = SubClass;
                S.augment(SubClass, prototype);
            }
            SubClass.extend = SubClass.extend || extend;
            SubClass.addMembers = addMembers;
            return SubClass;
        }
    });

    function addMembers(px) {
        var SubClass = this;
        wrapProtoForSuper(px, SubClass, SubClass.__hooks__ || {});
        S.mix(SubClass.prototype, px);
    }

    /**
     * The default set of attributes which will be available for instances of this class, and
     * their configuration
     *
     * By default if the value is an object literal or an array it will be 'shallow' cloned, to
     * protect the default value.
     *
     *      for example:
     *      @example
     *      {
     *          x:{
     *              value: // default value
     *              valueFn: // default function to get value
     *              getter: // getter function
     *              setter: // setter function
     *          }
     *      }
     *
     * @property ATTRS
     * @member KISSY.Base
     * @static
     * @type {Object}
     */

    function onSetAttrChange(e) {
        var self = this,
            method;
        // ignore bubbling
        if (e.target == self) {
            method = self[ON_SET + e.type.slice(5).slice(0, -6)];
            method.call(self, e.newVal, e);
        }
    }

    function addAttrs(host, attrs) {
        if (attrs) {
            for (var attr in attrs) {
                // 子类上的 ATTRS 配置优先
                // 父类后加,父类不覆盖子类的相同设置
                // 属性对象会 merge
                // a: {y: {getter: fn}}, b: {y: {value: 3}}
                // b extends a
                // =>
                // b {y: {value: 3, getter: fn}}
                host.addAttr(attr, attrs[attr], false);
            }
        }
    }

    function initAttrs(host, config) {
        if (config) {
            for (var attr in config) {
                // 用户设置会调用 setter/validator 的,但不会触发属性变化事件
                host.setInternal(attr, config[attr]);
            }
        }
    }

    function constructPlugins(self) {
        var plugins = self.get('plugins');
        S.each(plugins, function (plugin, i) {
            if (typeof plugin === 'function') {
                plugins[i] = new plugin();
            }
        });
    }

    function wrapper(fn) {
        return function () {
            return fn.apply(this, arguments);
        }
    }

    function wrapProtoForSuper(px, SubClass, hooks) {
        var extensions = SubClass.__extensions__;
        if (extensions.length) {
            for (p in hooks) {
                px[p] = px[p] || noop;
            }
        }
        // in case px contains toString
        for (var p in hooks) {
            if (p in px) {
                px[p] = hooks[p](px[p]);
            }
        }
        S.each(px, function (v, p) {
            if (typeof v == 'function') {
                var wrapped = 0;
                if (v.__owner__) {
                    var originalOwner = v.__owner__;
                    delete v.__owner__;
                    delete v.__name__;
                    wrapped = v.__wrapped__ = 1;
                    var newV = wrapper(v);
                    newV.__owner__ = originalOwner;
                    newV.__name__ = p;
                    originalOwner.prototype[p] = newV;
                } else if (v.__wrapped__) {
                    wrapped = 1;
                }
                if (wrapped) {
                    px[p] = v = wrapper(v);
                }
                v.__owner__ = SubClass;
                v.__name__ = p;
            }
        });
    }

    function callPluginsMethod(method) {
        var len,
            self = this,
            plugins = self.get('plugins');
        if (len = plugins.length) {
            for (var i = 0; i < len; i++) {
                plugins[i][method] && plugins[i][method](self);
            }
        }
    }

    function callExtensionsMethod(self, extensions, method, args) {
        var len;
        if (len = extensions && extensions.length) {
            for (var i = 0; i < len; i++) {
                var fn = extensions[i] && (
                    !method ?
                        extensions[i] :
                        extensions[i].prototype[method]
                    );
                if (fn) {
                    fn.apply(self, args || []);
                }
            }
        }
    }

    return Base;
}, {
    requires: ['base/attribute', 'event/custom']
});
/**
 * @ignore
 * 2013-08-12 yiminghe@gmail.com
 * - merge rich-base and base
 * - callSuper inspired by goto100
 */