# encoding: utf-8
=begin

* Name: SiSU

** Description: documents, structuring, processing, publishing, search
*** preprocessing, convert bi-footnotemarker-footnote to inline
   footnotes, invoked using: sisu --to-footnotes filename.sst

** Author: Ralph Amissah
  [ralph@amissah.com]
  [ralph.amissah@gmail.com]

** Copyright: (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Ralph Amissah,
  All Rights Reserved.

** License: GPL 3 or later:

  SiSU, a framework for document structuring, publishing and search

  Copyright (C) Ralph Amissah

  This program is free software: you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the Free
  Software Foundation, either version 3 of the License, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program. If not, see [http://www.gnu.org/licenses/].

  If you have Internet connection, the latest version of the GPL should be
  available at these locations:
  [http://www.fsf.org/licensing/licenses/gpl.html]
  [http://www.gnu.org/licenses/gpl.html]

** SiSU uses:
  * Standard SiSU markup syntax,
  * Standard SiSU meta-markup syntax, and the
  * Standard SiSU object citation numbering and system

** Hompages:
  [http://www.jus.uio.no/sisu]
  [http://www.sisudoc.org]

** Git
  [http://git.sisudoc.org/gitweb/?p=code/sisu.git;a=summary]
  [http://git.sisudoc.org/gitweb/?p=code/sisu.git;a=blob;f=lib/sisu/set_do_inline_footnotes.rb;hb=HEAD]

=end
module SiSU_ConvertFootnotes
  require_relative 'se'                                 # se.rb
    include SiSU_Env
  require_relative 'dp'                                 # dp.rb
    include SiSU_Param
  require_relative 'ao_syntax'                          # ao_syntax.rb
    include SiSU_AO_Syntax
  require_relative 'i18n'                               # i18n.rb
  class Instantiate < SiSU_Param::Parameters::Instructions
     @@flag={} #Beware!!
    def initialize
      @@flag['table_to']=false
      @@counter=@@column=@@columns=@@flag_vocab=0
      @@endnote={}
      @@endnote_array=@@word_mode=[]
      @@endnote_call_counter=1
      @@line_mode=''
    end
  end
  class Source <Instantiate
    @@ao_array=[]
    @@fns=nil
    def initialize(opt)
      @opt=opt
      @@fns||@opt.fns
      @my_make=SiSU_Env::CreateFile.new(@opt.fns)
      @fnm=SiSU_Env::InfoFile.new(@opt.fns).marshal.ao_content
    end
    def read                                                                     #creates ao
      begin
        @@ao_array=[]
        @@fns=@opt.fns
        create_ao
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.cmd,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        Instantiate.new
      end
    end
    def get                                                                      #reads ao, unless does not exist then creates first
      begin
        ao=[]
        unless @@fns==@opt.fns
          @@fns=@opt.fns
          @@ao_array=[]
        end
        ao=(@@ao_array.empty?) \
        ? read_fnm
        : @@ao_array.dup #check
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.cmd,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
        Instantiate.new
      end
    end
  protected
    def create_ao
      ao_array=[]
      SiSU_Screen::Ansi.new(
        @opt.act[:color_state][:set],
        'convert footnotes'
      ).green_title_hi unless @opt.act[:quiet][:set]==:on
      file_array=IO.readlines(@opt.fns,'')
      file_array.each do |l|
        if l =~/\r\n/ then l.gsub!(/\r\n/,"\n")
        end
      end
      meta=file_array.dup
      meta=meta.join.split("\n\n") #check whether can be eliminated, some of these are large objects to have twice
      @md=SiSU_Param::Parameters::Instructions.new(meta,@opt).extract
      if @md.en[:mismatch]==0 \
      or @md.opt.selections.str =~/=footnotes-force/
        meta=nil
        ao=SiSU_ConvertFootnotes::Make.new(@md,file_array).song
        SiSU_Screen::Ansi.new(
          @opt.act[:color_state][:set],
          @opt.fns,
          "#{@md.fns}.fn"
        ).output if @md.opt.act[:verbose][:set]==:on
        SiSU_Screen::Ansi.new(
          @opt.act[:color_state][:set],
          "#{@md.fns}.fn -> #{@md.fns}.fn"
        ).txt_red unless @md.opt.act[:quiet][:set]==:on
        ao.each {|s| ao_array << "#{s.strip}\n\n" unless s.strip.empty?}
        ao_array
      else
        SiSU_Screen::Ansi.new(
          @md.opt.act[:color_state][:set],
          '*WARN* no footnote conversion done, problem with source file',
          'to override use --convert=footnote-force (this is not advised)'
        ).warn unless @md.opt.act[:quiet][:set]==:on
        ''
      end
    end
    def read_fnm
      ao=[]
      ao=(FileTest.file?(@fnm)) \
      ? (File.open(@fnm){ |f| ao=Marshal.load(f)})
      : (SiSU_ConvertFootnotes::Source.new(@opt).create_ao) #watch
    end
  end
  class Output
    def initialize(md,data)
      @md,@data=md,data
      @my_make=SiSU_Env::CreateFile.new(@md.fns)
      SiSU_Env::InfoEnv.new(@md.fns)
      @hard="#{Dir.pwd}/#{@md.fns}.fn"
    end
    def hard_output
      filename_note=@my_make.file_note
      @data.each {|s| filename_note.puts s.strip + "\n\n" unless s.strip.empty?}
    end
  end
  class Make
    @@endnote={}
    @@endnote_array=@@word_mode=[]
    @@endnote_call_counter=1
    @@comment='%'
    @@flag={ ['table_to']=>false }
    def initialize(md,data)
      @md,@data=md,data
      @@word_mode=[]
      @env=SiSU_Env::InfoEnv.new(@md.fns)
      l=SiSU_Env::StandardiseLanguage.new(@md.opt.lng).language
      @language=l[:n]
      @translate=SiSU_Translate::Source.new(@md,@language)
    end
    def reset
      @@counter=@@column=@@columns=@@flag_vocab=0
      @@endnote={}
      @@endnote_array=@@word_mode=[]
      @@endnote_call_counter=1
      @@line_mode=''
    end
    def song
      reset
      data=@data
      @metafile="#{@env.processing_path.ao}/#{@md.fns}.meta"
      SiSU_Env::CreateFile.new(@md.fns)
      data=data.join.split("\n\n")
      data_new=[]
      data.each do |x|
        data_new << (x =~ /\n\n/m) \
        ? (x.split(/\n\n+/))
        : x
      end
      data=data_new.flatten
      data=SiSU_ConvertFootnotes::Make.new(@md,data).character_check
      data=SiSU_ConvertFootnotes::Make.new(@md,data).endnotes
      SiSU_ConvertFootnotes::Output.new(@md,data).hard_output
      reset
      data
    end
  protected
    def vocabulary
      data=@data
      tuned_file,vocab_insert=[],[]
      data.each do |para|
        if para =~/^1~/ \
        and @@flag_vocab==0
          vocab_insert << '@vocabulary: lex' << "\n\n" << para
          tuned_file << vocab_insert unless para.nil?
          @@flag_vocab=1
        else tuned_file << para unless para.nil?
        end
      end
      tuned_file
    end
    def character_check
      reset
      data=@data
      @tuned_file=[]
      endnote_no=1
      data.each do |para|
        para.strip!
        para.gsub!(/^[{~}]\s*$/,'')
        para.gsub!(/^#{@@comment}.*/,'')                                       #remove comment and divider #%
        para.gsub!(/<~#>|~#\s*/,'~#')
        para.gsub!(/-#\s*/,'-#')
        para.gsub!(/(~\{ )\s+/,'\1')
        para.gsub!(/ \/\//,'<br />')                                           #added 2004w29
        para.gsub!(/<br>/,'<br />')                                            #needed by xml, xhtml etc.
        para.gsub!(/`/,"'")
        para.gsub!(/\342\200\231/,"'") #if para =~/’/  #Avoid #&lsquo; &rsquo; #&ldquo; &rdquo;
        para.gsub!(/\t/,' ')
        para.gsub!(/�/,' ') #watch, replace with char code
        para.gsub!(/[“”]/,'""')
        para.gsub!(/[­–—]/,'-')   #— – chk
        para.gsub!(/·/,'*')
        para.gsub!(/\\copy(?:right)?\b/,'&#169;')
        para.gsub!(/\\trademark\b|\\tm\b/,'&#174;')
        para.gsub!(/\44/,'&#36;') #$ watch
        para=para + "\n"
        case para
        when /\^~/                                                             # endnotes
                                                                               #% Note must do this first (earlier loop) and then enter gathered data into ~^\d+
          sub_para=para.dup
          @@endnote_array << sub_para.gsub!(/\n/,'').gsub!(/\^~\s+(.+)\s*/,'~{ \1 }~').strip
           endnote_no+=1
          para=nil if para =~/\^~ .+/ #removes 'binary' endnote now in endnote array for later insertion
        end
        @tuned_file << para unless para.nil?
      end
      @tuned_file
    end
    def name_endnote_seg
      data=@data
      @tuned_file=[]
      data.each do |para|
        para.gsub!(/<:3>\s*<:ee>/,
          "#{@@endnote['special_align']} <p /><br />\r " +
          "#{@@endnote['seg_name_3']} <p /> " +
          "#{@@endnote['special_align_close']}")
        para.gsub!(/<:2>\s*<:ee>/,
          "#{@@endnote['special_align']} <p /><br />\r " +
          "#{@@endnote['seg_name_2']} <p />" +
          "#{@@endnote['special_align_close']}")
        para.gsub!(/<:1>\s*<:ee>/,
          "#{@@endnote['special_align']} <p /><br />\r " +
          "#{@@endnote['seg_name_1']} <p /> " +
          "#{@@endnote['special_align_close']}")
        @tuned_file << para
      end
      if @md.flag_auto_endnotes \
      and @md.flag_separate_endnotes_make
        @tuned_file << "\n1~endnotes Endnotes" #prob numbering, revisit
      end
      @tuned_file << "\n<ENDNOTES>"
      @tuned_file
    end
    def owner_details_seg
      data << '1~owner.details Owner Details'
    end
    def number_sub_heading(para,num,title_no)
      case para
      when /#{num}~- /    then para.gsub!(/#{num}~- /,"#{title_no} ")
      when /^#{num}~#\s*/ then para.gsub!(/^#{num}~#\s*/,"#{title_no} ")
      when /^#{num}~[a-z_\.]+ /
        para.gsub!(/^#{num}~([a-z_\.]+)\s+(.+)/i,%{#{num}~\\1 #{title_no} \\2  <:name##{title_no}>})
      else para.gsub!(/^#{num}~ /,"#{num}~#{title_no} #{title_no} ") #main
      end
      if @md.toc_lev_limit \
      and @md.toc_lev_limit < num
        para.gsub!(/^[2-6]~(?:~\S+)?\s*/,'!_ ')
      end
      para
    end
    def set_heading_top                                                        #% make sure no false positives
      unless @md.set_heading_top
        if (@md.opt.act[:verbose_plus][:set]==:on \
        || @md.opt.act[:maintenance][:set]==:on)
          puts "\tdocument contains no top level heading, (will have to manufacture one)"
        end
        data=@data
        @tuned_file=[]
        data.each do |para|
          unless @md.set_heading_top
            if para !~/^(?:@\S+:|0~\S+)\s/m \
            and para !~/\A\s*\Z/m
              @md.set_heading_top=true
              head=(@md.title.full) \
              ? (":A~ #{@md.title.full}")
              : (':A~ [no title provided]')
              @tuned_file << head
            end
          end
          @tuned_file << para
        end
        @tuned_file
      end
    end
    def set_heading_seg                                                        #% make sure no false positives
      unless @md.set_heading_seg
        if (@md.opt.act[:verbose_plus][:set]==:on \
        || @md.opt.act[:maintenance][:set]==:on)
          puts "\tdocument contains no segment level, (will have to manufacture one)"
        end
        data=@data
        @tuned_file=[]
        data.each do |para|
          unless @md.set_heading_seg
            if para !~/^(?:@\S+:|0~\S+|:[ABC]~)/m \
            and para !~/\A\s*\Z/m \
            and para !~/<:p[bn]>/
              @md.set_heading_seg=true
              head=(@md.title.full) \
              ? ("1~seg [#{@md.title.full}]")
              : ('1~seg [segment]')
              @tuned_file << head
            end
          end
          @tuned_file << para
        end
        @tuned_file
      end
    end
    def set_header_title                                                       #% make sure no false positives
      unless @md.set_header_title
        if (@md.opt.act[:verbose_plus][:set]==:on \
        || @md.opt.act[:maintenance][:set]==:on)
          puts "\t no document title provided, (will have to manufacture one)"
        end
        data=@data
        @tuned_file=[]
        data.each do |para|
          unless @md.set_header_title
            if para !~/^%{1,2}\s/m \
            and para !~/\A\s*\Z/m
              @tuned_file << "0~title #{@md.heading_seg_first}"
              @md.title.full=@md.heading_seg_first
              @md.set_header_title=true
            end
          end
          @tuned_file << para
        end
        @tuned_file
      end
    end
    def endnotes                                                                         #% endnote work zone
      data=@data
      @tuned_file=[]
      endnote_ref=1
      data.each do |para|
        case para                                                                               # manually numbered endnotes <!e(\d)!> <!e_(\d)!> -->
        when /~\{\s+.+?\}~/                                                                               # auto-numbered endnotes <!e!> <!e_!> -->
          para.gsub!(/\s*\}~/,' }~')                                           # required 2003w31
          @word_mode=para.scan(/\S+/)
          word_mode=SiSU_ConvertFootnotes::Make.new(@md,@word_mode).endnote_call_number
          para=word_mode.join(' ')
          endnote_ref+=1
        when /~\^(?:\s|$)|<:e>/                                                #%Note inserts endnotes previously gathered from /^(<!e[:_]!>|[-~]\{{3})/ (in earlier loop)
          word_mode=para.scan(/\S+/)
          word_mode=SiSU_ConvertFootnotes::Make.new(@md,word_mode).endnote_call_number
          para=word_mode.join(' ')
          endnote_ref+=1
        end
        @tuned_file << para
      end
      @tuned_file
    end
    def endnote_call_number
      data=@data
      data.each do |word|
        case word
        when /~\{/
          unless word =~/~\{\*+/
            @@endnote_call_counter+=1
          end
        when /~\^|<:e>/
          word.gsub!(/~\^|<:e>/,"#{@@endnote_array[@@endnote_call_counter-1]}")
          @@endnote_call_counter+=1
        end
      end
    end
    def strip_clean_extra_spaces(s)                                            # ao output tuned
      s=s.dup
      s=s.gsub(/[ ]+([,.;:?](?:$|\s))/,'\1')
      s=s.gsub(/ [ ]+/,' ')
      s=s.gsub(/^ [ ]+/,'')
      s=s.gsub(/ [ ]+$/,'')
      s=s.gsub(/(<\/[bi]>')[ ]+(s )/,'\1\2')
    end
    def strip_clean_of_markup(s)                                               # used for digest, define rules, make same as in db clean
      s=s.dup
      s=s.gsub(/(?:<\/?[ib]>|^:[A-C]~\S+|^[1-6]~\S+|~\{\d+\s.+?\}~)/,'') # markup and endnotes removed
                                                                               #% same as db clean -->
      s=s.gsub(/<del>(.+?)<\/del>/,'DELETED(\1)')                           # deletions
      s=s.gsub(/<sup>(\d+)<\/sup>/,'[\1]')
      s=s.gsub(/(?:&nbsp\\;|#{Mx[:nbsp]})+/,' ')  #checking source Mx not necessary
      s=s.gsub(/\{.+?\.(?:png|jpg|gif).+?\}(?:https?|file|ftp)\\\:\S+ /,' [image] ')             # else image names found in search
      s=s.gsub(/#{Mx[:lnk_o]}.+?\.(?:png|jpg|gif).+?#{Mx[:lnk_c]}#{Mx[:url_o]}\S+?#{Mx[:url_c]}/,' [image] ')             # else image names found in search, re-check
      s=s.gsub(/\s\s+/,' ')
      s=s.strip
    end
  end
end
__END__
@particulars=SiSU_Particulars::CombinedSingleton.instance.get_all(opt)
ao_array=@particulars.ao_array # ao file drawn here
