/* Copyright (c) 2007 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** 
 * Authorizing to this URL enables us full access to user's blog feeds.
 * @type {String}
 */
var BLOGGER_AUTH_URL = 'http://www.blogger.com/feeds';

/**
 * This is the URL to query to get a list of the user's blogs
 * @type {String}
 */ 
var BLOGGER_LIST_URL = 'http://www.blogger.com/feeds/default/blogs';

/**
 * This object holds all the global variables used throughout the program.
 * @type {Object}
 */
var BlogPress = {
  /**
   * This becomes the google.gdata.blogger.BloggerService object that
   * we later perform insert/update/delete/get operations on.
   * @type {Object}
   */
  service: null,
  /**
   * This holds google.gdata.blogger.BlogFeed objects, the users' blogs.
   * @type {Array}.<Object>
   */
  blogs: [],
  /**
   * This holds google.gdata.blogger.BlogEntry objects, the blog feed entries.
   * @type {Array}.<Object>
   */
  blogFeeds: [],
  /**
   * This keeps track of the currently selected blog's index in blogs array.
   * @type {Number}
   */
  currentBlogNum: 0
};

/**
 * This is used for setting the view mode in the app, by switchView.
 * @enum {Number}
 */
var BP_VIEWMODE = {
  SHOWPOSTS: 0,
  CREATEPOST: 1,
  EDITPOST: 2
};

/**
 * These are the IDs for the various DOM elements used by the JS.
 * @enum {String}
 */
var BP_ID = {
  SHOWPOST_TAB: 'sublink0',
  SHOWPOST_LINK: 'sublink0a',
  SHOWPOST_BLOCK: 'showAllPostsDiv',
  CREATEPOST_TAB: 'sublink1',
  CREATEPOST_LINK: 'sublink1a',
  CREATEPOST_BLOCK: 'createOrEditDiv',
  DRAFT_BUTTON: 'draftButton',
  PUBLISH_BUTTON: 'publishButton',
  AUTH_BUTTON: 'authButton',
  FORM_EDITURI: 'blogPostEditUri',
  FORM_TITLE: 'blogPostTitleInput',
  FORM_CONTENT: 'blogPostTextArea',
  FORM_TAGS: 'blogPostCategoriesInput',
  AUTH_BLOCK: 'loginNotice',
  BLOG_DROPDOWN: 'blogSelect'
};

/**
 * These are the hexadecimal color strings for the on/off tab states.
 * @enum {String}
 */
var BP_COLOR = {
  TAB_ON: '#0d324f',
  TAB_OFF: '#83b4d8',
  LINK_ON: '#fff',
  LINK_OFF: '#000'
};
 

/**
 * Short alias for document.getElementById.
 * @param {String} id The ID of the element.
 * @return {Element} The DOM element for that ID.
 */
function el(id) {
  return document.getElementById(id);
};

/**
 * Logging function. To make it easy to change logging method.
 * @param {String} status Message to display
 */
function changeStatus(status) {
  el("statusDiv").innerHTML = status;
};

/**
 * This function checks if the user is logged in, 
 * and changes the login button and displayed sections accordingly.
 * It also initializes the BlogPress.service variable
 * and calls a function to fill in the form based on the URL.
 */
function init() {
  BlogPress.service = new google.gdata.blogger.BloggerService(
      'GoogleInc-bloggerOffline-1');

  if (google.accounts.user.checkLogin(BLOGGER_AUTH_URL)) {
    el(BP_ID.AUTH_BUTTON).value = 'Logout';
    getBlogs();
  } else {
    el(BP_ID.AUTH_BUTTON).value = 'Login';
    el(BP_ID.AUTH_BLOCK).style.display = 'block';
  }

  switchView(BP_VIEWMODE.SHOWPOSTS);
};

/**
 * This function is triggered by the login/logout button.
 * If the user is logged in to the app, it logs them out.
 * If the user is logged out to the app, it logs them in.
 */
function loginOrLogout(){
  var token = google.accounts.user.checkLogin(BLOGGER_AUTH_URL);
  if (token) {
    google.accounts.user.logout();
    init();
  } else {
    google.accounts.user.login(BLOGGER_AUTH_URL);
  }
};

/**
 * Retrieves the feed of all the user's blogs. 
 */
function getBlogs() {
  var query = new google.gdata.blogger.BlogQuery(BLOGGER_LIST_URL);
  BlogPress.service.getBlogFeed(query, handleBlogsFeed, handleError);
};

