// Copyright (c) 2006 MapBuzz 

/*jsl:import ../../../client/src/base/namespace.js*/ 

namespace('MapBuzz')


// =====  CommentMessageCommand  =====
MapBuzz.Command.CommentMessage = function CommentMessageCommand(message, level)
{
  this.callSuper(MapBuzz.Command.CommentMessage, 'Comment Message')
  this.key = MapBuzz.Comments.MESSAGE
  this.data = {message: message, level: level}
}
Function.inherits(MapBuzz.Command.CommentMessage, MapBuzz.Command.AbstractCommand)

Object.extend(MapBuzz.Command.CommentMessage.prototype,
{
  execute: function()
  {
    MapBuzz.databus.send(this, this.key, this.data)
  }
})

// =====  Comments  =====
MapBuzz.Comments = function MapBuzzComments()
{
  this.editor = null    
  this.initialize()
}

MapBuzz.Comments.MESSAGE = 'COMMENT_MESSAGE'

Object.extend(MapBuzz.Comments.prototype,
{
  initialize: function()
  {
    this.commentsElement = document.getElementById('comments')
    this.discussionElement = document.getElementById('discussion')
    this.formElement = document.getElementById('comment_form')

    this.statusElement = this.findElement(this.discussionElement, 'div', 'message')
    this.cancelButton = this.findFormElement(this.formElement, 'input', 'cancel')
    
    var permissions = new MapBuzz.Resource.Permissions(true, true, true)
    this.resource = new MapBuzz.Resource.Remote(this.formElement.action, permissions)
    
    var subjectInput = this.findFormElement(this.formElement, 'input', 'subject')
    this.defaultSubject = subjectInput.value 
    
    var tagInput = this.findFormElement(this.formElement, 'input', 'tags')
    this.defaultTags = tagInput.value 
    
    this.setupEvents()
  },
  
  release: function()
  {
    this.releaseEvents()
    
    this.resetForm()
      
    this.discussionElement = null
    this.commentsElementElement = null
    this.formElement = null
  },
  
  setupEvents: function()
  {
    this.submitHandler = this.onSubmit.bindAsEventListener(this)
    Event.observe(this.formElement, 'submit', this.submitHandler, false)
    
    this.clickHandler = this.onClick.bindAsEventListener(this)
    Event.observe(this.discussionElement, 'click', this.clickHandler, false)

    this.cancelHandler = this.onCancel.bindAsEventListener(this)
    Event.observe(this.cancelButton, 'click', this.cancelHandler, false)
  },

  releaseEvents: function()
  {
    Event.stopObserving(this.formElement, 'submit', this.submitHandler, false)
    this.submitHandler = null

    Event.stopObserving(this.discussionElement, 'click', this.clickHandler, false)
    this.clickHandler = null

    Event.stopObserving(this.cancelButton, 'click', this.cancelHandler, false)
    this.cancelHandler = null
  },
      
  findFormElement: function(formElement, tagName, elementName)
  {
    var elements = formElement.getElementsByTagName(tagName)
    
    var regex = new RegExp(elementName, 'i')
    for (var i=0; i<elements.length; i++)
    {
      var element = elements[i]
      
      var name = element.name
      var id = element.id
      
      if ((name && name.match(regex)) ||
          (id && id.match(regex)))
        return element
    }
    return null
  },
  
  findElement: function(parentElement, tagName, className)
  {
    var result = null
    var elements = parentElement.getElementsByTagName(tagName)
    
    if (!className && elements.length == 1)
    {
      result = elements[0]
    }
    else if (className)
    {
      var regex = new RegExp(className, 'i')
      
      for (var i=0; i<elements.length; i++)
      {
        var element = elements[i]
        
        if (element.className.match(regex))
        {
          result = element
          break
        }
      }
    }
    return result
  },

  onSubmit: function(event)
  {
    Event.stop(event)
    
    // get the form data
    this.editor.save()
    var requestOptions = {parameters: this.formElement.serialize()}
    
    // now reset the form
    this.resetForm()

    // now submit the request    
    this.command.execute(requestOptions)
    
    return false
  },
  
  onClick: function(event)
  {
    var target = Event.element(event)

    // Did the user click on a link or button?
    var tagName = target.tagName
    
    if (!tagName || !tagName.match(/a|button/i))
      return
    else if (!target.id)
      return
      
    // Did the user click on a comment link?
    var match = target.id.match(/(create|edit|reply|delete|restore)_comment/)
    if (!match) return

    // Yes - stop the event's default action    
    Event.stop(event)

    /* Sometimes a link can be clicked multiple times - and we definitely don't 
    want to create mulitple editors. */
    if (this.editor)
      return
     
    this.resetForm()

    switch (match[1])
    {
      case 'create':
        this.clickCreateComment(target)
        break
      case 'edit':
        this.clickEditComment(target)
        break
      case 'reply':
        this.clickReplyComment(target)
        break
      case 'delete':
        this.clickDeleteComment(target)
        break
      case 'restore':
        this.clickRestoreComment(target)
        break
      default:
        throw new Error('Unknown comment action - ' + target.id)
    }
  },
  
  onCancel: function()
  {
    this.resetForm()
  },
  
  findParentElement: function(element, tagName, className)
  {
    var tagNameRegEx = null
    var classNameRegEx = null
    
    if (tagName)
      tagNameRegEx = new RegExp('\\b' + tagName + '\\b', 'i')
      
    if (className)      
      classNameRegEx = new RegExp('\\b' + className + '\\b', 'i')
    
    while (element)
    {
      var found = 0
      
      if (tagName)
      {
        if (element.tagName.match(tagNameRegEx))
          found += 1
        else
          found -= 1
      }
                
      if (className)
      {
        if (element.className.match(classNameRegEx))
          found += 1
        else
          found -= 1
      }
      
      // Hack to work around IE's immutable text nodes
      if (found > 0)
        return element
      else if (element.nodeType == 9) // document element
        return null
      else 
        element = element.parentNode
       
    }
    return null
  },
  
  getCommentElement: function(element)
  {
    return this.findParentElement(element, 'div', 'comment')
  },

  getCommentBody: function(commentElement)
  {  
    return this.findElement(commentElement, 'div', 'comment_main')
  },
      
  showCommentBody: function()
  {
    // Show any previously hidden elements    
    
    var parentNode = this.formElement.parentNode
    if (!parentNode.className.match(/comment/i)) return
    
    var tagName = parentNode.tagName
    if (tagName && tagName.match(/div/i))
    {
      var childNodes = parentNode.childNodes
      for (var i=0; i<childNodes.length; i++)
      {
        var element = childNodes[i]
        /* Text nodes don't have styles */
        if (element.style)
          element.style['display'] = ''
      }
    }
  },
    
  hideCommentBody: function(commentElement)
  {  
    var commentMainElement = this.getCommentBody(commentElement)
    
    var childNodes = commentMainElement.childNodes
    for (var i=0; i<childNodes.length; i++)
    {
      var element = childNodes[i]
      /* Text nodes don't have styles */
      if (element.style)
        element.style['display'] = 'none'
    }
  },

  getCollectionUri: function()
  {
    var formElement = this.getCollectionForm()
    return formElement.action
  },

  getEditUri: function(commentElement)
  {
    var formElement = this.getCommentForm(commentElement)
    return formElement.action
  },
  
  getCommendId: function(commentElement)
  {
    var match = commentElement.id.match(/comment_(\d+)/)
    return match[1]
  },    
  
  setupStatus: function(nextSibling)
  {
    /*this.statusElement.style['display'] = 'none'*/
    var parent = nextSibling.parentNode
    parent.insertBefore(this.statusElement, nextSibling)
  },
  
  unescapeValue: function(value)
  {
    /*value = value.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"')*/
    value = value.replace(/&amp;/g, '&').replace(/&nbsp;/g, ' ')
    value = value.replace(/&#(\d\d);/g, function(fullMatch, subMatch, post, string)
                                     {
                                        return String.fromCharCode(subMatch)
                                     })
    return value                             
  },
  
  showForm: function()
  {
    var subjectInput = this.findFormElement(this.formElement, 'input', 'subject')
    this.formElement.style['display'] = 'block'
    this.editor = new MapBuzz.UI.TextEditor('mceEditor', {theme_advanced_buttons1: MapBuzz.UI.TextEditor.CONDENSED_BUTTONS})
    MapBuzz.databus.send(MapBuzz.Message.CLEAR_MESSAGE, {level: MapBuzz.Message.PROCESSING})
    subjectInput.focus()
  },
  
  resetForm: function()
  { 
    if (this.editor)
      this.editor.release()

    this.editor = null
      
    // Show any hidden comments
    this.showCommentBody()
    
    // Now hide the form
    this.formElement.style['display'] = 'none'
        
    // Reset the form values      
    var subjectElement = this.findFormElement(this.formElement, 'input', 'subject')  
    subjectElement.value = this.defaultSubject

    var parentElement = this.findFormElement(this.formElement, 'input', 'subject')  
    parentElement.value = ''

    var tagsElement = this.findFormElement(this.formElement, 'input', 'subject')  
    tagsElement.value = this.defaultTags
    
    // Move the form to the discussion element so that
    // it doesn't get deleted by an Ajax replace 
    // element command.    
    if (this.formElement.parentNode != this.discussionElement)
      this.discussionElement.appendChild(this.formElement)
  },

  getCollectionForm: function()
  {
    return this.findElement(this.discussionElement, 'form', 'comment_collection_form')
  },
  
  getCommentForm: function(commentElement)
  {
    return this.findElement(commentElement, 'form', 'comment_edit_form')
  },
  
  populateForm: function(sourceFormElement, newComment)
  {
    var sourceSubjectElement = this.findFormElement(sourceFormElement, 'input', 'subject')
    var sourceTagsElement = this.findFormElement(sourceFormElement, 'input', 'tags')
    var sourceIdElement = this.findFormElement(sourceFormElement, 'input', 'id')
    var sourceArticleElement = this.findFormElement(sourceFormElement, 'input', 'article')

    var destinationSubjectElement = this.findFormElement(this.formElement, 'input', 'subject')
    var destinationTagsElement = this.findFormElement(this.formElement, 'input', 'tags')
    var destinationParentIdElement = this.findFormElement(this.formElement, 'input', 'parent_id')
    var destinationArticleElement = this.findFormElement(this.formElement, 'textarea', 'mceEditor')

    var value = Form.Element.getValue(sourceSubjectElement)
    value = this.unescapeValue(value)
    destinationSubjectElement.value = value

    value = Form.Element.getValue(sourceTagsElement)
    value = this.unescapeValue(value)
    destinationTagsElement.value = value

    value = Form.Element.getValue(sourceIdElement)
    value = this.unescapeValue(value)
    destinationParentIdElement.value = value
    
    if (newComment)
    {
      value = ''
      destinationArticleElement.value = value
    }
    else      
    {
      value = Form.Element.getValue(sourceArticleElement)
      value = this.unescapeValue(value)
      destinationArticleElement.value = value
    }    
  },

  clickCreateComment: function(linkElement)
  {
    var commitElement = this.findFormElement(this.formElement, 'input', 'commit')
    commitElement.value = 'Create'
    this.resource.uri = this.getCollectionUri()
    
    // Setup command for POSTing a new comment
    this.command = MapBuzz.Command.Build(new MapBuzz.Command.CommentMessage('Creating comment', MapBuzz.Message.PROCESSING),
                                         this.resource.postCommand(),
                                         new MapBuzz.Command.InsertElement('comments', Insertion.Top),
                                         new MapBuzz.Command.Databus(MapBuzz.Message.CLEAR_MESSAGE, {level: MapBuzz.Message.PROCESSING}))
    
    var subHeaderElement = linkElement.parentNode
    subHeaderElement.parentNode.insertBefore(this.formElement, subHeaderElement.nextSibling)
    this.populateForm(this.getCollectionForm(), true)
    this.showForm()
    
    // Setup the status window
    this.setupStatus(this.formElement)
  },
  
  clickReplyComment: function(linkElement)
  {
    var commitElement = this.findFormElement(this.formElement, 'input', 'commit')
    commitElement.value = 'Create'
    
    var commentElement = this.getCommentElement(linkElement, 'div', 'comment')
    
    this.resource.uri = this.getCollectionUri()

    // Setup command for POSTing a reply comment
    this.command = MapBuzz.Command.Build(new MapBuzz.Command.CommentMessage('Creating comment', MapBuzz.Message.PROCESSING),
                                         this.resource.postCommand(),
                                         new MapBuzz.Command.InsertElement(commentElement, Insertion.After),
                                         new MapBuzz.Command.Databus(MapBuzz.Message.CLEAR_MESSAGE, {level: MapBuzz.Message.PROCESSING}))

    // Setup the editor form and display it    
    this.populateForm(this.getCommentForm(commentElement), true)
    
    // Setup the parent id    
    var match = commentElement.id.match(/comment_(\d+)/)
    var commentId = match[1]

    var parentElement = this.findFormElement(this.formElement, 'input', 'parent')
    parentElement.value = commentId
    
    // Setup form
    var linksElement = this.findParentElement(linkElement, 'div')
    linksElement.parentNode.appendChild(this.formElement)
    this.showForm()
    
    // Setup status
    this.setupStatus(commentElement)
  },
  
  clickEditComment: function(linkElement)
  {
    var commitElement = this.findFormElement(this.formElement, 'input', 'commit')
    commitElement.value = 'Update'
    
    var commentElement = this.getCommentElement(linkElement)
    this.resource.uri = this.getEditUri(commentElement)

    this.command = MapBuzz.Command.Build(new MapBuzz.Command.CommentMessage('Saving comment', MapBuzz.Message.PROCESSING),
                                         this.resource.putCommand(),
                                         new MapBuzz.Command.ReplaceElement(commentElement),
                                         new MapBuzz.Command.Databus(MapBuzz.Message.CLEAR_MESSAGE, {level: MapBuzz.Message.PROCESSING}))
    
    
    // Hide comment body so we can show the form
    this.hideCommentBody(commentElement)

   // Setup the editor form and display it    
    this.populateForm(this.getCommentForm(commentElement), false)
    var commentMainElement = this.getCommentBody(commentElement)
    commentMainElement.appendChild(this.formElement)
    this.showForm()
    
    // Setup status
    this.setupStatus(commentElement)
  },
  
  clickDeleteComment: function(linkElement)
  {
    var commentElement = this.getCommentElement(linkElement)
    
    this.resource.uri = this.getEditUri(commentElement)
    
    // Setup command for DELETEing a comment
    this.command = MapBuzz.Command.Build(new MapBuzz.Command.CommentMessage('Deleting comment', MapBuzz.Message.PROCESSING),
                                         this.resource.deleteCommand(),
                                         new MapBuzz.Command.ReplaceElement(commentElement),
                                         new MapBuzz.Command.Databus(MapBuzz.Message.CLEAR_MESSAGE, {level: MapBuzz.Message.PROCESSING}))
    
    // Setup status
    this.setupStatus(commentElement)
    this.command.execute()
  },
  
  clickRestoreComment: function(linkElement)
  {
    var commentElement = this.getCommentElement(linkElement)

    this.resource.uri = this.getEditUri(commentElement)
    
    // Setup command for DELETEing a comment
    var requestOptions = {}
    requestOptions.parameters = {_method: 'restore'}
                          
    this.command = MapBuzz.Command.Build(new MapBuzz.Command.CommentMessage('Restoring comment', MapBuzz.Message.PROCESSING),
                                         this.resource.postCommand(requestOptions),
                                         new MapBuzz.Command.ReplaceElement(commentElement),
                                         new MapBuzz.Command.Databus(MapBuzz.Message.CLEAR_MESSAGE, {level: MapBuzz.Message.PROCESSING}))
    
    // Setup status
    this.setupStatus(commentElement)
    this.command.execute()
  }
})


