# encoding: utf-8
=begin

 * Name: SiSU

 * Description: a framework for document structuring, publishing and search

 * Author: Ralph Amissah

 * Copyright: (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
   2007, 2008, 2009, 2010, 2011, 2012, 2013 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>

   <http://www.sisudoc.org/sisu/en/manifest/gpl.fsf.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>

 * Download:
   <http://www.sisudoc.org/sisu/en/SiSU/download.html>

 * Git
   <http://sources.sisudoc.org/gitweb/?p=code/sisu.git;a=summary>
   <http://sources.sisudoc.org/?p=code/sisu.git;a=blob;f=lib/sisu/v5/digests.rb;hb=HEAD>

 * Ralph Amissah
   <ralph@amissah.com>
   <ralph.amissah@gmail.com>

 ** Description: document digests (md5|sha256) and structure processing

=end
module SiSU_DigestView
  require_relative 'particulars'                        # particulars.rb
  require_relative 'prog_text_translation'              # prog_text_translation.rb
  require_relative 'shared_markup_alt.rb'               # shared_markup_alt.rb
  class Source
    @@dg=nil
    def initialize(opt)
      @opt=opt
      @fnb=@opt.fnb
      @@endnotes_para=[]
      @@dg=nil
      @dg=@@dg ||=SiSU_Env::InfoEnv.new.digest.type
      @particulars=SiSU_Particulars::CombinedSingleton.instance.get_all(opt)
    end
    def read
      begin
        @env,@md,@ao_array=@particulars.env,@particulars.md,@particulars.ao_array
        unless @opt.act[:quiet][:set]==:on
          tool=(@opt.act[:verbose][:set]==:on \
          || @opt.act[:verbose_plus][:set]==:on \
          || @opt.act[:maintenance][:set]==:on) \
          ? "#{@env.program.text_editor} file://#{@md.file.output_path.hash_digest.dir}/#{@md.file.base_filename.hash_digest}"
          : "[#{@opt.f_pth[:lng_is]}] #{@opt.fno}"
          (@opt.act[:verbose][:set]==:on \
          || @opt.act[:verbose_plus][:set]==:on \
          || @opt.act[:maintenance][:set]==:on) \
          ? SiSU_Screen::Ansi.new(@opt.act[:color_state][:set],"Document #{@dg} Digests",tool).green_hi_blue
          : SiSU_Screen::Ansi.new(@opt.act[:color_state][:set],"Document #{@dg} Digests",tool).green_title_hi
          if @opt.act[:verbose_plus][:set]==:on \
          or @opt.act[:maintenance][:set]==:on
            SiSU_Screen::Ansi.new(@opt.act[:color_state][:set],@opt.fns,"file://#{@md.file.output_path.hash_digest.dir}/#{@md.file.base_filename.hash_digest}").flow
          end
        end
        SiSU_DigestView::Source::Scroll.new(@particulars).songsheet
      rescue
        SiSU_Errors::Rescued.new($!,$@,@opt.cmd,@opt.fns).location do
          __LINE__.to_s + ':' + __FILE__
        end
      ensure
      end
    end
    private
    class Scroll <Source
      @@dl=nil
      @@ds={}
      @@description,@@ds[:digests],@@ds[:tree],@@ds[:summary],@@sc_info=[],[],[],[],[]
      def initialize(particulars) #data='',md='')
        @particulars=particulars
        @data,@env,@md=@particulars.ao_array,@particulars.env,@particulars.md
        SiSU_Env::FileOp.new(@md).mkdir
        @@dg ||=@env.digest.type
        @@dl ||=@env.digest.length
        @dg,@dl=@@dg,@@dl
        l=SiSU_Env::StandardiseLanguage.new(@md.opt.lng).language
        @language=l[:n]
        @tr=SiSU_Translate::Source.new(@md,@language)
        @sp=' '
      end
      def songsheet
        @@description,@@ds[:digests],@@ds[:tree],@@ds[:summary],@@sc_info=[],[],[],[],[]
        message_digest
        ao_structure
        supplementary
        output
      end
      def description(f,e='')
        puts f + e.to_s if @md.opt.act[:verbose_plus][:set]==:on
        @@description << f << e
      end
      def digests(f,e='')
        puts f if @md.opt.act[:verbose_plus][:set]==:on
        @@ds[:digests] << f + "\n"
      end
      def ao_structure_tree(f,e='')
        puts f + e.to_s if @md.opt.act[:verbose_plus][:set]==:on
        @@ds[:tree] << f << e
      end
      def ao_structure_summary(f,e='')
        puts f + e.to_s if @md.opt.act[:verbose_plus][:set]==:on
        @@ds[:summary] << f << e
      end
      def rcinfo(f,e='')
        puts f + e.to_s if @md.opt.act[:verbose_plus][:set]==:on
        @@sc_info << f << e
      end
      def output
        file=SiSU_Env::FileOp.new(@md)
        filename_digest=file.write_file.hash_digest
        filename_digest << @@description.join << @@ds[:digests].join << @@ds[:tree].join << @@ds[:summary].join << @@sc_info.join
      end
      def rgx_txt(txt)
        txt=txt.gsub(/([()])/,"\\\\\\1")
      end
      def message_digest
        data=@data
        sys=SiSU_Env::SystemCall.new
        @p=[]
        @g,@v,@r='','',''
        data.each do |para|
          x=nil
          y,para_endnotes=[],[]
          if para =~/#{Mx[:id_o]}~(\d+);(?:(?:\w|[0-6]:)\d+);(?:\w\d+)#{Mx[:id_c]}#{Mx[:id_o]}([0-9a-f]{#{@dl}}):([0-9a-f]{#{@dl}})#{Mx[:id_c]}/
            ocn,d_clean,d_all=$1,$2,$3
            @ocn=ocn unless ocn.to_i==0
            para=para.gsub(/#{Mx[:fa_bold_o]}(.+?)#{Mx[:fa_bold_c]}/,'<b>\1</b>').
              gsub(/#{Mx[:fa_italics_o]}(.+?)#{Mx[:fa_italics_c]}/,'<i>\1</i>').
              gsub(/#{Mx[:fa_underscore_o]}(.+?)#{Mx[:fa_underscore_c]}/,'<u>\1</u>')
            if para=~/#{Mx[:en_a_o]}[\d*+]+.+?#{Mx[:id_o]}[0-9a-f]{#{@dl}}#{Mx[:id_c]}#{Mx[:en_a_c]}/
              para_endnotes << para.scan(/(?:#{Mx[:en_a_o]}|#{Mx[:en_b_o]})([\d*+]+).+?#{Mx[:id_o]}([0-9a-f]{#{@dl}})#{Mx[:id_c]}(?:#{Mx[:en_a_c]}|#{Mx[:en_b_c]})/)
            end
            images=[]
            if para !~/^%+\s/ \
            and para =~/(?:^|[^_\\])#{Mx[:lnk_o]}(\S+\.(png|jpg|gif))\s.+?#{Mx[:lnk_c]}(?:#{Mx[:url_o]}\S+?#{Mx[:url_c]}|image)/
              images=para.scan(/(?:^|[^_\\])#{Mx[:lnk_o]}(\S+\.(?:png|jpg|gif))\s.+?#{Mx[:lnk_c]}(?:#{Mx[:url_o]}\S+?#{Mx[:url_c]}|image)/).flatten
            else nil
            end
            x=case para
            when /^#{Mx[:meta_o]}title#{Mx[:meta_c]}/
              "\n" << '  '*0 << '@' << '  '*9
            when /^#{Mx[:meta_o]}subtitle#{Mx[:meta_c]}/
              "\n" << '  '*1 << '@' << '  '*8
            when /^#{Mx[:lv_o]}1:/ #fix Mx[:lv_o]
              "\n" << '  '*2 << ':A ' << '  '*6 << '- ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all
            when /^#{Mx[:lv_o]}2:/ #fix Mx[:lv_o]
              "\n" << '  '*3 << ':B ' << '  '*5 << '- ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all
            when /^#{Mx[:lv_o]}3:/ #fix Mx[:lv_o]
              "\n" << '  '*4 << ':C ' << '  '*4 << '- ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all
            when /^#{Mx[:lv_o]}4:/ #fix Mx[:lv_o]
              "\n" << '  '*5 << '1' << '  '*4 << '- ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all
            when /^#{Mx[:lv_o]}5:/ #fix Mx[:lv_o]
              "\n" << '  '*6 << '2' << '  '*3 << '- ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all
            when /^#{Mx[:lv_o]}6:/ #fix Mx[:lv_o]
              "\n" << '  '*7 << '3' << '  '*2 << '- ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all
            else
              if para =~/MD5\(\S+?\.sst\)=\s*<u>([0-9a-f]{#{@dl}})<\/u>/                                        #watch
                @n,@s=/MD5\((\S+?\.sst)\)=\s*<u>([0-9a-f]{#{@dl}})<\/u>/.match(para)[1,2]
              end
              x=unless ocn =~ /^0$/
                if images \
                and images.length > 0 # then get path of image & produce digest
                  @image_name,@image_dgst,@img=[],[],[]
                  images.each do |i|
                    image_source=if FileTest.file?("#{@env.path.image_source_include_local}/#{i}")
                      @env.path.image_source_include_local
                    elsif FileTest.file?("#{@env.path.image_source_include_remote}/#{i}")
                      @env.path.image_source_include_remote
                    elsif FileTest.file?("#{@env.path.image_source_include}/#{i}")
                      @env.path.image_source_include
                    else
                      SiSU_Screen::Ansi.new(@md.opt.act[:color_state][:set],"ERROR - image:", %{"#{i}" missing}, "search locations: #{@env.path.image_source_include_local}, #{@env.path.image_source_include_remote} and #{@env.path.image_source_include}").error2 unless @md.opt.act[:quiet][:set]==:on
                      nil
                    end
                    @img << /\S+\.(png|jpg|gif)/.match(i)[1]
                    not_found_msg='image not found'
                    if image_source
                      para_image = image_source + '/' + i
                      @image_name << i
                      @image_dgst << (@dg =~/^sha(?:2|256)$/) \
                      ? sys.sha256(para_image)
                      : sys.md5(para_image)
                    else
                      @image_name << '  '*16 + i + ' [image missing]'
                      @image_dgst << ''
                      @image_dgst[1]=not_found_msg + ' '*(32-not_found_msg.length)
                    end
                  end
                  line= "\n" + '  '*9 + ' - ' + ocn + ' '*(10-ocn.length) + d_clean + ' ' + d_all + "\n"
                  line_image=[]
                  c=0
                  @image_name.each do |ok|
                    line_image << %{                     #{@img[c]}       #{@image_dgst[c][1]}                                  #{@image_name[c]}}
                    c +=1
                  end
                  line=line + line_image.join("\n")
                else "\n" + '  '*9 + ' - ' + ocn + ' '*(10-ocn.length) + d_clean + ' ' + d_all
                end
              else
                prefix=''
                metad=[@tr.full_title,@tr.author,@tr.translator,@tr.illustrator,@tr.prepared_by,@tr.digitized_by,@tr.description,@tr.subject,@tr.abstract,@tr.publisher,@tr.contributor,@tr.date_created,@tr.date_issued,@tr.date_available,@tr.date_modified,@tr.date_valid,@tr.date,@tr.type,@tr.format,@tr.rights,@tr.identifier,@tr.source,@tr.language,@tr.language_original,@tr.relation,@tr.coverage,@tr.keywords,@tr.comments,@tr.cls_loc,@tr.cls_dewey,@tr.cls_gutenberg,@tr.cls_isbn,@tr.prefix_a,@tr.prefix_b,@tr.sourcefile,@tr.sourcefile_digest,@tr.last_generated,@tr.sisu_version,@tr.ruby_version,@tr.sc_number,@tr.sc_date,'Generated by: ','Ruby version: ']
                metad.each do |n|
                  m=rgx_txt(n)
                  if m=~/\S+/ \
                  and para=~/^#{m}:/
                    x,o=0,18
                    while x < 2; o = o + 2
                      x=o - n.length
                    end
                    space=' '*x
                    prefix="#{n.downcase}#{space}"
                    break
                  else prefix='  '*9
                  end
                end
                m_title=rgx_txt(@tr.full_title)
                m_author=rgx_txt(@tr.author)
                m_sourcefile_digest=rgx_txt(@tr.sourcefile_digest)
                m_sisu_version=rgx_txt(@tr.sisu_version)
                m_last_generated=rgx_txt(@tr.last_generated)
                m_ruby_version=rgx_txt(@tr.ruby_version)
                case para
                when /#{m_title}: /
                  @t=/#{m_title}: (.+?)#{Mx[:id_o]}~\d;(?:\w|[0-6]:)\d+;\w\d+#{Mx[:id_c]}#{Mx[:id_o]}[0-9a-f]{#{@dl}}:[0-9a-f]{#{@dl}}#{Mx[:id_c]}/.match(para)[1].gsub(/<\/?u>/,'').strip
                when /#{m_author}: /
                  @c=/#{m_author}: (.+?)#{Mx[:id_o]}~\d;(?:\w|[0-6]:)\d+;\w\d+#{Mx[:id_c]}#{Mx[:id_o]}[0-9a-f]{#{@dl}}:[0-9a-f]{#{@dl}}#{Mx[:id_c]}/.match(para)[1].gsub(/<\/?u>/,'').strip
                when /#{m_sourcefile_digest}.+?<u>/                                        #watch
                  dgst_extra="\n" << ' '*21 << 'source' << ' '*4 << @md.dgst[1] << ' '*34 << @md.fns
                when /#{m_sisu_version}: /
                  @v=/#{m_sisu_version}: (.+?)#{Mx[:id_o]}~\d;(?:\w|[0-6]:)\d+;\w\d+#{Mx[:id_c]}#{Mx[:id_o]}[0-9a-f]{#{@dl}}:[0-9a-f]{#{@dl}}#{Mx[:id_c]}/.match(para)[1].gsub(/<\/?u>/,'').strip
                when /#{m_last_generated}: /
                  @g=/#{m_last_generated}: (.+?)#{Mx[:id_o]}~\d;(?:\w|[0-6]:)\d+;\w\d+#{Mx[:id_c]}#{Mx[:id_o]}[0-9a-f]{#{@dl}}:[0-9a-f]{#{@dl}}#{Mx[:id_c]}/.match(para)[1].gsub(/<\/?u>/,'').strip
                when /#{m_ruby_version}: /
                  @r=/#{m_ruby_version}: (.+?)#{Mx[:id_o]}~\d;(?:\w|[0-6]:)\d+;\w\d+#{Mx[:id_c]}#{Mx[:id_o]}[0-9a-f]{#{@dl}}:[0-9a-f]{#{@dl}}#{Mx[:id_c]}/.match(para)[1].gsub(/<\/?u>/,'').strip
                end
                dgst_extra ||=''
                "\n" << prefix << ' - ' << ocn << ' '*(10-ocn.length) << d_clean << ' ' << d_all << dgst_extra << "\n"
              end
            end
            para_endnotes[0].each { |e| y << "\n" + ' '*(28-e[0].length) + "[#{e[0].to_s}] #{e[1].to_s}" } if para_endnotes[0]
            if y; digests(x,y)
            else  digests(x)
            end
          end
        end
        manifest="#{@env.url.root}/#{@md.fnb}/sisu_manifest.html"
        a=%{ocn      digest clean (no markup/notes),#{@sp*33}digest all (includes markup & endnotes)\n}
        description("#{@md.title.full}\n")
        description("#{@md.author}\n")
        description("#{@md.fns}\n")
        description("----------------------------------------------\n")
        description("SiSU Document Content Certificate (Digest/DCC)\n")
        description("----------------------------------------------\n")
        description("                               #{@dg} digests\n")
        description("------------\n")
        description("Sourcefile digest:             #{@md.dgst[1]}\n")
        description("  source filename:             #{@md.fns}\n")
        description("available outputs:             #{manifest}\n")
       #description("  time generated:                #{@g}\n")
       #description("  SiSU version used:             #{@v}\n")
       #description("  Ruby version used:             #{@r}\n")
        description("------------\n")
        description("Document Digests\n")
        description(a)
      end
      def ao_structure
        data=@data
        endnotes=nil
        data.each do |t_o|
          dgst=SiSU_TextRepresentation::ModifiedTextPlusHashDigest.new(@md,t_o).composite.dgst
          if dgst
            if t_o.is==:heading
              digests("#{@sp*0}#{dgst[:ocn]}#{@sp*(8-dgst[:ocn].to_s.length)}#{dgst[:dgst_stripped_txt]} #{dgst[:dgst_markedup_txt]} #{dgst[:is]} #{t_o.lv}")
            elsif t_o.is==:heading_insert
              digests("#{@sp*0}[#{dgst[:ocn]}]#{@sp*(6-dgst[:ocn].to_s.length)}#{dgst[:dgst_stripped_txt]} #{dgst[:dgst_markedup_txt]} #{dgst[:is]} #{t_o.lv}")
            else
              digests("#{@sp*0}#{dgst[:ocn]}#{@sp*(8-dgst[:ocn].to_s.length)}#{dgst[:dgst_stripped_txt]} #{dgst[:dgst_markedup_txt]} #{dgst[:is]}")
              if dgst[:images]
                dgst[:images].each do |img|
                  digests("#{@sp*8}#{img[:img_dgst]}#{@sp*66}#{img[:img_type]} #{img[:img_name]}")
                end
              end
            end
            if dgst[:endnotes]
              dgst[:endnotes].each do |en|
                digests("#{@sp*8}#{en[:note_dgst]}#{@sp*66}note [#{en[:note_number]}]")
                endnotes=en[:note_number]
              end
            end
          end
        end
        l=Hash.new(0)
        ocn=nil
        ao_structure_tree("------------\n")
        ao_structure_tree("document structure[*]\n")
        data.each do |t_o|
          if t_o.is==:heading
            x=case t_o.ln
            when 1; l[1] +=1 #fix Mx[:lv_o]
              '  '*0 << ':A'
            when 2; l[2] +=1 #fix Mx[:lv_o]
              '  '*1 << ':B'
            when 3; l[3] +=1 #fix Mx[:lv_o]
              '  '*2 << ':C'
            when 4; l[4] +=1 #fix Mx[:lv_o]
              '  '*3 << '1'
            when 5; l[5] +=1 #fix Mx[:lv_o]
              '  '*4 << '2'
            when 6; l[6] +=1 #fix Mx[:lv_o]
              '  '*5 << '3'
            else nil
            end
          end
          ocn=t_o.ocn if defined? t_o.ocn and t_o.is !=:heading_insert
          ao_structure_tree("#{x}\n") if x and not x.empty?
        end
        ao_structure_tree("  [*] heading levels\n")
        ao_structure_summary("------------\n")
        ao_structure_summary("document structure[*]\n")
        [0,1,2,3,4,5,6].each do |y|
          v=case y
          when 1; ':A'
          when 2; ':B'
          when 3; ':C'
          when 4; '1 '
          when 5; '2 '
          when 6; '3 '
          end
          ao_structure_summary("#{v}            = #{l[y]}\n") if l[y] > 0
        end
        ao_structure_summary("objects (ocn) = #{ocn}\n")
        ao_structure_summary("endnotes      = #{endnotes}\n")
        ao_structure_summary("  [*] number of headers (@) and of each heading level (:A to :C and 1 to 3)\n")
      end
      def supplementary
        if defined? @md.sc_number \
        and @md.sc_number
          rcinfo("------------\n")
          rcinfo("source control information\n")
          rcinfo("  (the following information while not important for document content certification\n   may help the publisher in locating the version referred to)\n")
          rcinfo("  rcs version number:            #{@md.sc_number}\n")
          if defined? @md.sc_date \
          and @md.sc_date
            rcinfo("  rcs date:                      #{@md.sc_date}\n")
          end
          if defined? @md.sc_time \
          and @md.sc_time
            rcinfo("  rcs time:                      #{@md.sc_time}\n")
          end
        end
        rcinfo("------------\n")
        rcinfo("Note: the time generated related fields (text and digests) will vary between otherwise identical document outputs\n")
      end
    end
  end
end
__END__
