#
# Provides a controller for managing Blogs.
#
class BlogController < ApplicationController
  
  require "syndication/rss"
  require "syndication/content"
  require "open-uri"
  require "digest/sha1"
  
  #
  # Gathers data for the "manage" view.
  #
  def manage
    
    if !@blogger_logged_in
      
      flash[ :error ] = "You need to be logged in to manage your blogs."
      redirect_to :controller => "blogger", :action => "login"
    end
  end
  
  #
  # Presents the form for adding a new Blog.
  #
  def add
    
    @form_action = :handle_add
    @submit_text = "Add Blog"
  end
  
  #
  # Adds a new Blog to the system.
  #
  def handle_add
    
    blog_info = params[ :blog ]
    
    # Make sure we have a feed url
    if blog_info[ :rss ].blank?
      
      flash[ :error ] = "I need the RSS feed to get the details of the blog"
      redirect_to :action => "add"

      return
    end
    
    # Parse the feed
    begin
      
      rss = fetch_feed( blog_info[ :rss ] )
    rescue
      
      flash[ :error ] = "I couldn't add that blog, the feed was not in a format I could understand"
      redirect_to :action => "manage"
      
      return
    end
    
    # Bail out now if we don't have a feed
    if !rss
      
      flash[ :error ] = "I retrieved the feed, but it appears to be missing information I need"
      redirect_to :action => "manage"
      
      return
    end
    
    # Save this feed
    blog = save_blog( rss, blog_info[ :rss ] )
    
    # Bail if the feed didn't get saved
    if !blog.save
      
      flash[ :error ] = "There was a problem adding your feed to the planet"
      redirect_to :action => "manage"
      
      return
    end
    
    # Loop through the stories for the feed
    rss.items.each { |item|
      
      # Create a new story
      story = save_story( blog, item )
      
      if !story.save
        
        logger.warn( "Could not save story \"#{story.title}\"" )
      end
    }

    flash[ :notice ] = "The blog \"#{blog.title}\" has been added to the Planet"
    redirect_to :action => "manage"
  end
  
  #
  # Removed the Blog from the system.
  #
  def delete
    
    begin
      
      blog = Blog.find( params[ :id ] )
    rescue ActiveRecord::RecordNotFound
      
      logger.error "Could not delete Blog with id #{params[ :id ]}"
      flash[ :error ] = "That blog could not be deleted because it does not exist"
      go_back
    else
       
      if blog
        blog.destroy
        flash[ :notice ] = "Blog \"#{blog.title}\" has been deleted"
      end

      redirect_to :back
    end
  end
  
  #
  # Updates a Blog and any associated stories.
  #
  def check_blog
    
    begin
      
      blog = Blog.find( params[ :id ] )
    rescue ActiveRecord::RecordNotFound
      
      logger.error "Could not update Blog with id #{params[ :id ]}"
      flash[ :error ] = "That blog could not be updated because it does not exist"
      go_back
    else
       
      if blog
        
        check_and_update_blog( blog )
        flash[ :notice ] = "The blog \"#{blog.title}\" was successfully updated"
      end

      redirect_to :back
    end
  end
  
  #
  # Updates all Blogs in the database.
  #
  def check_all_blogs
    
    blogs = Blog.find( :all )
    
    blogs.each { |blog|
      
      check_and_update_blog( blog )
    }
  end
  
  #
  # Prepares data for the "show" view.
  #
  def show
    
    if !params[ :id ] 
      
      flash[ :error ] = "You need to pick a blog to show."
      redirect_to :controller => "main"
      return
    end
    
    @blog = Blog.find( params[ :id ] )
    page = params[ :page ]
    
    @story_pages, @stories = paginate_collection( @blog.stories, :order => "pub_date DESC", :page => params[ :page ] )
  end
  
  private 
  
  #
  # Checks the Blog for changes and updates data as necessary.
  #
  def check_and_update_blog( blog ) 
    
    # Parse the feed
    begin
      
      rss = fetch_feed( blog.rss )
    rescue
      
      flash[ :error ] = "I couldn't check that blog, the feed was not in a format I could understand"
      redirect_to :action => "manage"
      
      return
    end
    
    rss_content_hash = Digest::SHA1.hexdigest( rss.to_s )
    if rss_content_hash == blog.content_hash
      
      # The Blog hasn't changed
      return
    end
    
    # Update this feed
    update_blog( blog, rss )
    
    # Bail if the feed didn't get saved
    if !blog.save
      
      flash[ :error ] = "There was a problem updating your feed on the planet"
      redirect_to :action => "manage"
      
      return
    end
    
     # Loop through the stories for the feed
      rss.items.each { |item|
        
        # Check to see if we have the story
        story = Story.find( :first, :conditions => { :link => item.link } )
        if story
          
          # Update the story
          update_story( story, item )
        else
          
          # Create a new story
          story = save_story( blog, item )
        end

        if !story.save

          logger.warn( "Could not save story \"#{story.title}\"" )
        end
      }
  end
  
  #
  # Updates the Story with the RSS item's data.
  #
  def update_story( story, item )
    
    story.title = item.title
    story.link = item.link
   
    if item.content_decoded && !item.content_decoded.blank?
      story.description = item.content_decoded
    else
      story.description = item.description
    end
    
    content_hash = ""
    if item.title; content_hash += item.title end
    if item.pubdate; content_hash += item.pubdate.to_s end
    if story.description; content_hash += story.description end
    
    story.last_read = Time.now
    
    # if item.pubdate
    #         story.pub_date = item.pubdate
    #       else
    #         story.pub_date = Time.now
    #       end
    
    # Clear out old categories
    story.categories.clear
    
    if item.category == nil || item.category.size < 1
      
      # Get the Uncategorized category
      categoryThis = Category.find( :first, :conditions => { :name => "Uncategorized" } )
      
      story.categories << categoryThis
    else
      
      # Check to see if we need to add categories
      item.category.each { |category|
      
        # Check for the category
        categoryThis = Category.find( :first, :conditions => { :name => category } )
      
        if categoryThis
        
          # Use the existing category
          story.categories << categoryThis
        else
        
          # Create a new category
          categoryNew = Category.new
          categoryNew.name = category
        
          if !categoryNew.save
          
            logger.warn( "Could not save category \"#{category.name}\"" )
          end
        
          # Use the new category
          story.categories << categoryNew
        end
      }
    end
    
    return story
  end
  
  #
  # Saves a Story to the database.
  #
  def save_story( blog, item )
    
    # Create a new story
    story = Story.new
    story.blog = blog
    story.title = item.title
    story.link = item.link
    
    if item.content_decoded && !item.content_decoded.blank?
      story.description = item.content_decoded
    else
      story.description = item.description
    end
    
    content_hash = ""
    if item.title; content_hash += item.title end
    if item.pubdate; content_hash += item.pubdate.to_s end
    if story.description; content_hash += story.description end
    
    story.content_hash = Digest::SHA1.hexdigest( content_hash )
    story.last_read = Time.now
    
    if item.pubdate
      story.pub_date = item.pubdate
    else
      story.pub_date = Time.now
    end
    
    if item.category == nil || item.category.size < 1
      
      # Get the Uncategorized category
      categoryThis = Category.find( :first, :conditions => { :name => "Uncategorized" } )
      
      story.categories << categoryThis
    else
      
      # Check to see if we need to add categories
      item.category.each { |category|
      
        # Check for the category
        categoryThis = Category.find( :first, :conditions => { :name => category } )
      
        if categoryThis
        
          # Use the existing category
          story.categories << categoryThis
        else
        
          # Create a new category
          categoryNew = Category.new
          categoryNew.name = category
        
          if !categoryNew.save
          
            logger.warn( "Could not save category \"#{category.name}\"" )
          end
        
          # Use the new category
          story.categories << categoryNew
        end
      }
    end
    
    return story
  end
  
  #
  # Saves a new Blog to the database.
  #
  def save_blog( rss, rss_url )
    
    # Save this feed
    blog = Blog.new
    blog.last_checked = Time.now
    blog.blogger = @blogger_logged_in
    blog.link = rss.channel.link
    blog.rss = rss_url
    blog.title = rss.channel.title
    blog.description = rss.channel.description.to_s
    blog.submitted = Time.now
    blog.approved = Time.now
    
    blog_hash_data = ""
    if rss.channel.title; blog_hash_data += rss.channel.title end
    if rss.channel.description; blog_hash_data += rss.channel.description.to_s end
    
    rss.items.each { |item| 
      
      content = nil;
      
      if !item.content_decoded.blank?
        content = item.content_decoded
      else
        content = item.description
      end
      
      if item.title; blog_hash_data += item.title end
      if item.pubdate; blog_hash_data += item.pubdate.to_s end
    }
    
    blog.content_hash = Digest::SHA1.hexdigest( blog_hash_data )
    
    return blog
  end
  
  #
  # Updates a Blog with new RSS data.
  #
  def update_blog( blog, rss )
  
    # Update this feed
    blog.last_checked = Time.now
    blog.link = rss.channel.link
    blog.title = rss.channel.title
    blog.description = rss.channel.description.to_s
    
    blog_hash_data = ""
    if rss.channel.title; blog_hash_data += rss.channel.title end
    if rss.channel.description; blog_hash_data += rss.channel.description.to_s end
    
    rss.items.each { |item| 
      
      content = nil;
      
      if item.content_decoded && !item.content_decoded.blank?
        content = item.content_decoded
      else
        content = item.description
      end
      
      if item.title; blog_hash_data += item.title end
      if item.pubdate; blog_hash_data += item.pubdate.to_s end
    }
    
    blog.content_hash = Digest::SHA1.hexdigest( blog_hash_data )
    
    return blog
  end
  
  #
  # Fetches the feed at the "url" and returns a parsed RSS object.
  #
  def fetch_feed( url )
    
    xml = nil
    
    open( url ) { |http| 
      
      xml = http.read
    }
    
    parser = Syndication::RSS::Parser.new
    
    rss = parser.parse( xml )
    
    return rss
  end
end
