var MIN_WIDTH = 400;
var MAX_WIDTH = 800;

if (!Modal) {
  var Modal = (function($) {
    var winStack = new Array, winSystemID = new Object;
    var win = function(opt, ele) {
      //debugger;
      if (opt.systemID) {
        if (winSystemID[opt.systemID]) {
          return ele ? createHandler(ele, opt) : winSystemID[opt.systemID];
        }
        winSystemID[opt.systemID] = this;
        this.systemID = opt.systemID;
      }
      if (ele) {
        return createHandler(ele, opt);
      }
      if (typeof opt.type == 'string') {
        opt = createModalType.call(this, opt, ele);
      }

      this.dom = new Object;

      if (opt.opacity == 'undefined') {
        opt.opacity = 0.2;
      }
      if (opt.opacity > 0) {
        this.dom.background = document.createElement('div');
        this.dom.background.className = 'modalBackground';
        $(this.dom.background).css({
          opacity: opt.opacity
        });
        document.body.appendChild(this.dom.background); //todo wtf???
      }

      this.dom.parent = document.createElement('div');
      this.dom.parent.className = 'stylish_popup';

      document.body.appendChild(this.dom.parent);

      this.dom.content = document.createElement('div');
      this.dom.content.className = 'body';
      this.dom.parent.appendChild(this.dom.content);

      this.dom.btnClose = document.createElement('ins');
      this.dom.btnClose.className = 'btnClose';
      $(this.dom.btnClose).bind('click', this, function(evt) {
        evt.data.triggerEvent = false;
        evt.data.hide();
      });
      this.dom.parent.appendChild(this.dom.btnClose);

      this.hideCall = opt.hideCall;
      this.showCall = opt.showCall;

//debugger;
      for (var i in opt.elements) {
        if (this.appendElement[i])
          this.appendElement[i](opt.elements[i], this);
      }

      //debugger;
      var width = (opt && opt.width) ? opt.width : 'auto';
      var height = (opt && opt.height) ? opt.height : 'auto';

      var contentDOM = $([this.dom.parent, this.dom.content]);

      contentDOM.css({
        width: width,
        height: height//,
        //minWidth: (opt && opt.minWidth) ? opt.minWidth : 'auto',
        //minHeight: (opt && opt.minHeight) ? opt.minHeight : 'auto',
        //maxWidth: (opt && opt.maxWidth) ? opt.maxWidth : 'auto',
        //maxHeight: (opt && opt.maxHeight) ? opt.maxHeight : 'auto'
      });

      /*if (width == 'auto') {
        if (contentDOM.width() < MIN_WIDTH) {
          contentDOM.css("width", MIN_WIDTH);
        }

        if (contentDOM.width() > MAX_WIDTH) {
          contentDOM.css("width", MAX_WIDTH);
        }
      }*/

      //alert($([this.dom.parent, this.dom.content]).width());
      $(this.dom.parent).css({
        left:'50%',
        top:'50%',
        display:'none'
      });
    };

    win.prototype.appendElement = {
      title: function(opt, obj) {
      //debugger;
        if (opt != '******') {
          obj.dom.title = document.createElement('div');
          obj.dom.title.className = 'modalTitle';
          $(obj.dom.title).html('<span>'+opt+'</span>');
  //        obj.dom.title.appendChild(document.createTextNode(opt));
          obj.dom.content.appendChild(obj.dom.title);
        }
        return obj;
      },
      message: function(opt, obj) {
        obj.dom.message = document.createElement('div');
        $(obj.dom.message).append(opt.dom);
        obj.dom.message.className = 'modalMessage ' + (opt.classMessageName || '');
        obj.dom.content.appendChild(obj.dom.message);
        return obj;
      },
      buttons: function(opt, obj) {
        if (!obj.dom.buttons) {
          obj.dom.buttons = document.createElement('div');
          obj.dom.buttons.className = 'modalButtons';
          obj.dom.message.appendChild(obj.dom.buttons);
        }
        for (var i = 0; i < opt.length; i++) {
          var span = document.createElement('span');
          span.className = (opt[i].active) ? 'btn_ok' : 'btn_cancel';
          span.style.cssText = 'margin: 1px';
          var button = document.createElement('a');
          button.href = '#';
          button.appendChild(document.createTextNode(opt[i].label));
          $(button).bind(opt[i].event || 'click', obj, opt[i].action);
          span.appendChild(button);
          obj.dom.buttons.appendChild(span);
        }
        return obj;
      }
    };

    win.prototype.show = function(call) {
      var data = true;
      if(typeof call == 'function'){
        data = call.call(this);
      }
      if(data && typeof this.showCall == 'function'){
        data = this.showCall();
      }
      if (data) {
        $(this.dom.background).show();
        $(this.dom.parent).css({
          visibility:'hidden',
          display:'block'
        })

        //увеличиваем ширину контейнера до конкретного значения ширины с учётом контента, который туда набили
        $(this.dom.content).css("width", $(this.dom.content).width());

        $(this.dom.parent).css({
          marginLeft: -this.dom.parent.offsetWidth / 2,
          marginTop: -this.dom.parent.offsetHeight / 2
        })
        .css({
          visibility : 'visible'
        });
        $(document).bind('keypress', keyEvent);
        this.index = winStack.length;
        winStack.push(this);
      }
      //alert("width: " + $(this.dom.background).width());
      return data;
    };

    function keyEvent(evt){
      if(evt.keyCode == 27){
        Modal.hide();
      }
    }

    win.prototype.hide = function(call) {
      var data = true;
      if (typeof call == 'function') {
        data = call.call(this);
      }
      if (data && typeof this.hideCall == 'function') {
        data = this.hideCall();
      }
      if (data) {
        winStack.splice(this.index, 1);
        for (var i = this.index; i < winStack.length; i++) {
          winStack[i].index = i;
        }
        this.index = 'undefined';
        $([this.dom.background, this.dom.parent]).hide();
      }
      if(!winStack.length){
        $(document).unbind('keypress', keyEvent);
      }
      return data;
    };

    win.prototype.remove = function(call) {
      var data = this.hide(call);
      if(data){
        $(this.dom.parent).remove();//todo wtf ??? parent is function ..... разобрался ... отстой
        if (this.dom.background) {
          $(this.dom.background).remove();
        }
      }
      return data;
    };

    win.prototype.resize = function(opt, time) {
      if (time > 0) {
        var offset = {
          start:{
            width:this.dom.content.offsetWidth - 2,
            height:this.dom.content.offsetHeight - 2
          }
        };
        if (opt.width == 'auto' || opt.height == 'auto') {
          this.dom.content.style.visibility = 'hidden';
          this.resize(opt);
          offset.end = {
            width:this.dom.content.offsetWidth - 2,
            height:this.dom.content.offsetHeight - 2
          };
          this.resize({
            width: offset.start.width,
            height: offset.start.height
          });
          this.dom.content.style.visibility = 'visible';
        } else {
          offset.end = {
            width:opt.width || offset.start.width,
            height:opt.height || offset.start.height
          };
        }

        $([this.dom.content, this.dom.parent]).animate({
          width:offset.end.width,
          height:offset.end.height
        }, {
          duration:time
        });
        $(this.dom.parent).animate({
          marginLeft:-(offset.end.width + 20) / 2,
          marginTop:-(offset.end.height + 20) / 2
        }, {
          queue:false,
          duration:time
        });
        if (this.dom.frame) {
          $(this.dom.frame).animate({
            width: offset.end.width,
            height: offset.end.height - this.dom.title.offsetHeight
          }, {
            queue:false,
            duration:time
          });
        }
        return this;
      }
      if (this.dom.frame) {
        if (opt && opt.width) {
          if (opt.width == 'auto')
            this.dom.frame.style.width = 'auto';
          this.dom.frame.style.width = opt.width == 'auto' ? this.dom.frame.contentWindow.document.body.scrollWidth + 'px' : opt.width + 'px';
        }
        if (opt && opt.height) {
          if (opt.height == 'auto')
            this.dom.frame.style.height = 'auto';
          this.dom.frame.style.height = opt.height == 'auto' ? this.dom.frame.contentWindow.document.body.scrollHeight + 'px' : opt.height - this.dom.title.offsetHeight + 'px';
        }
      }
      if (opt && opt.width) {
        $([this.dom.parent, this.dom.content]).css({
          width: opt.width
        });
      }
      if (opt && opt.height) {
        $([this.dom.parent, this.dom.content]).css({
          height: opt.height
        });
      }
      $(this.dom.parent).css({
        marginLeft: -this.dom.parent.offsetWidth / 2,
        marginTop: -this.dom.parent.offsetHeight / 2,
        left: '50%',
        top: '50%'
      });
      return this;
    };

    function createModalType(opt) {
    //debugger;
      var modalProp = {
        width: opt.width || 'auto',
        height: opt.height || 'auto',
        //minWidth: opt.minWidth || 'auto',
        //minHeight: opt.minHeight || 'auto',
        //maxWidth: opt.maxWidth || 'auto',
        //maxHeight: opt.maxHeight || 'auto',
        opacity: opt.opacity || 0.2,
        showCall: opt.showCall,
        hideCall: opt.hideCall,
        noTitle: opt.noTitle,
        stopEvent:opt.hasOwnProperty('stopEvent') ? opt.stopEvent : true,
        triggerEvent: (opt.hasOwnProperty('triggerEvent') && opt.triggerEvent === true),
        elements: {
          title : opt.noTitle ? '******' : (opt.title && opt.title.length) ? opt.title : ((opt.ele && opt.ele.getAttribute('title')) ? opt.ele.getAttribute('title') : 'Message')
        },
        ele:opt.ele,
        eventType:opt.eventType,
        systemID:opt.systemID
      };
      switch (opt.type) {
        case 'confirm':
          modalProp.elements.message = {
            dom:opt.content,
            classMessageName:opt.classMessageName || opt.type
          };
          modalProp.elements.buttons = [
            {
              label: opt.btnOkLabel || l10n.main.ok,
              active: true,
              action: function(evt) {
                if (opt.onOk)
                  opt.onOk.call(evt.data.dom.buttons[0], evt);
                evt.data.triggerEvent = true;
                evt.preventDefault();
                evt.data.hide();
              }
            },
            {
              label: opt.btnCancelLabel || l10n.main.cancel,
              active: false,
              action: function(evt) {
                if (opt.onCancel)
                  opt.onCancel.call(evt.data.dom.buttons[1], evt);
                evt.data.triggerEvent = false;
                evt.preventDefault();
                evt.data.hide();
              }
            }
          ];
          function pressEnter(evt){
            if(evt.keyCode == 13){
              $(evt.data.dom.buttons.firstChild).trigger('click');
              return false;
            }
          }
          modalProp.showCall = function() {
            if (opt.showCall && !opt.showCall.call(this)) {
              return false;
            }
            $(document).bind('keydown', this, pressEnter);
            return true;
          };
          modalProp.hideCall = function() {
            if (opt.hideCall && !opt.hideCall.call(this)) {
              return false;
            }
            $(document).unbind('keydown', pressEnter);
            return true;
          };
          break;
        case 'frame':
          var frame = document.createElement('iframe');

          frame.frameBorder = 0;
          $(frame).css({
            width: opt.width || 'auto',
            height: opt.height - 38 || 'auto'
          });

          $(frame).bind('load', function() {
            frame.style.visibility = 'visible';
            if (frame.contentWindow){
              frame.contentWindow.Modal = Modal;
              $(frame.contentWindow.document.body).css({
                background: '#fff'
              });
            }
          });

          this.src = opt.src;

          modalProp.showCall = function() {
            frame.style.visibility = 'hidden';
            if (!this.dom.frame) {
              this.dom.content.appendChild(frame);
              this.dom.frame = frame;
            }
            if (opt.showCall && !opt.showCall.call(this)) {
              return false;
            }
            this.dom.frame.src = this.src || opt.src || opt.ele.href;
            return true;
          };
          break;
        case 'info':
            modalProp.stopEvent = true;
            modalProp.showCall = function() {
              if(opt.showCall && !opt.showCall.call(this)){
                return false;
              }
              var ele = typeof opt.id == 'string' ? document.getElementById(opt.id) : $(opt.id)[0];
              this.tempEle = {
                child: ele,
                parent: ele.parentNode,
                after: ele.nextSibling
              };
              this.dom.content.appendChild(ele);

              $(ele).show();
              return true;
            };
            modalProp.hideCall = function() {
              if(opt.hideCall && !opt.hideCall.call(this)){
                return false;
              }
              if (this.tempEle.after && this.tempEle.parent)
                this.tempEle.parent.insertBefore(this.tempEle.child, this.tempEle.after);
              else
                document.body.appendChild(this.tempEle.child);
              $(this.tempEle.child).hide();
              return true;
            };
          break;
        case 'alert':
          modalProp.elements.message = {
            dom:opt.content,
            classMessageName:opt.classMessageName || ''
          };
          modalProp.elements.buttons = [
            {
              label: opt.btnOkLabel || l10n.main.ok,
              active: true,
              action: function(evt) {
                if (opt.onOk)
                  opt.onOk.call(evt.data.dom.buttons[0], evt);
                evt.data.triggerEvent = true;
                evt.data.hide();
                return false;
              }
            }
          ];
        break;
      }
      return modalProp;
    }


    var handler = (function(){
      var event, modal, onEvent, opt;
      function newShowCall(){
        return (modal.oldShow) ? modal.oldShow.apply(this, arguments) : true;
      }
      function newHideCall(){
        var resultHide = true;
        if(modal.oldHide){
          resultHide = modal.oldHide.apply(this, arguments);
        }
        if(modal.triggerEvent){
          $(opt.ele)
            .bind(opt.eventType, {
              modal:modal,
              onEvent:onEvent,
              opt:opt
            } , handler)
            .trigger(opt.eventType, true);
        }
        return resultHide;
      }
      return function(evt, trigger){
        event = evt;
        modal = evt.data.modal;
        onEvent = evt.data.onEvent;
        opt = evt.data.opt;

        if(modal.stopEvent && modal.showCall != newShowCall){
          modal.oldShow = modal.showCall;
          modal.showCall = newShowCall;
        }
        if(modal.triggerEvent && modal.hideCall != newHideCall){
          modal.oldHide = modal.hideCall;
          modal.hideCall = newHideCall;
        }

        if (trigger) {
          if(onEvent) {
            onEvent.apply(this, [event, this]);
          }
          if (this.tagName == 'A' && this.getAttribute('href') && this.getAttribute('href')!='#' && this.getAttribute('href').length > 1)location.href = this.href;
          else if (this.form && this.type == 'submit')this.form.submit();
          else if (this.form && this.type == 'reset')this.form.reset();
          $(this).bind(opt.eventType, {
            modal:modal,
            onEvent:onEvent,
            opt:opt
          }, handler);
        } else {
          if(modal.showCall != newShowCall){
            modal.oldShow = modal.showCall;
            modal.showCall = newShowCall;
          }
          if(modal.hideCall != newHideCall){
            modal.oldHide = modal.hideCall;
            modal.hideCall = newHideCall;
          }
          modal.show();
          if(modal.stopEvent){
            event.stopImmediatePropagation();
            $(opt.ele.parentNode).trigger(opt.eventType);
            return false;
          }
          if(onEvent) {
            onEvent.call(opt.ele, this);
          }
        }
      }
    })();

    var createHandler = function(ele, opt) {
      if (!opt){
        eval('var opt={' + $(ele).attr('modal') + '}');
      }

      if (!opt.eventType){
        opt.eventType = 'click';
      }

      opt.ele = ele;
      if(!winSystemID[opt.systemID] && typeof opt.type == 'string'){
        opt = createModalType(opt);
      }

//debugger;
      var newModal = (opt && winSystemID[opt.systemID]) ? winSystemID[opt.systemID] : new Modal(opt);

      if(newModal.stopEvent == undefined){
        newModal.stopEvent = opt.stopEvent;
      }
      if(newModal.triggerEvent == undefined){
        newModal.triggerEvent = opt.triggerEvent;
      }

      var onEvent = $(opt.ele).attr('on' + opt.eventType);
      $(opt.ele).removeAttr('on' + opt.eventType);
      $(ele)
        .unbind('click', handler)
        .bind(opt.eventType, {
          modal:newModal,
          onEvent:onEvent,
          opt:opt
        }, handler)
        .bind('focus', function() {
          this.blur();
        });

      return newModal;
    };

    function callMethod(data, index, methodName){
      if(!winStack.length){
        return;
      }
      var winCall = new Array;
      if(typeof index == 'number'){
        winCall.push(index);
      } else if(!index){
        winCall.push(winStack.length-1);
      } else if(index === 'all'){
        winCall.length = winStack.length-1;
      } else if($.isArray(index)){
        winCall = index;
      }
      for(var j = 0; j < winCall.length; j++){
        var obj = winStack[ winCall[j] || j ];
        obj[methodName].apply(obj, (data && $.isArray(data)) ? data : [data]);
      }
    }

    for(var i in win.prototype){
      if(typeof(win.prototype[i]) == 'function'){
        win[i] = (function(methodName){
          return function(data, index){
            callMethod(data, index, methodName);
          }
        })(i);
      }
    }

    win.updateListener = function(){
      $('*[modal]').each(function() {
        createHandler(this, null);
      });
    };

    $(document).ready(win.updateListener);

    return win;
  })(jQuery);
}