(function($, box) {
    
    box.addPluginUI('popin', {
        version: '0.7',
        require: 'box 0.4.1',
        
        create: function(datas) {
            if(typeof datas.id == 'string') {
                var id = 'popin.' + datas.id;
                return (box.ui('plugin.' + id) || box.addPluginUI(id, new Popin(datas)));
            }
        },
        
        destroy: function() {
            var i = arguments.length, bId, id, pId;
            while(i--) {
                bId = arguments[i];
                id = 'popin.' + arguments[i];
                pId = 'plugin.' + id;
                if(box.ui(pId)) {
                    removeOpenClick(box.ui(pId));
                    box.ui('generic').destroy(bId + 'Popin', bId + 'Mask', bId + 'Loader');
                    removeBindings(box.ui(pId));
                    box.removePluginUI(id);
                }
            }
        }
    });
    
    var createMask = function(popin) {
        if(popin.mask && popin.mask.html) {
            box.ui('generic').create({
                id: popin.id + 'Mask'
            }).setContent({
                html: popin.mask.html
            });
            
            popin.mask.ui = box.ui('generic.' + popin.id + 'Mask');
        }
    };
    
    var createLoader = function(popin) {
        if(popin.loader && popin.loader.html) {
            box.ui('generic').create({
                id: popin.id + 'Loader'
            }).setContent({
                html: popin.loader.html
            });
            
            popin.loader.ui = box.ui('generic.' + popin.id + 'Loader');
        }
    };
    
    var addOpenClick = function(popin) {
        if(popin.cfg.openTrigger) {
            $(popin.cfg.openTrigger).bind('click.popin', function(e) {
                clickToOpen(e, this, popin);
            });
        } else if(popin.cfg.openDelegate) {
            box.addClick({
                id: popin.id + 'PopinOpen',
                deepness: popin.cfg.openDelegate.deepness,
                test: popin.cfg.openDelegate.test,
                action: function(e, element) {
                    clickToOpen(e, element, popin);
                }
            });
        }
    };
    
    var removeOpenClick = function(popin) {
        if(popin.cfg.openTrigger) {
            $(popin.cfg.openTrigger).unbind('click.popin');
        } else if(popin.cfg.openDelegate) {
            box.removeClick(popin.id + 'PopinOpen');
        }
    };
    
    var clickToOpen = function(e, element, popin) {
        e.preventDefault();
        if(popin.isAvailable()) {
            var datas = $(element).getBoxDatas();
            datas.url = element.href;
            datas.cache = datas.cache == 'false' ? false : true;
            popin.open(datas);
        }
    };
    
    // @fixme how to deal with multiple popins
    var addCloseClick = function(popin) {
        if(popin.cfg.closeTrigger) {
            $(popin.cfg.closeTrigger).click(function(e) {
                e.preventDefault();
                $(this).unbind('click');
                popin.close();
            });
        } else if(popin.cfg.closeDelegate) {
            box.addClick({
                id: popin.id + 'PopinClose',
                deepness: popin.cfg.closeDelegate.deepness,
                test: popin.cfg.closeDelegate.test,
                action: function(e, element) {
                    e.preventDefault();
                    box.removeClick(popin.id + 'PopinClose');
                    popin.close();
                }
            })
        }
    };
    
    var addBindings = function(popin) {
        var bindings = {}, uiPopinId = 'generic.' + popin.id + 'Popin', uiMaskId, uiLoaderId;
        
        if(popin.mask) {
            uiMaskId = 'generic.' + popin.mask.ui.id;
        }
        if(popin.loader) {
            uiLoaderId = 'generic.' + popin.loader.ui.id;
        }
        
        if(popin.cfg.openDuration) {
            bindings['addtodom.' + uiPopinId] = function(e) {
                addCloseClick(popin);
            };
            
            bindings['open.' + uiPopinId] = function(e) {
                popin.opened = true;
                box.ui('escape').create(popin.id + 'Popin');
            };
        } else {
            bindings['addtodom.' + uiPopinId] = function(e) {
                addCloseClick(popin);
                popin.opened = true;
                box.ui('escape').create(popin.id + 'Popin');
            };
        }
        
        if(!popin.mask && !popin.loader) {
            bindings['contentready.' + uiPopinId] = function(e) {
                openPopin(popin);
            };
        }
        
        if(popin.mask && !popin.loader) {
            bindings['contentready.' + uiPopinId] = function(e) {
                if(popin.mask.ui.isAvailable()) {
                    openPopin(popin);
                }
            };
            
            bindings[(popin.mask.openDuration ? 'open.' : 'addtodom.') + uiMaskId] = function(e) {
                if(box.ui(uiPopinId).isContentReady()) {
                    openPopin(popin);
                }
            };
        }
        
        if(!popin.mask && popin.loader) {
            bindings['contentloaded.' + uiPopinId] = function(e) {
                if(popin.loader.ui.isAvailable()) {
                    closeLoader(popin);
                }
            };
            
            bindings[(popin.loader.openDuration ? 'open.' : 'addtodom.') + uiLoaderId] = function(e) {
                if(box.ui(uiPopinId).isContentLoaded()) {
                    closeLoader(popin);
                }
            };
            
            bindings[(popin.loader.openDuration ? 'close.' : 'removefromdom.') + uiLoaderId] = function(e) {
                if(e.source.isInDOM()) {
                    e.source.removeFromDOM();
                }
                if(box.ui(uiPopinId).isContentLoaded()) {
                    openPopin(popin);
                }
            };
        }
        
        if(popin.mask && popin.loader) {
            bindings['contentloaded.' + uiPopinId] = function(e) {
                if(popin.mask.ui.isAvailable() && popin.loader.ui.isAvailable()) {
                    closeLoader(popin);
                }
            };
            
            bindings[(popin.mask.openDuration ? 'open.' : 'addtodom.') + uiMaskId] = function(e) {
                if(box.ui(uiPopinId).isContentLoaded()) {
                    openPopin(popin);
                } else {
                    openLoader(popin);
                }
            };
            
            bindings[(popin.loader.openDuration ? 'open.' : 'addtodom.') + uiLoaderId] = function(e) {
                if(box.ui(uiPopinId).isContentLoaded()) {
                    closeLoader(popin);
                }
            };
            
            bindings[(popin.loader.closeDuration ? 'close.' : 'removefromdom.') + uiLoaderId] = function(e) {
                if(e.source.isInDOM()) {
                    e.source.removeFromDOM();
                }
                if(box.ui(uiPopinId).isContentLoaded()) {
                    openPopin(popin);
                }
            };
        }
        
        if(!popin.mask) {
            if(popin.cfg.closeDuration) {
                bindings['close.' + uiPopinId] = function(e) {
                    e.source.removeFromDOM();
                    popin.opened = false;
                    if(popin.pending) {
                        getPopin(popin, box.clone(popin.pending));
                        popin.pending = null;
                        if(popin.loader) {
                            openLoader(popin);
                        }
                    }
                }
            }
        } else {
            if(popin.cfg.closeDuration) {
                bindings['close.' + uiPopinId] = function(e) {
                    e.source.removeFromDOM();
                    popin.opened = false;
                }
            }
            
            bindings['removefromdom.' + uiPopinId] = function(e) {
                if(popin.pending) {
                    getPopin(popin, box.clone(popin.pending));
                    popin.pending = null;
                    if(popin.loader) {
                        openLoader(popin);
                    }
                } else {
                    closeMask(popin);
                }
            }
            
            if(popin.mask.closeDuration) {
                bindings['close.' + uiMaskId] = function(e) {
                    e.source.removeFromDOM();
                }
            }
        }
        
        bindings['closefromkey.escape.' + popin.id + 'Popin'] = function(e) {
            closePopin(popin);
        };
        
        box.bind(bindings);
    };
    
    var removeBindings = function(popin) {
        var uiPopinId = 'generic.' + popin.id + 'Popin', uiMaskId, uiLoaderId;
        
        box.unbind(
            'open.' + uiPopinId,
            'close.' + uiPopinId,
            'closefromkey.escape.' + popin.id + 'Popin'
        );
        
        if(popin.mask) {
            uiMaskId = 'generic.' + popin.mask.ui.id;
            
            box.unbind('open.' + uiMaskId, 'close.' + uiMaskId);
        }
        
        if(popin.loader) {
            uiLoaderId = 'generic.' + popin.loader.ui.id;
            
            box.unbind('open.' + uiLoaderId, 'close.' + uiLoaderId);
        }
    };
    
    var openMask = function(popin) {
        popin.mask.ui
            .addToDOM({target: popin.mask.insertPoint, method: popin.mask.insertMethod})
            .applyStyles(popin.mask.insertStyles);
        if(popin.mask.openDuration) {
            popin.mask.ui.applyStyles(popin.mask.openStyles, {duration: popin.mask.openDuration, endEventType: 'open'});
        }
    };
    
    var closeMask = function(popin) {
        if(popin.mask.closeDuration) {
            popin.mask.ui.applyStyles(popin.mask.closeStyles, {duration: popin.mask.closeDuration, endEventType: 'close'});
        } else {
            popin.mask.ui.removeFromDOM();
        }
    };
    
    var openLoader = function(popin) {
        popin.loader.ui
            .addToDOM({target: popin.loader.insertPoint, method: popin.loader.insertMethod})
            .applyStyles(popin.loader.insertStyles);
        if(popin.loader.openDuration) {
            popin.loader.ui.applyStyles(popin.loader.openStyles, {duration: popin.loader.openDuration, endEventType: 'open'});
        }
    };
    
    var closeLoader = function(popin) {
        if(popin.loader.closeDuration) {
            popin.loader.ui.applyStyles(popin.loader.closeStyles, {duration: popin.loader.closeDuration, endEventType: 'close'});
        } else {
            popin.loader.ui.removeFromDOM();
        }
    };
    
    var getPopin = function(popin, datas) {
        popin.current = datas;
        box.fire({type: 'beforeopen', component: 'plugin.popin', id: popin.id, source: popin});
        box.ui('generic.' + popin.id + 'Popin').add(popin.current.id).setContent({
            url: popin.current.url,
            cache: popin.current.cache,
            timeout: popin.loader && popin.loader.timeout
        });
    };
    
    var openPopin = function(popin) {
        box.ui('generic.' + popin.id + 'Popin')
            .applyStyles({visibility: 'hidden'})
            .addToDOM({target: popin.cfg.insertPoint, method: popin.cfg.insertMethod})
            .applyStyles(popin.cfg.insertStyles);
        if(popin.cfg.openDuration) {
			try{
            box.ui('generic.' + popin.id + 'Popin').applyStyles(popin.cfg.openStyles, {duration: popin.cfg.openDuration, endEventType: 'open'});
			}catch(err){};
        }
    };
    
    var closePopin = function(popin) {
        popin.current = null;
        if(popin.cfg.closeDuration) {
			try{
            box.ui('generic.' + popin.id + 'Popin').applyStyles(popin.cfg.closeStyles, {duration: popin.cfg.closeDuration, endEventType: 'close'});
			}catch(err){};
        } else {
            box.ui('generic.' + popin.id + 'Popin').removeFromDOM();
        }
    };
    
    var Popin = function(datas) {
        this.initialize(datas);
    };
    Popin.prototype = {
        initialize: function(datas) {
            this.id = datas.id;
            this.cfg = datas.popin;
            this.mask = datas.mask;
            this.loader = datas.loader;
            
            createMask(this);
            createLoader(this);
            
            box.ui('generic').create({id: this.id + 'Popin'});
            this.cfg.insertStyles.visibility = 'visible';
            
            addBindings(this);
            addOpenClick(this);
        },
        
        open: function(datas) {
            if(!datas.id) {
                datas.id = 'default';
            }
            if(!this.opened) {
                getPopin(this, datas);
                if(this.mask) {
                    openMask(this);
                } else if(this.loader) {
                    openLoader(this);
                }
            } else {
                this.pending = datas;
                this.close();
            }
        },
        
        close: function() {
            if(box.ui('generic.' + this.id + 'Popin').isInDOM()) {
                closePopin(this);
            }
        },
        
        error: function(datas) {
            if(this.isAvailable()) {
                box.ui('generic.' + this.id + 'Popin').add('error').setContent({html: datas});
            }
        },
        
        isAvailable: function() {
            return box.ui('generic.' + this.id + 'Popin').isAvailable();
        }
    };
    
})(jQuery, box);