/**
 * Updates the current blog number when drop-down is changed.
 */
function updateCurrentBlogNum() {
  BlogPress.currentBlogNum = el(BP_ID.BLOG_DROPDOWN).value;
  displayBlogPosts(BlogPress.currentBlogNum);
};

/**
 * This function is called after the blogs feed query returns.
 * It parses through the entries, getting the necessary info and
 * filling the blogs dropdown.
 * @param {google.gdata.BlogFeed} resultsFeedRoot The blogs feed
 */
function handleBlogsFeed(resultsFeedRoot) {
  var blogsFeed = resultsFeedRoot.feed;
  BlogPress.blogs = blogsFeed.getEntries();

  for (var i = 0; i < BlogPress.blogs.length; i++) {
    var blog = BlogPress.blogs[i];
    var blogTitle = blog.getTitle().getText();
    var blogSelect = el(BP_ID.BLOG_DROPDOWN);
    var blogOption = document.createElement('option');
    blogOption.setAttribute('value', i);
    blogOption.appendChild(document.createTextNode(blogTitle));
    blogSelect.appendChild(blogOption);
    loadBlog(i);
  }
};

/**
 * Loads the blog specified by num index. 
 * If it's the first blog, displays the posts.
 * @param {Number} num The index of the blog in blogs array.
 */
function loadBlog(num) {
  var blog = BlogPress.blogs[num];
  if (!blog) {
    return;
  }
  var blogPostHref = blog.getEntryPostLink().getHref();
  BlogPress.service.getBlogPostFeed(blogPostHref, function (resultsFeedRoot) {
    var postsFeed = resultsFeedRoot.feed;
    BlogPress.blogFeeds[num] = postsFeed;
    if (num == 0) { displayBlogPosts(num); }
  }, handleError);
};

/**
 * Displays the blog posts specified by num index.
 * @param {Number} blogNum The index of the blog whose posts to display.
 */
function displayBlogPosts(blogNum) {
  var blogPostsFeed = BlogPress.blogFeeds[blogNum];

  // If there are no posts in this blog feed, display message.
  if (blogPostsFeed.entry.length == 0) {
    el('showAllPostsDiv').innerHTML = 
        'No posts. Click "create post" to get started now!';
    return;
  }
 
  var table = document.createElement('table');
  var tbody = document.createElement('tbody');
   
  // Create the top row for the table.
  var th = document.createElement('tr');
  addRowCell(th, 'top', document.createTextNode('When'));
  addRowCell(th, 'top', document.createTextNode('Title'));
  addRowCell(th, 'top', document.createTextNode('Author'));
  addRowCell(th, 'top', document.createTextNode(''));
  addRowCell(th, 'top', document.createTextNode(''));
  addRowCell(th, 'top', document.createTextNode(''));
  tbody.appendChild(th);

  // Iterate through the blog posts, displaying info for each.
  for (var i = 0; i < blogPostsFeed.entry.length; i++) {
    // Get the blog post information from the feed.
    var blogPost = blogPostsFeed.getEntries()[i];
    var title = blogPost.getTitle().getText();
    var author = blogPost.getAuthors()[0].getName().getValue();
    var date = blogPost.getUpdated().getValue().getDate();
    var when = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + 
        date.getDay() + " " + date.getHours() + ":" + date.getMinutes();
    
    // We know it's a draft post if it doesn't have an html link.
    var isDraft = true; 
    if (blogPost.getHtmlLink()) {
      isDraft = false;
      var htmlLinkHref = blogPost.getHtmlLink().getHref();
    }
    
    // Create the row with all the post information.    
    var color = (i%2 == 0) ? 'odd' : 'even';

    var tr = document.createElement('tr');

    addRowCell(tr, color, document.createTextNode(when));
    addRowCell(tr, color, document.createTextNode(title));
    addRowCell(tr, color, document.createTextNode(author));

    // Create link to view post, if possible.
    if (!isDraft) {
      var viewA = document.createElement('a');
      viewA.setAttribute('href', htmlLinkHref);
      viewA.appendChild(document.createTextNode('View'));      
      addRowCell(tr, color, viewA);
    } else {
      addRowCell(tr, color, document.createTextNode('(draft)'));
    }

    // Create edit-this-post link. 
    var viewA = document.createElement('a');
    viewA.setAttribute('href', 'javascript:prepareEditView(' + i + ',' + 
        isDraft + ')');
    viewA.appendChild(document.createTextNode('Edit'));      
    addRowCell(tr, color, viewA);
    
    // Create delete-this-post link.
    var viewA = document.createElement('a');
    viewA.setAttribute('href', 'javascript:deletePost(' + i + ')');
    viewA.appendChild(document.createTextNode('Delete'));      
    addRowCell(tr, color, viewA);

    tbody.appendChild(tr);
  }
  table.appendChild(tbody);
  el('showAllPostsDiv').innerHTML = '';
  el('showAllPostsDiv').appendChild(table);
};