// =====  TabComments  =====
namespace('MapBuzz.Component')
MapBuzz.Component.TabComments = function ComponentTabComments(tab)
{
  this.tab = tab
  this.initialize()
}

Object.extend(MapBuzz.Component.TabComments.prototype,
{
  initialize: function()
  {
    this.registerListeners()
  },
  
  release: function()
  {
    this.unregisterListeners()
  },
  
  registerListeners: function()
  {
    // Listen for user panning
    this.clickListener = MapBuzz.databus.registerListener(MapBuzz.UI.REPLIES_LINK_CLICK, function(message)
    {
      var uri = message.data.uri
      this.loadComments(uri)
    }.bind(this))      
  },
   
  unregisterListeners: function()
  {
    MapBuzz.databus.unregisterListener(MapBuzz.UI.REPLIES_LINK_CLICK, this.clickListener)
  },
  
  loadComments: function(uri)
  {
    this.tab.resource = new MapBuzz.Resource.Remote(uri)
    this.tab.activate()
  }    
})


if (document.isFacebook)
{
  Object.extend(MapBuzz.Comments.prototype,
  {
    setupEvents: function()
    {
      this.submitHandler = this.onSubmit.bind(this)
      Event.observe(this.formElement, 'submit', this.submitHandler, false)
      
      this.clickHandler = this.onClick.bind(this)
      Event.observe(this.discussionElement, 'click', this.clickHandler, false)

      this.cancelHandler = this.onCancel.bind(this)
      Event.observe(this.cancelButton, 'click', this.cancelHandler, false)
    }
  })
}