var FiatControl = Class.create({
  initialize: function(name, command, queryCommand) {
    this.name = name;
    if (Object.isFunction(command)) {
      this.command = command;
    } else if (Object.isString(command)) {
      this.command = function() { this.editor.execCommand(command) };
      this.queryCommand = function() { return document.queryCommandState(command) };
    } else if (Object.isArray(command)) {
      this.command = function() { this.editor.execCommand(command[0], command[1]) };
      this.queryCommand = function() { return document.queryCommandState(command[0], command[1]) };
    } else {
      this.command = function() { this.editor.execCommand(this.name) };
      this.queryCommand = function() { return document.queryCommandState(this.name) };
    }
    if (Object.isFunction(queryCommand))
      this.queryCommand = queryCommand;
    else if (Object.isString(queryCommand)) {
      this.queryCommand = function() {
        var node = Selection().getRange().getNode();
        var nodeName = queryCommand.toUpperCase();
        return node.nodeName == nodeName || node.ancestors().detect(function(el) { return el.nodeName == nodeName });
      }
    }
  },
  runCommand: function(evnt) {
    this.command.bind(this)();
    return this.stopEvent(evnt);
  },
  checkState: function() {
    try {
      if (this.queryCommand)
        this.setState(this.queryCommand.bind(this)())
    } catch(e) { console.log('failed for ' + this.name); this.setState(false)};
  },
  setState: function(state) {
    if (state)
      this.element.addClassName('active');
    else
      this.element.removeClassName('active');
  },
  stopEvent: function(evnt) {
    Event.stop(evnt);
    return false;
  },
  toElement: function() {}
});
var FiatButton = Class.create(FiatControl, {
  initialize: function($super, name, command, queryCommand) {
    $super(name, command, queryCommand);
    this.icon = "/images/fiat_editor/button_" + this.name + ".png"
  },
  setupObservers: function() {
    this.element.observe('click', this.runCommand.bind(this));
    this.element.observe('mouseup', this.stopEvent);
    this.element.observe('mousedown', this.stopEvent);
  },
  setState: function(state) {
    if (state) {
      this.element.addClassName('active');
      this.element.down().src = this.icon.replace('.png', '_active.png');
    } else {
      this.element.removeClassName('active');
      this.element.down().src = this.icon;
    }
  },
  toElement: function() {
    this.element = new Element('div', {id: this.name + '_button', title: this.name.capitalize(), className: 'fiat-button'}).insert(new Element('img', {src: this.icon}));
    this.setupObservers();
    return this.element;
  }
});
var FiatSelect = Class.create(FiatControl, {
  initialize: function($super, name, options, command, queryCommand) {
    $super(name, command, queryCommand);
    this.options = options
    if (Object.isFunction(command))
      this.command = command;
    else if (Object.isString(command))
      this.command = function() { this.editor.execCommand(command, this.value()) };
    else
      this.command = function() { this.editor.execCommand(this.name, this.value()) };
  },
  setupObservers: function() {
    this.select.observe('change', this.runCommand.bind(this));
  },
  value: function() {
      return this.select.value;
  },
  setState: function(state) {
    var options = $A(this.select.options)
    this.select.selectedIndex = (options.detect(function(option) {
      return option.value.toLowerCase() == state.toLowerCase();
    }.bind(this)) || options.last()).index;
  },
  toElement: function() {
    this.select = new Element("select")
    this.options.each(function(option) {
      this.select.insert(new Element('option', {value: option}).update(option.capitalize()));
    }.bind(this));
    this.element = new Element('div', {id: this.name + '_select', title: this.name.capitalize(), className: 'fiat-button'}).insert(this.select);
    this.setupObservers();
    return this.element;
  }
});
var FiatEditor = Class.create({
  defaultOptions: {
    menuItems: [
      new FiatButton('save', function() { this.editor.save(); }),
      new FiatButton('cancel', function() { this.editor.cancel(); }),
      {section: 'Mode', items: [
        new FiatButton('p', ['formatblock', 'p'], 'p'),
        new FiatButton('h1', ['formatblock', 'h1'], 'h1'),
        new FiatButton('h2', ['formatblock', 'h2'], 'h2'),
        new FiatButton('h3', ['formatblock', 'h3'], 'h3'),
        new FiatButton('h4', ['formatblock', 'h4'], 'h4'),
        new FiatButton('h5', ['formatblock', 'h5'], 'h5'),
        new FiatButton('ul', 'insertunorderedlist', 'ul'),
        new FiatButton('ol', 'insertorderedlist', 'ol')
      ]},
      {section: 'Style', items: [
        new FiatButton('bold'),
        new FiatButton('italic'),
        new FiatButton('underline')
      ]},
      {section: 'Insert', items: [
        new FiatButton('link', function() { window.links_dialog.show(); }, 'a'),
        new FiatButton('image', function() { window.image_dialog.show(); }),
        new FiatButton('video', function() { window.video_dialog.show(); })
      ]}
    ]
  },
  initialize: function() {
    this.documentBody = $(document.body);
    this.documentBody.defaultMargin = parseFloat(this.documentBody.getStyle('margin-top'));
    this.options = Object.extend(this.defaultOptions, arguments[1] || {});
    this.initializeEditables();
    this.initializeMenu();
    this.background = new Element('div', {className: 'fiat-background'}).hide();
    this.documentBody.insert({bottom: this.background});
    this.background.defaultOpacity = this.background.getStyle('opacity');
    if (Prototype.Browser.IE)
      this.background.defaultOpacity = 0.3;
  },
  initializeEditables: function() {
    this.sections = [];
    $$('.editable').each(this.initializeEditor.bind(this))
  },
  initializeEditor: function(section) {
    if (!section.editorObj) {
      section.editorObj = this;
      section.observe('keyup', this.checkState.bind(this));
      section.observe('click', this.checkState.bind(this));
      section.observe('click', function() {this.currentSection = section;}.bind(this));
    }
    this.sections.push(section);
  },
  initializeMenu: function() {
    var buttonContainer = new Element("div", {className: "fiat-editor-buttons"});
    this.options.menuItems.reverse().each(function(item) {
      if (item.section) {
        var section = new Element('fieldset', {className: 'section'}).insert(new Element('legend').update(item.section));
        item.items.each(function(item) {
          section.insert(item);
          item.editor = this;
        }.bind(this));
        buttonContainer.insert({top: section})
      } else {
        item.editor = this;
        buttonContainer.insert({top: item});
      }
    }.bind(this));
    this.options.menuItems = this.options.menuItems.map(function(item) {return item.section ? item.items : item;}).flatten();
    this.menuContainer = new Element("div", {className: "fiat-editor-menu"}).insert(buttonContainer)
    this.menuContainer.hide();
    this.documentBody.insert({top: this.menuContainer})
  },
  checkState: function() {
    if (!this.active) return;
    if (this.range && this.range.detach)
      this.range.detach();
    this.range = Selection().getRange();
    this.options.menuItems.each(M.send('checkState'));
  },
  execCommand: function(command, args) {
    try {
      if (Prototype.Browser.IE && command == 'formatblock')
        args = "<" + args + ">";
      if (Prototype.Browser.IE)
        this.range.select();
      document.execCommand(command, false, args);
    } catch(e) {
      console.log('execCommand Failed');
    }
    this.checkState();
  },
  open: function() {
    this.initializeEditables();
    this.active = true;
    this.sections.each(function(section) {
      section.originalHTML = section.innerHTML;
      section.addClassName("fiat-editor");
      section.contentEditable = true;
      section.observe('keyup', function(event) {
        if (event.keyCode == Event.KEY_RETURN)
          this.forceParagraph();
      }.bind(this));
      section.observe('focus', function() { window.setTimeout(function() {
        this.forceParagraph();
      }.bind(this), 1) }.bind(this));
      if (section.innerHTML == '') {
        section.innerHTML = '​';
      }
      section.select('img').each(function(img) {
        if (!img.controlObj)
          new ImageControl(img);
        else
          img.controlObj.enable();
      })
    }.bind(this));
    try {
      new Fx.Style(this.background, 'opacity', {onStart: function(element) { element.setStyle({opacity: 0}); element.show();}}).custom(0, this.background.defaultOpacity);
      new Fx.Style(this.documentBody, 'margin-top').custom(this.documentBody.defaultMargin, this.documentBody.defaultMargin + 42);
      new Fx.Style(this.menuContainer, 'opacity', {onStart: function(element) { element.setStyle({opacity: 0}); element.show();}}).custom(0, 1);
      new Fx.Style('revision_bar', 'opacity', {onStart: function(element) { element.setStyle({opacity: 1}); element.show();}}).custom(1, 0);
    } catch(e) {
      this.background.show();
      this.documentBody.setStyle({marginTop: this.documentBody.defaultMargin + 42 + "px"})
      this.menuContainer.show();
      $('revision_bar').hide();
    }
  },
  forceParagraph: function() {
    var node = Selection().getRange().getNode();
    if (node && ['div', 'span'].include(node.nodeName.toLowerCase())) {
      this.execCommand('formatblock', 'p');
      
      // Prevent nested paragraphs, which happens in certain odd cases.
      var node = Selection().getRange().getNode();
      if (node.nodeName.toLowerCase() == 'p' && node.parentNode.nodeName.toLowerCase() == 'p') {
        $(node.parentNode).insert({after: node});
        var range = Selection().createRange();
        range.setStart(node);
        range.setEnd(node);
        Selection().setRange(range);
      }
    }
  },
  save: function() {
    var json = this.sections.collect(function(section) {
      section.select('img').each(function(img) {
        if (img.controlObj)
          img.controlObj.disable();
      })
      return section.innerHTML.gsub(/​/, '');
    }).toJSON();
    new Ajax.Request(window.location, {parameters: {content: json, authenticity_token: window._token}, method: 'put', asynchronous: false});
    window.location.reload();
    this.close();
  },
  cancel: function() {
    this.sections.each(function(section) {
      section.innerHTML = section.originalHTML;
    });
    this.close();
  },
  close: function() {
    this.active = false;
    this.sections.each(function(section) {
      section.blur();
      section.removeClassName("fiat-editor");
      section.contentEditable = false;
      section.select('img').each(function(img) {
        if (img.controlObj)
          img.controlObj.disable();
      })
    });
    try {
      new Fx.Style(this.menuContainer, 'opacity', {onComplete: function(element) { element.hide();}}).custom(1, 0);
      new Fx.Style(this.documentBody, 'margin-top').custom(this.documentBody.defaultMargin + 42, this.documentBody.defaultMargin);
      new Fx.Style(this.background, 'opacity', {onComplete: function(element) { element.hide();}}).custom(this.background.getStyle('opacity'), 0);
      new Fx.Style('revision_bar', 'opacity', {onStart: function(element) { element.setStyle({opacity: 0}); element.show();}}).custom(0, 1);
    } catch(e) {
      this.menuContainer.hide()
      this.documentBody.setStyle({marginTop: this.documentBody.defaultMargin + "px"})
      this.background.hide();
      $('revision_bar').show();
    } 
  }
});