/**
 * Adds a TD to the given row with the given content node.
 * @param {Element} tr The row to append the created TD to.
 * @param {String} classname The name of the CSS class for this cell.
 * @param {Element} content The DOM node to append to the created TD.
 */
function addRowCell(tr, classname, content) {
  var td = document.createElement('td');
  td.className = classname;
  td.appendChild(content);
  tr.appendChild(td);
};

/*
 * Switches to create/edit/show posts view and mode.
 * @param {Number} viewNum Indicates which view to switch to.
 */
function switchView(viewNum) {
  switch(viewNum) {
  case BP_VIEWMODE.SHOWPOSTS:
    el(BP_ID.SHOWPOST_TAB).style.backgroundColor = BP_COLOR.TAB_ON;
    el(BP_ID.SHOWPOST_LINK).style.color = BP_COLOR.LINK_ON;
    el(BP_ID.CREATEPOST_TAB).style.backgroundColor = BP_COLOR.TAB_OFF;
    el(BP_ID.CREATEPOST_LINK).style.color = BP_COLOR.LINK_OFF;
    el(BP_ID.CREATEPOST_BLOCK).style.display = 'none';
    el(BP_ID.SHOWPOST_BLOCK).style.display = 'block';
    loadBlog(BlogPress.currentBlogNum);
    break;
  case BP_VIEWMODE.CREATEPOST: 
    el(BP_ID.SHOWPOST_TAB).style.backgroundColor = BP_COLOR.TAB_OFF;
    el(BP_ID.SHOWPOST_LINK).style.color = BP_COLOR.LINK_OFF;
    el(BP_ID.CREATEPOST_TAB).style.backgroundColor = BP_COLOR.TAB_ON;
    el(BP_ID.CREATEPOST_LINK).style.color = BP_COLOR.LINK_ON;
    el(BP_ID.SHOWPOST_BLOCK).style.display = 'none';
    el(BP_ID.CREATEPOST_BLOCK).style.display = 'block';
    el(BP_ID.FORM_EDITURI).value = '';
    el(BP_ID.FORM_TITLE).value = '';
    el(BP_ID.FORM_CONTENT).value = '';
    el(BP_ID.FORM_TAGS).value = '';
    el(BP_ID.DRAFT_BUTTON).style.visibility = 'visible';
    el(BP_ID.PUBLISH_BUTTON).value = 'Publish';
    break;
  case BP_VIEWMODE.EDITPOST:
    el(BP_ID.SHOWPOST_TAB).style.backgroundColor = BP_COLOR.TAB_ON;
    el(BP_ID.SHOWPOST_LINK).style.color = BP_COLOR.LINK_ON;
    el(BP_ID.CREATEPOST_TAB).style.backgroundColor = BP_COLOR.TAB_OFF;
    el(BP_ID.CREATEPOST_LINK).style.color = BP_COLOR.LINK_OFF;
    el(BP_ID.SHOWPOST_BLOCK).style.display = 'none';
    el(BP_ID.CREATEPOST_BLOCK).style.display = 'block';
    break;
  }
};

/**
 * Reads the contents of the blog post form and sends it to the Blogger
 * servers as either a new blog post or an update of an old one.
 * @param {Boolean} isDraft Indicates whether this post is a draft.
 */
