require 'date'
require 'fileutils.rb'
# Useful methods that don't need to scan any files in order to work
class GTDContexts
  class << self
    def contexts
      @@contexts ||= self.get_env_contexts
      @@contexts.uniq!
      @@contexts.sort!
      @@contexts
    end
    def contexts=(newContexts)
      @@contexts = newContexts
    end
    def get_env_contexts
      contexts = ("#{ENV['TM_GTD_CONTEXT']} #{ENV['TM_GTD_CONTEXTS']}").chomp.split(" ").compact.sort
      contexts = ["email", "office", "online", "home", "call", "waiting"] if contexts.empty?
      return contexts.uniq.sort
    end
  end
end
# COMMENT TO BE REMOVED ONCE THINGS ARE CHECKED TO WORK
# module GTDLight
#   def GTDLight.get_env_contexts
#     contexts = ((ENV['TM_GTD_CONTEXT'] || "") + " " + (ENV['TM_GTD_CONTEXTS'] || "")).chomp.split(" ").compact.sort
#     contexts = ["email", "office", "online", "home", "call", "waiting"] if contexts.empty?
#     return contexts.uniq.sort
#   end
# end
class Array
  def next(item)
    i = (self.index(item) || -1) + 1
    return self[i % length]
  end
  def previous(item)
    i = (self.index(item) || length) -1
    return self[i % length]
  end
end
# The test <tt>date === DateLate</tt> returns true if +date+ is earlier than Date.today
class DateLate
  def self.===(date)
    date = Date.parse(date) unless Date === date
    return date < Date.today
  end
end
# A simple mixin that creates +uri+'s, +txmt+ uri schemes and +link+'s out of any object
# that responds to +file+, +line+ and +name+.
module Linkable
  def uri
    "file://#{e_url self.file.to_s}"
  end
  def txmt
    "txmt://open?url=#{self.uri}&line=#{self.line}"
  end
  def link(attributes = {})
    s = "<a href=\"#{self.txmt}\""
    attributes.each do |key,value|
      s << " #{key.to_s}=\"#{value.to_s}\""
    end
    s << ">#{self.name}</a>"
  end
  def mark_completed_link(attributes={})
    s = "<input type=\"checkbox\" href=\"\#\""
    attributes.each do |key,value|
      s << " #{key.to_s}=\"#{value.to_s}\""
    end
    pathToScript = File.join(ENV['TM_BUNDLE_SUPPORT'],"bin","mark_completed.rb")
    string_to_execute = (e_js_sh pathToScript) + " #{e_js_sh self.name} #{e_js_sh self.file.to_s} #{e_js_sh self.line}"
    s << " onClick='TextMate.system(\"2>/dev/console #{string_to_execute}\", null); return false;'"
    s << ">Mark!</a>"
  end
end

# Very light logger class.
class MyLogger
  # Adds a string to the logs.
  def self.log(string)
    @@logs << string
  end
  # Dumps a string with all the logs.
  def self.dump
    @@logs.sort.join("\n")
  end
  # Clears the logs.
  def self.clear
    @@logs = []
  end
  self.clear
end
# A very-poor-man's Builder. Produces tables. Example of use:
#
#    pr = Printer.new
#    pr.table do
#      pr.title("Actions for context: #{context}")
#      pr.headers(["Action name","Project","Due_by"])
#      actions.each do |a|
#        proj = a.parent.link
#        due = case a.due
#          when "",nil
#            ""
#          when DateLate
#            "<span style=\"color:red\">#{a.due}</span>"
#          else
#            a.due
#        end
#        pr.row([a.link,proj,due])
#      end
#    return pr.to_html
class Printer
  # An array of items of the form +[:type,items]+, +items+ an array of strings.
  attr_accessor :items
  def initialize
    @items = []
  end
  # Adds caption to the table.
  def title(title)
    @items << [:title,title]
  end
  # Creates a table, with contents generated by yielding the block.
  def table
    @items << [:table_begin,nil]
    yield
    @items << [:table_end,nil]
  end
  # Adds headers to the table.
  def headers(*headers)
    @items << [:headers,*headers]
  end
  # Adds a row of items.
  def row(*row_items)
    @items << [:row,*row_items]
  end
  def row_next(*row_items)
    @items << [:row_next,*row_items]
  end
  # Adds raw HTML code.
  def raw(code)
    @items << [:raw, code]
  end
  # Generate an HTML string out of the data that has been passed.
  def to_html
    s = []
    @items.each do |type,item|
      case type
        when :title
          s << "<caption>#{item}</caption>"
        when :table_begin
          s << '<table class="graybox" cellspacing="0" cellpadding="5">'
        when :table_end
          s << "</table>"
        when :row
          s << "<tr class='not-next'>"
          s << item.map{|i| "<td>#{i}</td>"}.join("\n")
          s << "</tr>"
        when :row_next
          s << "<tr class='next'>"
          s << item.map{|i| "<td>#{i}</td>"}.join("\n")
          s << "</tr>"
        when :headers
          s << "<tr>"
          s << item.map{|i| "<th>#{i}</th>"}.join("\n")
          s << "</tr>"
        when :raw
          s << item
      end
    end
    return s.join("\n")+"\n"
  end
end

module GTD
  # Safe write of the pairs in +filepairs+, of the form <tt>[filename,format]</tt>.
  def GTD.safe_write_with_backup(filepairs)
    # First read each filepair and compare with string to replace it.
    # If the saem, ignore.
    changed_filepairs = []
    for filename,string in filepairs do
      File.open(filename,'r') do |f|
        changed_filepairs << [filename,string] unless f.read.to_s == string
      end
    end
    begin
      for filename,string in changed_filepairs do
        newFile = filename + "~~"
        raise "Could not create different name" if newFile == filename
        File.open(newFile, 'w') do |f|
          f.puts string
        end
      end
    rescue Exception => e
      $stderr.puts "There was a problem saving the files: #{e}.\nExiting... Some extra files may have been created."
      raise e
    end
    begin
      for filename,string in changed_filepairs do
        FileUtils.mv(filename,filename+"~")
      end
      for filename,string in changed_filepairs do
        FileUtils.mv(filename+"~~",filename)
      end
    rescue Exception => e
      $stderr.puts "There was a problem moving the files: #{e}.\nExiting... Files with extension \".gtd~~\" contain the newest data that could not be moved."
      raise e
    end
  end
end
class NilClass
  def link
    return ""
  end
end
# URL escape a string but preserve slashes (idea being we have a file system path that we want to use with file://)
# Borrowed from TextMate's library.
def e_url(str)
  str.gsub(/([^a-zA-Z0-9\/_.-]+)/n) do
    '%' + $1.unpack('H2' * $1.size).join('%').upcase
  end
end
# Also borrowed from TextMate's library.
# escape text to make it useable in a shell script as one “word” (string)
# Do not escape slashes.
def e_sh(str)
  str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF])/, '\\')
end
# first escape for use in the shell, then escape for use in a JS string
def e_js_sh(str)
  (e_sh str).gsub("\\", "\\\\\\\\")
end
