=begin
Plugin: OliveTree Bible Reader Plugin
Description: Track annotations from OliveTree's Bible Reader
Author: [Micah Cooper](http://meebles.org)
Configuration:
  option_1_name: [ "example_value1" , "example_value2", ... ]
  option_2_name: example_value
Notes:
  - This plugin pulls annotations from OliveTree Bible Reader and adds to Slogger
  - This plugin is unofficial and does not use published APIs. As such, it may break at any time and without notice.
=end

require 'multimap'

config = { # description and a primary key (username, url, etc.) required
  'description' => ['Track annotations from OliveTree\'s Bible Reader',
                    'otUser is your OliveTree.com username (email address)',
                    'otPass is your OliveTree.com password'],
  'otUser' => '',
  'otPass' => '',
  #'annotationCategories' => [],
  'lastSequenceNumber' => 0,
  ##'additional_config_option' => false,
  'tags' => '#social #Bible' # A good idea to provide this with an appropriate default setting
}
# Update the class key to match the unique classname below
$slog.register_plugin({ 'class' => 'OTBibleLogger', 'config' => config })

# unique class name: leave '< Slogger' but change ServiceLogger (e.g. LastFMLogger)
class OTBibleLogger < Slogger
  def doSetup


  end

  def bigQuery

    bookName = ['', 'Genesis', 'Exodus', 'Leviticus', 'Numbers', 'Deuteronomy', 'Joshua', 'Judges', 'Ruth',
           '1 Samuel', '2 Samuel', '1 Kings', '2 Kings', '1 Chronicles', '2 Chronicles',
                       'Ezra', 'Nehemiah', 'Esther', 'Job', 'Psalm', 'Proverbs', 'Ecclesiastes', 'Song of Solomon',
           'Isaiah', 'Jeremiah', 'Lamentations', 'Ezekiel', 'Daniel', 'Hosea', 'Joel', 'Amos', 'Obadiah',
           'Jonah', 'Micah', 'Nahum', 'Habakkuk', 'Zephaniah', 'Haggai', 'Zechariah', 'Malachi', 'Matthew',
           'Mark', 'Luke', 'John', 'Acts', 'Romans', '1 Corinthians', '2 Corinthians', 'Galatians',
                       'Ephesians', 'Philippians', 'Colossians', '1 Thessalonians', '2 Thessalonians', '1 Timothy',
                       '2\+Timothy', 'Titus', 'Philemon', 'Hebrews', 'James', '1 Peter', '2 Peter', '1 John',
           '2 John', '3 John', 'Jude', 'Revelation']


    if @config.key?(self.class.name)
      config = @config[self.class.name]
      # check for a required key to determine whether setup has been completed or not
      if !config.key?('otUser') || config['otUser'] == []
        @log.warn("<Service> has not been configured or an option is invalid, please edit your slogger_config file.")
        return
      else
        # set any local variables as needed
        username = config['otUser']
        password = config['otPass']
        cfgSeqNum = config['lastSequenceNumber'] || 0
      end
    else
      exit
    end
    username = config['otUser']
    password = config['otPass']
    cfgSeqNum = config['lastSequenceNumber'] || 0
    #queryURL = 'https://sync.olivetree.com/syncables.xml?device_id=<MYID>&above=321&managed_data_set=managed_annotations&sync_client_protocol_version=4&reader_version=com.olivetree.BibleReaderMac_5.4.2.1_OS_10.9.3'
    queryURL = 'https://sync.olivetree.com/syncables.xml'
    uri = URI(queryURL)
    params = {:device_id=> "001", :above=> cfgSeqNum, :managed_data_set=> "managed_annotations", :sync_client_protocol_version=> "4", :reader_version=> "slogger"}
    uri.query = URI.encode_www_form( params )
    req = Net::HTTP::Get.new(uri)
    req.basic_auth username, password

    res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) {|http|
      http.request(req)
    }

    case res
      when Net::HTTPSuccess, Net::HTTPRedirection then
        xml_data = res.body
      else
        puts res.error!
    end

    

    doc = REXML::Document.new(xml_data);

    ##annoCats = REXML::XPath.match(doc, '///[@type="AnnotationCategory"]')
    xpathtext = ''
    annoCats = REXML::XPath.match(doc, '/syncables/syncable/data-type[text()="AnnotationCategory"]/..') # ending .. gets the parent elements with this text
    #puts annoCats
    annoCats.each do |annoCat|
      #puts 'new cat'
      #puts annoCat
      #puts 
      parentCategoryID = annoCat.elements['datum/parent-category-id'].text
      name = annoCat.elements['datum/name'].text
      id = annoCat.elements['datum/client-id'].text

      # We're sticking the categories right into our config file

      parentText = ''
      if 1 < parentCategoryID.to_i
        parentText = config[parentCategoryID] || ''
      end

      config[id] = parentText + ' :: ' + name

    end


    # Tags are 3NF to allow many-to-many it appears
    # UserTags is one entry, annotations are another, and then there's a 3rd
    # value joining the two.

    annoTags = REXML::XPath.match(doc, '/syncables/syncable/data-type[text()="UserTag"]/..')
    annoTags.each do |annoTag|
      name = annoTag.elements['datum/name'].text
      id = annoTag.elements['datum/client-id'].text

      # We're sticking the tags right into our config file for perpetual use
      config[id] = name
    end



    # Now we're going on a tag hunt
    # This was within each annotation find, but that was slow and silly
    tagAssocs = Hash.new([])
    doc.elements.each('/syncables/syncable/data-type[text()="AnnotationUserTag"]/..') do |e|
      annotationID = e.elements['datum/annotation-id'].text unless e.elements['datum/annotation-id'].nil?
      userTagID = e.elements['datum/user-tag-id'].text unless e.elements['datum/user-tag-id'].nil?
      tagAssocs[annotationID] << userTagID
    end

    annos = REXML::XPath.match(doc, '/syncables/syncable/data-type[text()="Annotation"]/..')
    annos.each do |anno|
      if !anno.elements['datum/content'].nil?
        annoContent = anno.elements['datum/content'].text
        #puts annoContent
      else
        annoContent = ''
      end

      #annoContent = anno.elements['datum/content'].text unless anno.elements['datum/content'].nil? || ''
      if anno.elements['datum/client-id'].nil?
        annoID = ''
      else
        annoID = anno.elements['datum/client-id'].text
      end
      #annoID = anno.elements['datum/client-id'].text unless anno.elements['datum/client-id'].nil? || ''

      if anno.elements['datum/created-date'].nil?
        puts 'setting to now'
        annoCreatedDate = Time.now
      else
        annoCreatedDate = Time.at(anno.elements['datum/created-date'].text.to_i) 
      end

      if anno.elements['datum/modified-date'].nil?
        annoModDate = 0
      else
        annoModDate = Time.at(anno.elements['datum/modified-date'].text.to_i) #unless anno.elements['datum/modified-date'].nil? || 0
      end

      if anno.elements['datum/title'].nil?
        annoTitle = ''
      else
        annoTitle = anno.elements['datum/title'].text
      end

      if anno.elements['datum/end-phrase'].nil?
        annoEndPhrase = ''
      else
        annoEndPhrase = anno.elements['datum/end-phrase'].text 
      end

      if anno.elements['datum/start-phrase'].nil?
        annoStartPhrase = ''
      else
        annoStartPhrase = anno.elements['datum/start-phrase'].text 
      end

      if anno.elements['datum/annotation-category-id'].nil? 
        annoCat = ''
        annoWordCat = ''
      else
        annoCat = anno.elements['datum/annotation-category-id'].text 
        annoWordCat = config[annoCat] || ''
      end
      
      if anno.elements['datum/book-begin'].nil? 
        annoBookBegin = nil
      else
        annoBookBegin = bookName[anno.elements['datum/book-begin'].text.to_i] 
      end

      if anno.elements['datum/book-end'].nil? 
        annoBookEnd = nil
      else
        annoBookEnd = bookName[anno.elements['datum/book-end'].text.to_i]
      end

      if anno.elements['datum/chapter-begin'].nil?
        annoChapStart = nil
      else 
        annoChapStart = anno.elements['datum/chapter-begin'].text 
      end

      if anno.elements['datum/chapter-end'].nil? 
        annoChapEnd = nil
      else
        annoChapEnd = anno.elements['datum/chapter-end'].text 
      end

      if anno.elements['datum/verse-begin'].nil?
        annoVerseStart = nil
      else
        annoVerseStart = anno.elements['datum/verse-begin'].text 
      end

      if anno.elements['datum/verse-end'].nil? 
        annoVerseEnd = nil
      else
        annoVerseEnd = anno.elements['datum/verse-end'].text 
      end

      # we stuck our associations in a multimap earlier
      # now we query that multimap for each annotation to see if it has
      # an associated tag
      aTags = []
      annoTagAssocs = tagAssocs[annoID] || ''

      annoTagAssocs.each do |aTA|
        aTags.push(config[aTA])
        #@log.info(config[aTA])
      end

      #if 'true' != anno.elements['datum/book-begin'].attributes['nil']
      if annoBookBegin
        verseSelection = ''
        verseSelection = verseSelection + annoBookBegin

        if annoChapStart 
          verseSelection = verseSelection + ' ' + annoChapStart
        end
        #verseSelection = verseSelection + annoChapStart
        if annoVerseStart
          verseSelection = verseSelection + ':' + annoVerseStart
        end

        verseSelectionEnd = ''
        if anno.elements['datum/book-begin'].text.to_i < anno.elements['datum/book-end'].text.to_i

          verseSelectionEnd = verseSelectionEnd + annoBookEnd
          if annoChapEnd
            verseSelectionEnd = verseSelectionEnd + ' ' + annoChapEnd
            verseSelectionEnd = verseSelectionEnd + ':' + annoVerseEnd
          else
            verseSelectionEnd = verseSelectionEnd + verseSelectionEnd
          end
          if verseSelectionEnd != ''
            verseSelection = verseSelection + ' – ' + verseSelectionEnd
          end
