#!/usr/bin/env ruby

MY_TASKO_ID = 1        # Your Tasko ID
MY_TASKO_KEY = 'AAAA'  # Your Tasko key
MY_TASKS_DIR = ENV['HOME'] + '/Tasks'
MY_TASKO_SUMMARY_FILE = ENV['HOME'] + '/.tasko.yml'

require 'yaml'
require 'digest/md5'
require 'xmlrpc/client'

class String
  def md5
    Digest::MD5::hexdigest(self)
  end
end

module XMLRPC::ParseContentType
  def parse_content_type(str)
    ["text/xml", "utf-8"]
  end
end

class TaskoAPI
  XMLRPC::Config.module_eval { remove_const(:ENABLE_NIL_PARSER) }
  XMLRPC::Config.const_set(:ENABLE_NIL_PARSER, true)

  URL = "http://taskodone.com/api"

  def initialize(id, pass)
    @id, @pass = id, pass
  end

  def newpaper(*args)
    client.call('new', @id, @pass, *args)
  end

  def method_missing(method, *args)
    client.call(method, @id, @pass, *args)
  end

  private  
  def client
    XMLRPC::Client.new2(URL)
  end
end

class TaskoSyncer
  EXT = '.taskpaper'

  def initialize(id, pass, dir, summary_file)
    @tasko = TaskoAPI.new(id, pass)
    @dir = dir
    @summary_file = summary_file
    @summary = YAML::load(File.read(@summary_file)) rescue {}
  end

  def sync
    begin
      papers = @tasko.papers

      local_files.each do |file|
        papers = update_file_contents(file, papers)
      end

      papers.each do |paper|
        update_missing_paper paper
      end

      save_summary
    rescue SocketError
      puts "No internet connection"
    end    
  end

  private

  def write_task_file(paper, data)
    File.open(File.join(@dir, paper + '.taskpaper'), 'w') do |f|
      f.write data
    end
    update_summary(paper, data)
  end

  def update_summary(paper, data)
    @summary[paper] = data.md5
  end

  def save_summary
    File.open(@summary_file, 'w') do |f|
      f.write @summary.to_yaml
    end
  end

  def local_files
    Dir[File.join(@dir, "*#{EXT}")]
  end

  def summary_match?(paper, data)
    !@summary[paper].nil? and @summary[paper] == data.md5
  end

  def update_file_contents(file, papers)
    paper = File.basename(file, EXT)
    data = File.read(file)

    if summary_match?(paper, data)
      remote_data = @tasko.paper(paper)
      if data != remote_data
        puts "Pulling #{paper}"
        write_task_file(paper, remote_data)
      end
      papers.delete(paper)          
    else
      puts "Pushing #{paper}"

      if papers.delete(paper)
        @tasko.edit(paper, data)
      else
        @tasko.newpaper(paper, data)
      end

      update_summary(paper, data)
    end

    papers
  end

  def update_missing_paper(paper)
    if @summary[paper].nil?
      puts "Adding #{paper}"
      data = @tasko.paper(paper)
      write_task_file(paper, data)
    else
      puts "Deleting #{paper}"
      @tasko.delete(paper)
      @summary.delete(paper)
    end
  end
end

if __FILE__ == $0
  TaskoSyncer.new(MY_TASKO_ID, MY_TASKO_KEY, MY_TASKS_DIR, MY_TASKO_SUMMARY_FILE).sync
end