#!/usr/bin/ruby -w
# Encoding: UTF-8
# frozen_string_literal: true
# =========================================================================== #
# === Colours::ReplaceTokensWithColourCode
#
# This class can take a String, such as "<one>foo</one>bar" and turn
# it into another String containing a variant that has ANSI colour
# codes embedded.
#
# The input for this class should be the String that contains HTML-like
# numbers, such as <one> or <two>.
#
# The class has to remain flexible, as downstream code may want to
# customize it. One way to customize it is to pass the default
# colour to this method, as second argument.
#
# The default colour is (currently, in Oct 2020) grey.
# 
# Usage example:
#
#   Colours::ReplaceTokensWithColourCode.new(ARGV)
#
# =========================================================================== #
# require 'colours/replace_tokens_with_colour_code/replace_tokens_with_colour_code.rb'
# Colours::ReplaceTokensWithColourCode.new(ARGV)
# =========================================================================== #
require 'colours/base/base.rb'

module Colours

class ReplaceTokensWithColourCode < Base # === Colours::ReplaceTokensWithColourCode

  require 'colours/autogenerated/html_colours_methods.rb'

  # ========================================================================= #
  # === ARRAY_ALLOWED_HTML_COLOURS
  # ========================================================================= #
  ARRAY_ALLOWED_HTML_COLOURS = ::Colours.array_html_colours?.map {|entry|
    entry.to_sym
  }

  # ========================================================================= #
  # === ARRAY_ALLOWED_NUMBERED_WORDS
  # ========================================================================= #
  ARRAY_ALLOWED_NUMBERED_WORDS = %w(
    one
    two
    three
    four
    five
  )

  # ========================================================================= #
  # === HASH_DEFAULT_REPLACEMENT_COLOURS
  # ========================================================================= #
  HASH_DEFAULT_REPLACEMENT_COLOURS = {
    one:            :teal,           # This could also be :steelblue.
    two:            :lightseagreen,  # Or :dodgerblue.
    three:          :mediumseagreen, # ← Used to be 'peru' up until 23.11.2019.
    four:           :mediumorchid,   # ← Used to be 'olivedrab' up until 06.12.2022.
    five:           :lightgreen,     # Or :olivedrab.
    rev:            ::Colours.rev,     # Or  USE_THIS_COLOUR_FOR_THE_DEFAULT_COLOUR.
    default_colour: ::Colours.rev      # This is actually the same as :rev. Could also be USE_THIS_COLOUR_FOR_THE_DEFAULT_COLOUR.
  }

  # ========================================================================= #
  # === initialize
  # ========================================================================= #
  def initialize(
      commandline_arguments = nil,
      run_already           = true,
      &block
    )
    reset
    set_commandline_arguments(
      commandline_arguments
    )
    # ======================================================================= #
    # === Handle blocks next
    # ======================================================================= #
    if block_given?
      yielded = yield
      # ===================================================================== #
      # === Handle Hashes next
      # ===================================================================== #
      if yielded.is_a? Hash
        do_update_the_main_hash_with_this_dataset(yielded)
      end
    end
    case run_already
    # ======================================================================= #
    # === :do_not_run_yet
    # ======================================================================= #
    when :do_not_run_yet
      run_already = false
    end
    run if run_already
  end

  # ========================================================================= #
  # === reset                                                     (reset tag)
  # ========================================================================= #
  def reset
    # ======================================================================= #
    # === @hash_replacement_colours
    #
    # We need to specify a default replacement-Hash. The user can override
    # it, or individual elements.
    # ======================================================================= #
    @hash_replacement_colours = HASH_DEFAULT_REPLACEMENT_COLOURS
    # ======================================================================= #
    # === @sanitized_line
    # ======================================================================= #
    @sanitized_line = nil
    # ======================================================================= #
    # === @main_regex
    # ======================================================================= #
    @main_regex = main_regex?
  end

  # ========================================================================= #
  # === rev?
  # ========================================================================= #
  def rev?
    if @hash_replacement_colours.has_key?(:rev)
      return @hash_replacement_colours[:rev]
    else
      ::Colours.rev
    end
  end

  # ========================================================================= #
  # === is_this_a_HTML_colour?
  # ========================================================================= #
  def is_this_a_HTML_colour?(i)
    ARRAY_ALLOWED_HTML_COLOURS.include?(i)
  end

  # ========================================================================= #
  # === sanitized_line?
  # ========================================================================= #
  def sanitized_line?
    @sanitized_line
  end; alias result? sanitized_line? # === result?

  # ========================================================================= #
  # === main_regex?
  # ========================================================================= #
  def main_regex?
    REGEX_FOR_HTML_COLOURS
  end

  # ========================================================================= #
  # === do_update_the_main_hash_with_this_dataset
  # ========================================================================= #
  def do_update_the_main_hash_with_this_dataset(i)
    @hash_replacement_colours.update(i)
  end

  # ========================================================================= #
  # === hash_replacement_colours?
  # ========================================================================= #
  def hash_replacement_colours?
    @hash_replacement_colours
  end; alias main_hash? hash_replacement_colours? # == main_hash?

  # ========================================================================= #
  # === set_hash_replacement_colours
  # ========================================================================= #
  def set_hash_replacement_colours(i)
    @hash_replacement_colours = i
  end; alias set_use_this_hash set_hash_replacement_colours # === set_use_this_hash

  # ========================================================================= #
  # === parse_this_complex_line                                   (parse tag)
  #
  # This is the method that will parse a complex line. A complex line is
  # a line that may include e. g. <one> as well as <royalblue> and
  # similar such entities.
  #
  # This method will always assign towards @sanitized_line whenever it
  # is called.
  # ========================================================================= #
  def parse_this_complex_line(
      i,
      use_this_as_replacement_hash = :default, # This is, as the name suggests, the replacement Hash.
      rev                          = nil       # This then defaults to ::Colours.revert
    )
    rev = ::Colours.rev if rev.nil? # Add a small safeguard here.
    if i and does_this_string_contain_a_special_token?(i)
      use_this_colour_for_the_default_colour = rev.to_sym # Default.
      # ===================================================================== #
      # === The replacement Hash
      #
      # Specify our "replacement-hash", that is colours used for e. g.
      # <one>, <two> or <three> or HTML colours such as <royalblue>
      # and so forth.
      #
      # This clause only matters if we have a special token in the given
      # String.
      # ===================================================================== #
      case use_this_as_replacement_hash
      # ===================================================================== #
      # === :default
      #
      # The default entry-point.
      # ===================================================================== #
      when :default,
           :default_hash
        # =================================================================== #
        # The following hash can be used as a default "replacement" Hash.
        # =================================================================== #
        use_this_as_replacement_hash = @hash_replacement_colours
      end
      # ===================================================================== #
      # Next we can finally replace the found keyword. We have to make a
      # decision here, though - if it is part of the Array containing
      # all HTML colours, then we'll replace it with the HTML colour at
      # hand; otherwise we'll use the replacement Hash as-is:
      # ===================================================================== #
      scanned_result = i.scan(
        @main_regex
      ).flatten
      # ===================================================================== #
      # We then know that this line contains either a HTML colour or a
      # numbered word, such as one, two, three, four or five.
      # ===================================================================== #
      _ = use_this_as_replacement_hash # Our replacement Hash.
      rev = _[:rev] if _.is_a?(Hash) and _.has_key?(:rev)
      scanned_result.each {|this_numbered_word|
        # =================================================================== #
        # Since as of December 2021 we only accept Symbols here, hence
        # the .to_sym call next.
        # =================================================================== #
        this_numbered_word = this_numbered_word.to_sym
        i = i.dup if i.frozen?
        if is_this_a_HTML_colour?(this_numbered_word)
          use_this_regex =
            # =============================================================== #
            # We will simply re-use the regex-generator near the top of this
            # file here.
            # =============================================================== #
            ::Colours.return_main_regex_to_use_for_obtaining_the_full_content(this_numbered_word)
          # ================================================================= #
          # We must use .gsub!() because the colour-string may occur more
          # than once in the given String.
          # ================================================================= #
          begin
          if use_this_colour_for_the_default_colour == :"\e[0;37m" # Ad-hoc fix.
            i.gsub!(
              use_this_regex,
              ::Colours::HtmlColoursMethods.send(this_numbered_word.to_sym, "\\1") { :omit_end }+
              use_this_colour_for_the_default_colour.to_s
            )
          else
            i.gsub!(
              use_this_regex,
              ::Colours::HtmlColoursMethods.send(this_numbered_word.to_sym, "\\1") { :omit_end }+
              ::Colours::HtmlColoursMethods.send(use_this_colour_for_the_default_colour,'') { :omit_end }
            )
          end
          rescue NoMethodError => exception
            pp 'use_this_colour_for_the_default_colour was:'
            pp use_this_colour_for_the_default_colour
            pp exception
          end
        elsif _.has_key?(this_numbered_word) # This is for a numbered word, such as <one>.
          # ================================================================= #
          # === :default_colour
          # ================================================================= #
          if use_this_as_replacement_hash.has_key? :default_colour
            use_this_colour_for_the_default_colour = use_this_as_replacement_hash[:default_colour]
          end
          if use_this_as_replacement_hash.has_key? :rev
            use_this_colour_for_the_default_colour = use_this_as_replacement_hash[:rev]
          end
          replacement_colour = _[this_numbered_word] # This will become the new main colour.
          use_this_regex =
            # =============================================================== #
            # We will simply re-use the regex-generator near the top of this
            # file here.
            # =============================================================== #
            ::Colours.return_main_regex_to_use_for_obtaining_the_full_content(this_numbered_word)
          # ================================================================= #
          # We must use .gsub!() because the colour-string may occur more
          # than once in the given String.
          # ================================================================= #
          i.gsub!(
            use_this_regex,
            ::Colours.colourize(replacement_colour.to_sym, "\\1")+
            ::Colours::HtmlColoursMethods.send(use_this_colour_for_the_default_colour,'') { :omit_end }
          )
        end
      }
    # else # else we do not have to do anything.
    end
    @sanitized_line = i.dup # And assign it here.
    return @sanitized_line # Always return it here.
  end; alias parse parse_this_complex_line # === parse

  # ========================================================================= #
  # === run                                                         (run tag)
  # ========================================================================= #
  def run
    second_argument = second_argument?
    if second_argument and second_argument.is_a?(Hash)
      set_hash_replacement_colours(second_argument)
    end
    _ = first_argument?
    if _ and !_.nil?
      parse(_)
    end
  end

  # ========================================================================= #
  # === does_this_string_contain_a_special_token?
  #
  # A special token is either a HTML colour, or something like
  # <one></one>.
  # ========================================================================= #
  def does_this_string_contain_a_special_token?(i)
    scanned_result = i.scan(
      @main_regex
    ).flatten
    does_it_include_a_HTML_colour = scanned_result.any? {|entry|
      ARRAY_ALLOWED_HTML_COLOURS.include?(entry.to_sym)
    }
    does_it_include_a_numbered_word = scanned_result.any? {|entry|
      ARRAY_ALLOWED_NUMBERED_WORDS.include?(entry)
    }
    return (does_it_include_a_HTML_colour or does_it_include_a_numbered_word) 
  end

  # ========================================================================= #
  # === Colours::ReplaceTokensWithColourCode[]
  # ========================================================================= #
  def self.[](i = ARGV)
    new(i)
  end