function insertOrUpdatePostEntry(isDraft) {
  // Get the values from the form.
  var title = el(BP_ID.FORM_TITLE).value;
  var content= el(BP_ID.FORM_CONTENT).value;
  var categoriesString = el(BP_ID.FORM_TAGS).value;
  var categoriesArray = categoriesString.split(',');

  // Create event object and set relevant properties.
  var blogPostEntry = new google.gdata.blogger.BlogPostEntry();
  blogPostEntry.setTitle(google.gdata.Text.create(title));
  blogPostEntry.setContent(google.gdata.Text.create(content, 'html'));

  for (var c = 0; c < categoriesArray.length; c++) {
    if (categoriesArray[c] != '') {
      var category = new google.gdata.Category();
      category.setTerm(categoriesArray[c]);
      category.setScheme('http://www.blogger.com/atom/ns#');
      blogPostEntry.addCategory(category);
    }
  }
 
  if (isDraft) {
    var draft = new google.gdata.Draft();
    draft.setValue(google.gdata.Draft.VALUE_YES);
    var control = new google.gdata.Control();
    control.setDraft(draft);
    blogPostEntry.setControl(control);
  }

  var entryEditUri = el(BP_ID.FORM_EDITURI).value;
  if (entryEditUri != '') { // Editing an existent post.
    BlogPress.service.updateEntry(entryEditUri, blogPostEntry, 
        handleEntryPost, handleError);
  } else { // Adding a new post.
    var currentBlog = BlogPress.blogs[BlogPress.currentBlogNum];
    var blogPostHref = currentBlog.getEntryPostLink().getHref();
    BlogPress.service.insertEntry(blogPostHref, blogPostEntry, 
        handleEntryPost, handleError);
  }
};

/**
 * Deletes the n'th post in the current blog feed.
 * @param {Number} postNum The index of the post to delete.
 */
function deletePost(postNum) {
  var currentBlog = BlogPress.blogFeeds[BlogPress.currentBlogNum];
  var blogPostEntry = currentBlog.getEntries()[postNum];
  var entryEditHref = blogPostEntry.getEditLink().getHref();
  BlogPress.service.deleteEntry(entryEditHref, handleDeletePost, handleError);
};

/**
 * Prepare to go into edit view by pre-filling the form
 * with the post details.
 * @param {Number} postNum The index of the post to edit.
 * @param {Boolean} isDraft Indicates whether post to edit is a draft.
 */
function prepareEditView(postNum, isDraft) {
  var currentBlog = BlogPress.blogFeeds[BlogPress.currentBlogNum];
  var blogPostEntry = currentBlog.getEntries()[postNum];

  var editUri = blogPostEntry.getEditLink().getHref();
  var title = blogPostEntry.getTitle().getText();
  var content = blogPostEntry.getContent().getText();
  var categoriesA = [];
  for (var c = 0; c < blogPostEntry.getCategories().length; c++) {
    categoriesA.push(blogPostEntry.getCategories()[c].getTerm());
  }
  var categories = categoriesA.join(', ');

  el(BP_ID.FORM_EDITURI).value = editUri;
  el(BP_ID.FORM_TITLE).value = title;
  el(BP_ID.FORM_CONTENT).value = content;
  el(BP_ID.FORM_TAGS).value = categories;

  if (isDraft) {
    el(BP_ID.DRAFT_BUTTON).style.visibility = 'visible';
    el(BP_ID.PUBLISH_BUTTON).value = 'Publish';
  } else {
    el(BP_ID.DRAFT_BUTTON).style.visibility = 'hidden';
    el(BP_ID.PUBLISH_BUTTON).value = 'Re-publish';
  }

  switchView(BP_VIEWMODE.EDITPOST);
};

/**
 * This function is called when post is deleted.
 * It calls getBlogs to update the current blog feed list.
 * @param {google.gdata.blogger.BlogEntry} entryRoot
 */
function handleDeletePost() {
  changeStatus('Successfully deleted post.');
  getBlogs();
};


/**
 * This function is called when a post is created or edited.
 * It calls getBlogs to update the current blog feed list.
 * @param {Object} entryRoot
 */
function handleEntryPost(entryRoot) {
  changeStatus('Successfully added or updated post.');

  var blogPostEntry = entryRoot.entry;
  el(BP_ID.FORM_EDITURI).value = blogPostEntry.getEditLink().getHref();

  if (blogPostEntry.getHtmlLink()) { // not a draft
    el(BP_ID.DRAFT_BUTTON).style.visibility = 'hidden';
    el(BP_ID.PUBLISH_BUTTON).value = 'Re-publish';
  }

  switchView(BP_VIEWMODE.SHOWPOSTS); 
};

/**
 * This function is called if an error is encountered
 * while retrieving a feed, adding, editing or deleting an entry.
 * @param {Object} error
 */
function handleError(error) {
  changeStatus(error.cause ? error.cause.statusText : error.message);
};


