define(function (require) {

    var featureManager = require('./featureManager');
    var zrUtil = require('zrender/core/util');
    var graphic = require('../../util/graphic');
    var Model = require('../../model/Model');
    var DataDiffer = require('../../data/DataDiffer');
    var listComponentHelper = require('../helper/listComponent');
    var textContain = require('zrender/contain/text');

    return require('../../echarts').extendComponentView({

        type: 'toolbox',

        render: function (toolboxModel, ecModel, api) {
            var group = this.group;
            group.removeAll();

            if (!toolboxModel.get('show')) {
                return;
            }

            var itemSize = +toolboxModel.get('itemSize');
            var featureOpts = toolboxModel.get('feature') || {};
            var features = this._features || (this._features = {});

            var featureNames = [];
            zrUtil.each(featureOpts, function (opt, name) {
                featureNames.push(name);
            });

            (new DataDiffer(this._featureNames || [], featureNames))
                .add(process)
                .update(process)
                .remove(zrUtil.curry(process, null))
                .execute();

            // Keep for diff.
            this._featureNames = featureNames;

            function process(newIndex, oldIndex) {
                var featureName = featureNames[newIndex];
                var oldName = featureNames[oldIndex];
                var featureOpt = featureOpts[featureName];
                var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);
                var feature;

                if (featureName && !oldName) { // Create
                    if (isUserFeatureName(featureName)) {
                        feature = {
                            model: featureModel,
                            onclick: featureModel.option.onclick,
                            featureName: featureName
                        };
                    }
                    else {
                        var Feature = featureManager.get(featureName);
                        if (!Feature) {
                            return;
                        }
                        feature = new Feature(featureModel);
                    }
                    features[featureName] = feature;
                }
                else {
                    feature = features[oldName];
                    // If feature does not exsit.
                    if (!feature) {
                        return;
                    }
                    feature.model = featureModel;
                }

                if (!featureName && oldName) {
                    feature.dispose && feature.dispose(ecModel, api);
                    return;
                }

                if (!featureModel.get('show') || feature.unusable) {
                    feature.remove && feature.remove(ecModel, api);
                    return;
                }

                createIconPaths(featureModel, feature, featureName);

                featureModel.setIconStatus = function (iconName, status) {
                    var option = this.option;
                    var iconPaths = this.iconPaths;
                    option.iconStatus = option.iconStatus || {};
                    option.iconStatus[iconName] = status;
                    // FIXME
                    iconPaths[iconName] && iconPaths[iconName].trigger(status);
                };

                if (feature.render) {
                    feature.render(featureModel, ecModel, api);
                }
            }

            function createIconPaths(featureModel, feature, featureName) {
                var iconStyleModel = featureModel.getModel('iconStyle');

                // If one feature has mutiple icon. they are orginaized as
                // {
                //     icon: {
                //         foo: '',
                //         bar: ''
                //     },
                //     title: {
                //         foo: '',
                //         bar: ''
                //     }
                // }
                var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon');
                var titles = featureModel.get('title') || {};
                if (typeof icons === 'string') {
                    var icon = icons;
                    var title = titles;
                    icons = {};
                    titles = {};
                    icons[featureName] = icon;
                    titles[featureName] = title;
                }
                var iconPaths = featureModel.iconPaths = {};
                zrUtil.each(icons, function (icon, iconName) {
                    var normalStyle = iconStyleModel.getModel('normal').getItemStyle();
                    var hoverStyle = iconStyleModel.getModel('emphasis').getItemStyle();

                    var style = {
                        x: -itemSize / 2,
                        y: -itemSize / 2,
                        width: itemSize,
                        height: itemSize
                    };
                    var path = icon.indexOf('image://') === 0
                        ? (
                            style.image = icon.slice(8),
                            new graphic.Image({style: style})
                        )
                        : graphic.makePath(
                            icon.replace('path://', ''),
                            {
                                style: normalStyle,
                                hoverStyle: hoverStyle,
                                rectHover: true
                            },
                            style,
                            'center'
                        );

                    graphic.setHoverStyle(path);

                    if (toolboxModel.get('showTitle')) {
                        path.__title = titles[iconName];
                        path.on('mouseover', function () {
                                path.setStyle({
                                    text: titles[iconName],
                                    textPosition: hoverStyle.textPosition || 'bottom',
                                    textFill: hoverStyle.fill || hoverStyle.stroke || '#000',
                                    textAlign: hoverStyle.textAlign || 'center'
                                });
                            })
                            .on('mouseout', function () {
                                path.setStyle({
                                    textFill: null
                                });
                            });
                    }
                    path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal');

                    group.add(path);
                    path.on('click', zrUtil.bind(
                        feature.onclick, feature, ecModel, api, iconName
                    ));

                    iconPaths[iconName] = path;
                });
            }

            listComponentHelper.layout(group, toolboxModel, api);
            // Render background after group is layout
            // FIXME
            listComponentHelper.addBackground(group, toolboxModel);

            // Adjust icon title positions to avoid them out of screen
            group.eachChild(function (icon) {
                var titleText = icon.__title;
                var hoverStyle = icon.hoverStyle;
                // May be background element
                if (hoverStyle && titleText) {
                    var rect = textContain.getBoundingRect(
                        titleText, hoverStyle.font
                    );
                    var offsetX = icon.position[0] + group.position[0];
                    var offsetY = icon.position[1] + group.position[1] + itemSize;

                    var needPutOnTop = false;
                    if (offsetY + rect.height > api.getHeight()) {
                        hoverStyle.textPosition = 'top';
                        needPutOnTop = true;
                    }
                    var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8);
                    if (offsetX + rect.width /  2 > api.getWidth()) {
                        hoverStyle.textPosition = ['100%', topOffset];
                        hoverStyle.textAlign = 'right';
                    }
                    else if (offsetX - rect.width / 2 < 0) {
                        hoverStyle.textPosition = [0, topOffset];
                        hoverStyle.textAlign = 'left';
                    }
                }
            });
        },

        remove: function (ecModel, api) {
            zrUtil.each(this._features, function (feature) {
                feature.remove && feature.remove(ecModel, api);
            });
            this.group.removeAll();
        },

        dispose: function (ecModel, api) {
            zrUtil.each(this._features, function (feature) {
                feature.dispose && feature.dispose(ecModel, api);
            });
        }
    });

    function isUserFeatureName(featureName) {
        return featureName.indexOf('my') === 0;
    }

});