end

# =========================================================================== #
# === Colours.does_this_string_contain_a_special_token?
# =========================================================================== #
def self.does_this_string_contain_a_special_token?(i, &block)
  _ = ::Colours::ReplaceTokensWithColourCode.new(i, :do_not_run_yet, &block)
  _.does_this_string_contain_a_special_token?(i)
end; self.instance_eval { alias is_this_replacement_worthy? does_this_string_contain_a_special_token? } # === Colours.is_this_replacement_worthy? 

# =========================================================================== #
# === Colours.replace_html_like_tokens
#
# The second argument to this method allows the user to use another Hash
# for the replacement table.
#
# Usage examples:
#
#   x = Colours.replace_number_words_with_the_corresponding_html_colour("- <teal>Asbestbedingte Erkrankungen</teal> haben eine <two>Latenzzeit</two> von etwa n Jahren? Etwa <steelblue>30 Jahren</steelblue>.")
#   x = "<one>Methämoglobin</one> ist <two>ungeeignet</two> für <three>den</three> Sauerstofftransport; die <two>roten Blutkörperchen</two>. Welches Enzymsystem ist dies?"
#   y = Colours.replace_number_words_with_the_corresponding_html_colour(x)
#
# See also:
#
#   https://rubular.com/r/XUPQJFKlDs2OYP
#
# =========================================================================== #
def self.replace_html_like_tokens(
    i,
    optional_use_this_hash = nil,
    &block
  )
  _ = ::Colours::ReplaceTokensWithColourCode.new(i, :do_not_run_yet, &block)
  if optional_use_this_hash
    _.set_use_this_hash(optional_use_this_hash)
  end
  _.run
  _.result?
end; self.instance_eval { alias replace_number_words_with_the_corresponding_html_colour replace_html_like_tokens } # === Colours.replace_number_words_with_the_corresponding_html_colour

end

if __FILE__ == $PROGRAM_NAME
  alias e puts
  _ = Colours::ReplaceTokensWithColourCode.new(ARGV, :do_not_run_yet)
  e _.parse(
    "<royalblue>This</royalblue> <tomato>is</tomato> <cyan>test</cyan>."
  )
  e _.parse(
    "<royalblue>Test1</royalblue> <one>test2</one> <two>test3</two> <three>test4</three>"
  )
  e _.parse(
    "<yodel>foobar</yodel>"
  )
  e _.parse(
    "<one>abc</one> hey there"
  )
  _.parse(
    "<one>abc</one> hey there
     <one>how do you do?</one>
     <two>lots of things we should do</two>.
     <royalblue>foobar</royalblue>"
  )
  e _.result?
  _.parse('The system (<royalblue>of awesomeness</royalblue>). 
  is coming closer.')
  e _.result?
end # replacetokenswithcolourcode