=begin
        elsif anno.elements['book-begin'].text.to_i < anno.elements['book-end'].text.to_i

          if annoChapEnd
            verseSelectionEnd = verseSelectionEnd + ' ' + annoChapEnd
            verseSelectionEnd = verseSelectionEnd + ':' + annoVerseEnd
          else
            verseSelectionEnd = verseSelectionEnd + verseSelectionEnd
          end
          if verseSelectionEnd != ''
            verseSelection = verseSelection + ' – ' + verseSelectionEnd
          end
=end
        end
      end

      # Pull desired standard tags from config
      tags = config['tags'] || ''
      tags = tags.scan(/#([A-Za-z0-9]+)/m).map { |tag| tag[0].strip }.delete_if {|tag| tag =~ /^\d+$/ }.uniq.sort

      # Pull Annotation Category (with hierarchy) and push onto tag array
      tags = tags.push(annoWordCat)

      # Push the Name of the Bible book if there is one
      tags = tags.push(annoBookBegin)
      # Grab those tags from way up
      aTags.each do |thisTag|
        tags = tags.push(thisTag)
      end

      options = {}
      options['content'] = "#{annoTitle}\n\n#{verseSelection}\n\n#{annoContent}"
      if annoModDate != 0
        options['datestamp'] = annoModDate.utc.iso8601
      elsif annoCreatedDate
        options['datestamp'] = annoCreatedDate.utc.iso8601
      end
      options['starred'] = false
      options['uuid'] = %x{uuidgen}.gsub(/-/,'').strip
      options['tags'] = tags


      # Create a journal entry
      # to_dayone accepts all of the above options as a hash
      # generates an entry base on the datestamp key or defaults to "now"
      sl = DayOne.new
      sl.to_dayone(options)


    end

    # This section returns the highest sequence number in our run and then updates config
    seqNums = REXML::XPath.each(doc, '//sequence-number')
    bunchONums = []
    #seqNumMax = seqNums.max
    seqNums.each do |seqNum|
      bunchONums.push(seqNum.text.to_i)
    end

    seqNumMax = bunchONums.max
    #@log.info( seqNumMax )
    config['lastSequenceNumber'] = seqNumMax

    return config



  end


  # every plugin must contain a do_log function which creates a new entry using the DayOne class (example below)
  # @config is available with all of the keys defined in "config" above
  # @timespan and @dayonepath are also available
  # returns: nothing
  def do_log
    if @config.key?(self.class.name)
      config = @config[self.class.name]
      # check for a required key to determine whether setup has been completed or not
      if !config.key?('otUser') || config['otUser'] == []
        @log.warn("<Service> has not been configured or an option is invalid, please edit your slogger_config file.")
        return
      else
        # set any local variables as needed
        username = config['otUser']
        password = config['otPass']
        cfgSeqNum = config['lastSequenceNumber'] || 0
      end
    else
      @log.warn("OliveTree has not been configured or a feed is invalid, please edit your slogger_config file.")
      return
    end
    @log.info("Logging OliveTree posts for #{username}")

    ##additional_config_option = config['additional_config_option'] || false
    tags = config['tags'] || ''
    tags = "\n\n#{@tags}\n" unless @tags == ''

    today = @timespan

    # Perform necessary functions to retrieve posts

    # First, we query the status URL to determine highest sequence number
    annoXML = config['annotationXML'] || ''

    statusURL = 'https://sync.olivetree.com/syncables/status.xml?device_id=001&do_not_lock=true&sync_client_protocol_version=4'
    uri = URI(statusURL)
    req = Net::HTTP::Get.new(uri)
    req.basic_auth username, password
    ##req.use_ssl = true


    res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) {|http|
      http.request(req)
    }

    case res
      when Net::HTTPSuccess, Net::HTTPRedirection then
        xml_data = res.body
      else
        puts res.error!
    end




    xml_data = res.body
    doc = REXML::Document.new(xml_data);
    doc.root.each_element('//customer') { |item|
        qrdSeqNumEl = item.elements['sequence-number'] || 0
        qrdSeqNum = qrdSeqNumEl.get_text.value # comes in as text
        intQrdSeqNum = qrdSeqNum.to_i # convert to integer

        # This part is this way just for debugging — otherwise, it works awesome!
        # config = bigQuery # comment this out
        # uncomment these
        i = config['lastSequenceNumber']
        runCount = 0
        while i < intQrdSeqNum do
          config = bigQuery
          i = config['lastSequenceNumber']
          # runaway prevention
          runCount += 1
          if runCount > 20
            break
          end
        end


        return config

    }



    # create an options array to pass to 'to_dayone'
    # all options have default fallbacks, so you only need to create the options you want to specify

    # To create an image entry, use `sl.to_dayone(options) if sl.save_image(imageurl,options['uuid'])`
    # save_image takes an image path and a uuid that must be identical the one passed to to_dayone
    # save_image returns false if there's an error

  end



  def helper_function(args)
    # add helper functions within the class to handle repetitive tasks
  end
end
