# encoding: utf-8
module Hyla
  module Commands
    class Generate < Command

      def self.process(args, options = {})

        rendering = options[:rendering] if self.check_mandatory_option?('-r / --rendering', options[:rendering])

        case rendering

          when 'toc2adoc'

            Hyla.logger.info "Rendering : Table of Content to Asciidoc"
            self.check_mandatory_option?('-t / --toc', options[:toc])
            self.check_mandatory_option?('-d / --destination', options[:destination])

            @toc_file = options[:toc]
            @out_dir = options[:destination]
            @project_name = options[:project_name] if options[:project_name]
            @project_name = 'My Project' if !options[:project_name]

            self.table_of_content_to_asciidoc(@toc_file, @out_dir, @project_name)

          when 'adoc2html'

            Hyla.logger.info "Rendering : Asciidoc to HTML"
            self.check_mandatory_option?('-s / --source', options[:source])
            self.check_mandatory_option?('-d / --destination', options[:destination])

            @destination = options[:destination]
            @source = options[:source]

            # Check Style to be used
            new_asciidoctor_option = {
                :template_dirs => [self.backend_dir(options[:backend])],
                :attributes => {
                    'stylesheet' => self.check_style(options[:style])
                }
            }

            merged_options = Configuration[options].deep_merge(new_asciidoctor_option)

            extensions = 'adoc|ad|asciidoc'

            self.asciidoc_to_html(@source, @destination, extensions, merged_options)

          when 'index2html'
            Hyla.logger.info "Rendering : Asciidoctor Indexed Files to HTML"
            self.check_mandatory_option?('-s / --source', options[:source])
            self.check_mandatory_option?('-d / --destination', options[:destination])

            @destination = options[:destination]
            @source = options[:source]

            new_asciidoctor_option = {
                :template_dirs => [self.backend_dir(options[:backend])],
                :attributes => {
                    'stylesheet' => self.check_style(options[:style])
                }
            }

            merged_options = Configuration[options].deep_merge(new_asciidoctor_option)

            # Extension(s) of the files containing include directives
            extensions = 'txt'

            self.asciidoc_to_html(@source, @destination, extensions, merged_options)

          when 'html2pdf'

            Hyla.logger.info "Rendering : Generate PDF from HTML file"

            source_dir = options[:source] if self.check_mandatory_option?('-s / --source', options[:source])
            out_dir = options[:destination] if self.check_mandatory_option?('-d / --destination', options[:destination])

            file_name = options[:file]
            cover_path ||= options[:cover_path]
            header_html_path = options[:header_html_path]
            footer_text = options[:footer_text]

            self.html_to_pdf(file_name, source_dir, out_dir, footer_text, header_html_path, cover_path)

          when 'cover2png'

            Hyla.logger.info "Rendering : Generate Cover HTML page & picture - format png"

            out_dir = options[:destination] if self.check_mandatory_option?('-d / --destination', options[:destination])
            file_name = options[:cover_file]
            image_name = options[:cover_image]

            # Configure Slim engine
            slim_file = Configuration::cover_template
            slim_tmpl = File.read(slim_file)
            template = Slim::Template.new(:pretty => true) { slim_tmpl }

            # Do the Rendering HTML
            parameters = {:course_name => options[:course_name],
                          :module_name => options[:module_name],
                          :image_path => options[:image_path]}
            res = template.render(Object.new, parameters)

            unless Dir.exist? out_dir
              FileUtils.mkdir_p out_dir
            end

            Dir.chdir(out_dir) do
              out_file = File.new(file_name, 'w')
              out_file.puts res
              out_file.puts "\n"

              # Do the Rendering Image
              kit = IMGKit.new(res, quality: 90, width: 950, height: 750)
              kit.to_img(:png)
              kit.to_file(image_name)

              # Convert HTML to Image
              # system ("wkhtmltoimage -f 'png' #{file_name} #{image_name}")
            end

          else
            Hyla.logger.error ">> Unknow rendering"
            exit(1)
        end
      end

      #
      # Return backend directory
      # containing templates (haml, slim)
      #
      def self.backend_dir(backend)
        case backend
          when 'deckjs'
            return [Configuration::backends, 'haml', 'deckjs'] * '/'
          when 'revealjs'
            return [Configuration::backends, 'slim', 'revealjs'] * '/'
          when 'html5'
            return [Configuration::backends, 'slim', 'html5'] * '/'
          else
            return [Configuration::backends, 'slim', 'html5'] * '/'
        end
      end

      #
      # Call Asciidoctor.render function
      #
      def self.asciidoc_to_html(source, destination, extensions, options)

        # Move to Source directory & Retrieve Asciidoctor files to be processed
        source = File.expand_path source
        @destination = File.expand_path destination

        Hyla.logger.info ">>       Source dir: #{source}"
        Hyla.logger.info ">>  Destination dir: #{@destination}"

        # Exit if source directory does not exist
        if !Dir.exist? source
          Hyla.logger.error ">> Source directory does not exist"
          exit(1)
        end

        # Move to source directory
        Dir.chdir(source)
        current_dir = Dir.pwd
        Hyla.logger.info ">>       Current dir: #{current_dir}"

        #
        # Backup Asciidoctor attributes
        # Strange issue discovered
        #
        @attributes_bk = options[:attributes]

        # Delete destination directory (generated_content, ...)
        # FileUtils.rm_rf(Dir.glob(@destination))

        # Search for files using extensions parameter and do the rendering
        adoc_file_paths = []
        Find.find(current_dir) do |f|
          if f =~ /.*\.(?:#{extensions})$/

            path_to_source = Pathname.new(source)
            path_to_adoc_file = Pathname.new(f)
            relative_path = path_to_adoc_file.relative_path_from(path_to_source).to_s
            Hyla.logger.debug ">>       Relative path: #{relative_path}"
            adoc_file_paths << relative_path

            # Get asciidoc file name
            file_name_processed = path_to_adoc_file.basename

            #
            # Create destination dir relative to the path calculated
            #
            html_dir = @destination + '/' + File.dirname(relative_path)
            Hyla.logger.info ">>        Dir of html: #{html_dir}"
            FileUtils.mkdir_p html_dir

            # Copy Fonts
            # TODO : Verify if we still need to do that as the FONTS liberation have been moved
            # TODO : under local lib directory of revealjs
            # self.cp_resources_to_dir(File.dirname(html_dir), 'fonts')

            # Copy Resources for Slideshow
            case options[:backend]
              when 'deckjs'
                # Copy css, js files to destination directory
                self.cp_resources_to_dir(File.dirname(html_dir), 'deck.js')
              when 'revealjs'
                self.cp_resources_to_dir(File.dirname(html_dir), 'revealjs')
            end

            #
            # Check if companion parameter is defined
            # as we have to generate a new AllSlides.txt file
            # containing as tag name this value [snippet]
            #
            if options[:snippet_content] == true
              Hyla.logger.info "Snippet content has been selected. Index file will be modified and modifications will be reverted after asciidoctor processing"
              add_tag_to_index_file(f)
            end

            #
            # Render asciidoc to HTML
            #
            Hyla.logger.info ">> File to be rendered : #{file_name_processed}"

            #
            # Convert asciidoc file name to html file name
            #
            html_file_name = file_name_processed.to_s.gsub(/.adoc$|.ad$|.asciidoc$|.index$|.txt$/, '.html')
            options[:to_dir] = html_dir
            options[:to_file] = html_file_name
            options[:attributes] = @attributes_bk
            Asciidoctor.render_file(f, options)

            #
            # Check if companion parameter is defined
            # as we have to generate a new AllSlides.txt file
            # containing as tag name this value [companion]
            #
            if options[:snippet_content] == true
              remove_tag_from_index_file(f)
            end

          end
        end

        # No asciidoc files retrieved
        if adoc_file_paths.empty?
          Hyla.logger.info "     >>   No asciidoc files retrieved."
          exit(1)
        end

      end

      #
      # CSS Style to be used
      # Default is : asciidoctor.css
      #
      def self.check_style(style)
        if !style.nil?
          css_file = [style, '.css'].join()
        else
          css_file = 'asciidoctor.css'
        end
      end

      #
      # Copy resources to target dir
      def self.cp_resources_to_dir(path, resource)
        source = [Configuration::resources, resource] * '/'
        destination = [path, resource] * '/'
        FileUtils.cp_r source, destination
      end

      #
      # Method parsing TOC File to generate directories and files
      # Each Level 1 entry will become a directory
      # and each Level 2 a file created under the directory
      #
      # @param [File Containing the Table of Content] toc_file
      # @param [Directory where asciidoc files will be generated] out_dir
      # @param [Project name used to create parent of index files] project_name
      #
      def self.table_of_content_to_asciidoc(toc_file, out_dir, project_name)

        Hyla.logger.info '>> Project Name : ' + project_name + ' <<'

        # Open file & parse it
        f = File.open(toc_file, 'r')

        # Expand File Path
        @out_dir = File.expand_path out_dir

        #
        # Create destination directory if it does not exist
        unless Dir.exist? @out_dir
          FileUtils.mkdir_p @out_dir
        end

        # Copy YAML Config file
        FileUtils.cp_r [Configuration::templates, Configuration::YAML_CONFIG_FILE_NAME] * '/', @out_dir

        # Copy styles
        FileUtils.cp_r Configuration::styles, @out_dir

        #
        # Move to 'generated' directory as we will
        # create content relative to this directory
        #
        Dir.chdir @out_dir
        @out_dir = Pathname.pwd

        # Create index file of all index files
        @project_index_file = self.create_index_file(project_name, Configuration::LEVEL_1)


        # File iteration
        f.each do |line|

          #
          # Check level 1
          # Create a directory where its name corresponds to 'Title Level 1' &
          # where we have removed the leading '=' symbol and '.' and
          # replaced ' ' by '_'
          #
          if line[/^=\s/]

            #
            # Create Directory of the module and next the File
            #
            dir_name = remove_special_chars(2, line)
            new_dir = [@out_dir, dir_name].join('/')
            FileUtils.rm_rf new_dir
            FileUtils.mkdir new_dir
            Hyla.logger.info '>> Directory created : ' + new_dir + ' <<'

            Dir.chdir(new_dir)

            # Add image, audio, video directory
            self.create_asset_directory(['image', 'audio', 'video'])

            #
            # Create an index file
            # It is used to include files belonging to a module and will be used for SlideShows
            # The file created contains a title (= Dir Name) and header with attributes
            #
            @index_file = create_index_file(dir_name, Configuration::LEVEL_1)

            # Include index file created to parent index file
            @project_index_file.puts Configuration::INCLUDE_PREFIX + dir_name + '/' + dir_name + Configuration::INDEX_SUFFIX + Configuration::INCLUDE_SUFFIX
            @project_index_file.puts "\n"

            #
            # Generate a Module key value
            # 01, 02, ...
            # that we will use as key to create the asciidoc file
            #
            dir_name = File.basename(Dir.getwd)
            @module_key = dir_name.initial.rjust(2, '0')
            Hyla.logger.info ">> Module key : #@module_key <<"

            #
            # Reset counter value used to generate file number
            # for the file 01, 00
            #
            @index = 0

            # Move to next line record
            next
          end

          #
          # Check Level 2
          # Create a file for each Title Level 2 where name corresponds to title (without '==' symbol) &
          # where we have removed the leading '==' symbol  and '.' and replace ' ' by '_'
          #
          if line[/^==\s/]

            # Close File created previously if it exists
            if !@previous_f.nil?
              @previous_f.close
            end

            #
            # Replace special characters form the file and
            # add the module key followed by the index value for the file
            # Example : m01p01_MyTitle.ad, m01p02_Another_Title.ad
            #
            f_name = remove_special_chars(3, line)
            @index += 1
            #file_index = @index.to_s.initial.rjust(2, '0')
            file_index = sprintf('%02d', @index)
            f_name = 'm' + @module_key + 'p' + file_index + '_' + f_name + '.ad'

            Hyla.logger.info '   # File created : ' + f_name.to_s

            #
            # Create File and add configuration HEADER
            #
            @new_f = File.new(f_name, 'w')
            @new_f.puts Configuration::HEADER
            @new_f.puts "\n"

            @previous_f = @new_f

            # Include file to index
            @index_file.puts Configuration::INCLUDE_PREFIX + f_name + Configuration::INCLUDE_SUFFIX
            @index_file.puts "\n"
          end

          #
          # Add Content to file if it exists and line does not start with characters to be skipped
          #
          if !@new_f.nil? and !line.start_with?(Configuration::SKIP_CHARACTERS)
            @new_f.puts line
          end

        end

      end

=begin
      #
      # Generate PDF
      #
      def self.html_to_pdf(source, destination, html_file_name)
        file_path = [source, html_file_name] * '/'
        html_file = File.new(file_path)
        kit = PDFKit.new(html_file,
                         :page_size => 'A4',
                         :toc => true,
                         :page_offset => 1,
                         :footer_center => 'Page [page]')

        # Create destination directory if it does not exist
        unless File.directory?(destination)
          FileUtils.mkdir_p(destination)
        end

        # Save PDF to a file
        pdf_file_name = [destination, html_file_name.sub(/html|htm/, 'pdf')] * '/'
        kit.to_file(pdf_file_name)
        Hyla.logger.info ">> PDF file generated and saved : #{pdf_file_name} "
      end
=end

      def self.html_to_pdf(file_name, source, destination, footer_text, header_html_path, cover_path)

        @cover_path = cover_path
        destination= File.expand_path destination
        pdf_file = [destination, "result.pdf"] * '/'
        wkhtml_cmd = "wkhtmltopdf"
        size = 'A4'

        # pdf_file_name = [destination, html_file_name.sub(/html|htm/, 'pdf')] * '/'

        list_of_files = ""

        unless File.directory?(destination)
          FileUtils.mkdir_p(destination)
        end

        if file_name.nil? || file_name.empty?
          filter = [source] * '/' + "*.html"
          files = Dir[filter]

          files.each do |file|
            file_name = File.basename file
            next if file_name.downcase.include?('assessments')
            next if file_name.downcase.include?('labinstructions')
            next if file_name.downcase.include?('title')
            next if file_name.downcase.include?('cover')
            file = File.expand_path file
            list_of_files = list_of_files + " " + file
          end
        else
          #
          # If the file passed as parameter has extension name equal to txt, then we will extract the file names
          # whenever we have a include:: directive in the file
          #
          extension_name = File.extname file_name

          case extension_name
            when '.txt'
              file_to_processed = [File.expand_path(Dir.getwd), file_name] * '/'
              result = self.extract_file_names(file_to_processed, source)

              result.each do | file_path |
                if file_path.downcase.include?('title') || file_path.downcase.include?('cover')
                  @cover_path = file_path
                  next
                end
                list_of_files = list_of_files + " " + file_path
              end

            else
              list_of_files = [File.expand_path(source), file_name] * '/'
          end
        end

        wkhtml_cmd.concat " #{list_of_files} #{pdf_file}"
        wkhtml_cmd.concat " --margin-top '18mm' --header-html '#{header_html_path}'" if header_html_path
        wkhtml_cmd.concat " --margin-bottom '10mm'  --footer-center '#{footer_text}'" if footer_text
        wkhtml_cmd.concat " --cover '#@cover_path'" if @cover_path
        wkhtml_cmd.concat " --page-size #{size}"
        Hyla.logger.debug "c #{wkhtml_cmd}"

        Dir.chdir(source) do
          system "#{wkhtml_cmd}"
        end
      end

      #
      # Create Asset Directory
      #
      def self.create_asset_directory(assets = [])
        assets.each do |asset|
          Dir.mkdir(asset) if !Dir.exist? asset
        end
      end

      #
      # Remove space, dot from a String
      #
      def self.remove_special_chars(pos, text)
        return text[pos, text.length].strip.gsub(/\s/, '_')
        .gsub('.', '')
        .gsub('&', '')
      end

      #
      # Add '/' at the end of the target path
      # if the target path provided doesn't contain it
      #
      def self.check_slash_end(out_dir)
        last_char = out_dir.to_s[-1, 1]
        Hyla.logger.info '>> Last char : ' + last_char
        if !last_char.equal? '/\//'
          temp_dir = out_dir.to_s
          out_dir = temp_dir + '/'
        end
        out_dir
      end

      #
      # Create ascidoc index file
      # containing references to asciidoc files part of a module
      #
      def self.create_index_file(file_name, level)
        n_file_name = file_name + Configuration::INDEX_SUFFIX
        index_file = File.new(n_file_name, 'w')

        index_file.puts Configuration::HEADER_INDEX
        index_file.puts "\n"
        # TODO - until now we cannot use level 0 for parent/children files
        # even if doctype: book
        # This is why the level for each index file title is '=='
        index_file.puts '== ' + file_name
        index_file.puts "\n"

        index_file
      end

      #
      # Add snippet tag index file
      #
      def self.add_tag_to_index_file(index_file)
        text = File.read(index_file)
        replace = text.gsub!('[]','[tag=' + Configuration::SNIPPET_TAG + ']')
        File.open(index_file, "w") { |file| file.puts replace }
      end

      #
      # Remove snippet tag from index file
      #
      def self.remove_tag_from_index_file(index_file)
        text = File.read(index_file)
        replace = text.gsub!('[tag=' + Configuration::SNIPPET_TAG + ']', '[]')
        File.open(index_file, "w") { |file| file.puts replace }
      end

      #
      # Check mandatory options
      #
      def self.check_mandatory_option?(key, value)
        if value.nil? or value.empty?
          Hyla.logger.warn "Mandatory option missing: #{key}"
          exit(1)
        else
          true
        end
      end

      #
      # Extract files names from a file containing include:: directive
      #
      def self.extract_file_names(file_name, destination)

        result = []
        f = File.open(file_name, 'r')
        matches = f.grep(Configuration::IncludeDirectiveRx)

        if matches

          matches.each do |record|
            # Extract string after include::
            matchdata = record.match(/^include::(.+)/)

            if matchdata

              data = matchdata[1]
              # Remove []
              name = data.to_s.gsub(/[\[\]]/, '').strip

              # Rename file to .html
              name = name.gsub(/ad$/, 'html')
              file_name = [destination, name] * '/'
              file_path = File.expand_path file_name
              result << file_path
            end
          end
        end
        f.close
        return result
      end

    end # class
  end # module Commands
end # module Hyla