#!/usr/bin/ruby -w
# Encoding: UTF-8
# frozen_string_literal: true
# =========================================================================== #
# === Colours
# =========================================================================== #
# require 'colours/toplevel_methods/toplevel_methods.rb'
# Colours.return_a_unique_array_containing_all_available_colours
# =========================================================================== #
module Colours

  require 'colours/requires/require_yaml.rb'
  require 'colours/constants/constants.rb'
  require 'colours/toplevel_methods/e.rb'
  require 'colours/toplevel_methods/rev.rb'
  require 'colours/eparse/eparse.rb'
  require 'colours/version/version.rb'

  # ========================================================================= #
  # === @prefer_this_colour_schemata
  #
  # The following Array ranks which colour schemata we will try to use.
  # More important entries should come on top.
  #
  # Konsole will be the default, normally, followed by the 256 colours; and
  # then the basic colours.
  #
  # Of course you can decide on your own via the .yml file, and other
  # methods that allow us to change this setting, but when it comes
  # to implementations such as for simp() or sfancy() then we will
  # honour the setting in the yaml file (if we use any colours at all,
  # that is). The yaml file is typically called
  # "prefer_this_colour_schemata.yml".
  # ========================================================================= #
  if File.exist? FILE_PREFER_THIS_COLOUR_SCHEMATA
    @prefer_this_colour_schemata = YAML.load_file(FILE_PREFER_THIS_COLOUR_SCHEMATA)
  end

  # ========================================================================= #
  # === Colours.prefer_which_colour_scheme?
  # ========================================================================= #
  def self.prefer_which_colour_scheme?
    @prefer_this_colour_schemata.first.to_sym
  end; self.instance_eval { alias prefer_which_colour_schemata? prefer_which_colour_scheme? } # === Colours.prefer_which_colour_schemata?

  # ========================================================================= #
  # === @use_colours
  #
  # Whether we will use colours or whether we will not. This can be
  # used to globally disable colours in every project using the
  # Colours gem.
  # ========================================================================= #
  @use_colours = true

  # ========================================================================= #
  # === Colours.use_colours?
  #
  # Use this method to find out whether we wish to use colours or
  # whether we do not.
  # ========================================================================= #
  def self.use_colours?
    @use_colours
  end

  # ========================================================================= #
  # === use_colours?
  #
  # Delegate towards the class-method here. Unsure whether this method
  # is really needed, but let's keep it for now. After all we can
  # use "include Colours" to add that method.
  # ========================================================================= #
  def use_colours?
    ::Colours.use_colours?
  end

  # ========================================================================= #
  # === @last_colour_used
  #
  # This instance variable keeps track what the last colour used was.
  # ========================================================================= #
  @last_colour_used = nil

  # ========================================================================= #
  # === last_colour_used?
  #
  # Tell us which colour was used last.
  # ========================================================================= #
  def last_colour_used?
    @last_colour_used.to_s # Always required a string.
  end

  # ========================================================================= #
  # === Colours.set_last_colour_used
  # ========================================================================= #
  def self.set_last_colour_used(i)
    @last_colour_used = i
  end

  # ========================================================================= #
  # === Colours.esystem
  # ========================================================================= #
  def self.esystem(i)
    e i
    system i
  end

  # ========================================================================= #
  # === Colours.read_and_display_this_file
  #
  # This method accepts a file path - in other words, the input should be
  # the location of a specific file on your filesystem - and then simply
  # reads in the content of said file via File.readlines().
  #
  # Content such as <slateblue> found in this file will be replaced
  # with the respective R,G,B substring value.
  #
  # In the test/ subdirectory there is an example file for this - have
  # a look there for more information.
  # ========================================================================= #
  def self.read_and_display_this_file(
      i = TEST_FILE
    )
    i = i.to_s
    if File.exist? i
      new_array = []
      data = File.readlines(i)
      data.each {|entry|
        # ======================================================================= #
        # Check whether the entry has a <> tag:
        # ======================================================================= #
        if entry.include?('<') and entry.include?('>') and 
           entry.include?('</>')
          # ================================================================= #
          # Ok, we may assume that something like <slateblue> is there.
          # ================================================================= #
          entry = sanitize_line(entry)
        end
        new_array << entry
      }
      e new_array # For now we will simply output that modified Array.
    else
      e "Notice: The file at `#{sfile(i)}` does not exist."
    end
  end; self.instance_eval { alias read_file read_and_display_this_file } # === Colours.read_file

  # ========================================================================= #
  # === Colours.return_italic
  #
  # To test this method, try:
  #
  #   Colours.italic('Hello world!')
  #
  # ========================================================================= #
  def self.return_italic(
      show_this_text = 'This must be italic.', 
      make_newline   = false
    )
    result = "\x1b".dup
    result << '['
    result << ITALIC_CODE
    if block_given?
      # ===================================================================== #
      # Right now we assume that this must be a colour.
      # ===================================================================== #
      result << ";38;2;"
      result << html_colour_to_stringified_rgb_values(yield)
    end
    result << 'm'
    if make_newline
      show_this_text = "#{show_this_text}\n"
    end
    result << show_this_text
    result << REVERT
    result
  end; self.instance_eval { alias string_italic return_italic } # === Colours.string_italic

  # ========================================================================= #
  # === Colours.make_colour
  #
  # This is used primarily for testing.
  # ========================================================================= #
  def self.make_colour(
      what_colour_to_use
    )
    i = what_colour_to_use # Copy.
    constant = i.to_s.upcase
    name_of_colour = const_get(constant)
    string = '%-34s' % ("#{name_of_colour} Test with #{constant}.")
    string << IS_A_TEST+'('+name_of_colour.inspect.delete('"')+')'
    e(string)
  end

  # ========================================================================= #
  # === Colours.disable_colours
  #
  # This method can be used to disable the colours on the Colours
  # namespace.
  # ========================================================================= #
  def self.disable_colours(
      be_verbose = false
    )
    puts 'Disabling colours next.' if be_verbose
    @use_colours = false
  end; self.instance_eval { alias disable disable_colours } # === Colours.disable

  # ========================================================================= #
  # === Colours.html_colourize
  #
  # This method will simply return the colour-code + text.
  #
  # The third argument, called `append_revert`, can be used to determine
  # whether we will append the revert code to the generated output String
  # or whether we will not. By default we will, but there are examples
  # where we may wish to assemble our own colour string, and in these
  # cases we do not yet know which text is to be shown - hence, this
  # must become an optional argument.
  #
  # A block can be passed to this method. If the block has, as content,
  # the Symbol :omit_end then the end-part of the ANSI code will not
  # be used.
  #
  # Usage examples:
  #
  #   x = Colours.colourize('slateblue', 'Hello world!'); pp x
  #   y = Colours.colourize('slateblue', 'Hello world!') { :omit_end }; pp y
  #
  # ========================================================================= #
  def self.html_colourize(
      colour_to_use = return_random_html_colour,
      this_text     = nil,
      append_revert = true,
      &block
    )
    require 'colours/html_colours/html_colours.rb'
    require 'colours/rgb/rgb.rb'
    if block_given?
      yielded = yield
      case yielded
      # ===================================================================== #
      # === :omit_end
      # ===================================================================== #
      when :omit_end
        append_revert = false
      end
    end
    if this_text.nil?
      this_text = colour_to_use.to_s.dup
    end
    if append_revert
      "#{rgb_value_as_escape_code_string(colour_to_use)}#{this_text}#{revert}"
    else
      "#{rgb_value_as_escape_code_string(colour_to_use)}#{this_text}"
    end
  end; self.instance_eval { alias colourize       html_colourize } # === Colours.colourize
       self.instance_eval { alias kde_colour      html_colourize } # === Colours.kde_colour
       self.instance_eval { alias konsole_colours html_colourize } # === Colours.konsole_colours

  # ========================================================================= #
  # === Colours.enable_colours
  #
  # Use this method to enable colours for the whole Colours namespace.
  # ========================================================================= #
  def self.enable_colours(
      be_verbose = false
    )
    puts 'Enabling colours next.' if be_verbose
    @use_colours = true # Defined below.
  end; self.instance_eval { alias enable enable_colours } # === Colours.enable

  # ========================================================================= #
  # === Colours.show_help                                          (help tag)
  # ========================================================================= #
  def self.show_help
    e
    e 'The commandline-interface for the Colours project supports '\
      'the following documented instructions:'
    e
    eparse '  file?               # read in from the default test file'
    eparse '  html_colours        # show all html colours'
    eparse '  open                # open this file here in your editor'
    eparse '  --version?          # report the current version of '\
           'the colours gem'
    eparse '  --base-dir?         # show the project base directory '\
           'of the Colours namespace'
    eparse '  --show-html-colours # show the available HTML colours'
    e
  end; self.instance_eval { alias help show_help } # === Colours.help

  # ========================================================================= #
  # === underline
  # ========================================================================= #
  def underline(
      i            = '',
      make_newline = false
    )
    Colours.underline(i, make_newline)
  end

  # ========================================================================= #
  # === Colours.return_main_regex_to_use_for_obtaining_the_full_content
  #
  # The first regex that is commented out, in the body of this method,
  # was in use until May 2023. It was then replaced with the new
  # regex, which also has an associated test-case, in the test/
  # subdirectory of the colours gem.
  #
  # For the current regex see here:
  #
  #   https://rubular.com/r/1dpSmYqQ9SVvMt
  #
  # I was also using a second regex that was slightly incompatible.
  # For legacy reasons it can be seen here: https://rubular.com/r/sDNQd81MtciMJL
  # ========================================================================= #
  def self.return_main_regex_to_use_for_obtaining_the_full_content(this_colour)
    # /<#{this_colour}>([\-\{\}\[\]\\\(\)\/a-zA-Z0-9’éúÄäÖöÜüÅαβß&%+−=≡πμ°₁₂₃₄₅₆₇₈₉⁰¹²³⁴⁵⁶⁷⁸⁹⁻!#\*~:;∨"',_\|\n\. ]+)<\/#{this_colour}>/
    # /<#{this_numbered_word}>([-#~≥!\e=><;,→⁻⁺@^„“γαµ_₀₁₂₃₄₅₆₇₈₉²³⁴⁵⁶⁷⁸⁹äöüÄÖÜβß%&:≡°$A-Za-z0-9\n \?\\'\|\(\)\{\}\[\]\"\*\.\+]+)<\/#{this_numbered_word}>/
    /<#{this_colour}>([\n₀₁₂₃₄₅₆₇₈₉⁰¹²³⁴⁵⁶⁷⁸⁹°→⁻⁺≥$@σπµμγ∨^αβßÅÄäéúÖöÜüa-zA-Z0-9’'"„“~#&%!,;:_+−=≡<>\*\\\/\.\{\}\(\)\|\[\]\?\- ]*?)<\/#{this_colour}>/
  end

  # ========================================================================= #
  # === Colours.show_the_version
  #
  # Invocation example:
  #
  #   colours --version?
  #
  # ========================================================================= #
  def self.show_the_version
    e steelblue(VERSION)
  end

  # ========================================================================= #
  # === restore?
  #
  # This will restore to the default again.
  # ========================================================================= #
  def restore?
    Colours.restore?
  end; alias revert restore? # === revert
       alias rev?   restore? # === rev?

  # ========================================================================= #
  # === Colours.report_whether_colours_will_be_used
  # ========================================================================= #
  def self.report_whether_colours_will_be_used
    if @use_colours
      puts 'Yes, colours will be used.'
    else
      puts 'Colours are currently disabled.'
    end
  end

  # ========================================================================= #
  # === Colours.use_colours=
  #
  # This setter-method determines whether the colours gem will use
  # colours or whether it will not.
  #
  # The first input argument given to this method should be a Boolean
  # value, such as true or false.
  # ========================================================================= #
  def self.use_colours=(
      new_value = true
    )
    @use_colours = new_value
  end; self.instance_eval { alias set_use_colours use_colours= } # === Colours.set_use_colours

  # ========================================================================= #
  # === Colours.replace_html_colours_in_this_string
  #
  # This method will replace all HTML colours in a given string, such as 
  # "<slateblue>test</slateblue>", with the corresponding RGB colour 
  # variant for the commandline.
  #
  # Typically this refers to a terminal such as the KDE Konsole, and a
  # shell such as bash (although other shells are fine too, and many
  # other terminals, such as the gnome-terminal, most likely will work
  # fine as well - but it is optimized for the KDE Konsole). 
  #
  # This method should only be called after a prior check was done,
  # to determine whether the given input String at hand does indeed
  # include a valid HTML colour; otherwise it would be fairly pointless
  # to invoke this method, if it is already known that the String at
  # hand does not contain any HTML colour at all. In order to
  # determine whether a String may include a valid HTML colour,
  # the method called line_contains_a_valid_html_colour?() can be
  # used.
  #
  # Usage example:
  #
  #   Colours.replace_html_colours_in_this_string
  #   Colours.replace_html_colours_in_this_string('- The <one>UID</one> of <royalblue>the</royalblue> user called <two>root</two> is ... ? <one>0</one>.', :lightgreen)
  #
  # ========================================================================= #
  def self.replace_html_colours_in_this_string(
      i,
      use_this_colour_for_the_default_colour = :default, # ← This specifies the default colour.
      shall_we_revert_at_the_end_of_the_line = true
    )
    i = i.dup # We want to work on a copy.
    result = ''.dup # Our result-string.
    case use_this_colour_for_the_default_colour
    # ======================================================================= #
    # === :default
    # ======================================================================= #
    when :default,
         :default_colour
      use_this_colour_for_the_default_colour = USE_THIS_COLOUR_FOR_THE_DEFAULT_COLOUR
    end
    case shall_we_revert_at_the_end_of_the_line
    # ======================================================================= #
    # === :revert
    # ======================================================================= #
    when :revert
      shall_we_revert_at_the_end_of_the_line = true
    end
    result << ::Colours::HtmlColoursMethods.send(use_this_colour_for_the_default_colour) { :omit_end }
    # result = result.dup
    # ======================================================================= #
    # This method will make use of two different regexes.
    # ======================================================================= #
    scanned_results = i.scan(
      REGEX_FOR_HTML_COLOURS
    ).flatten.uniq
    add_on_string = i.dup
    # ======================================================================= #
    # scanned_results may be an Array such as ['steelblue', 'tomato']
    # ======================================================================= #
    scanned_results.each {|this_colour|
      if is_this_a_valid_html_colour?(this_colour)
        # =================================================================== #
        # We must use .gsub!() because the colour-string may occur more 
        # than once. Unfortunately for longer Strings this becomes a bit
        # fragile.
        # =================================================================== #
        part1 = ::Colours::HtmlColoursMethods.send(this_colour.to_sym, "\\1") { :omit_end }
        part2 = ::Colours::HtmlColoursMethods.send(use_this_colour_for_the_default_colour) { :omit_end }
        add_on_string.gsub!(
          ::Colours.return_main_regex_to_use_for_obtaining_the_full_content(this_colour),
          part1+part2
        ).dup
      end
    }
    result << add_on_string
    if shall_we_revert_at_the_end_of_the_line
      result << ::Colours.revert
    end
    return result
  end; self.instance_eval { alias replace_html_colours                      replace_html_colours_in_this_string } # === Colours.replace_html_colours
       self.instance_eval { alias replace_all_raw_html_colours_in_this_line replace_html_colours_in_this_string } # === Colours.replace_all_raw_html_colours_in_this_line
       self.instance_eval { alias replace_all_html_colours_in_this_line     replace_html_colours_in_this_string } # === Colours.replace_all_html_colours_in_this_line
       self.instance_eval { alias parse_html_colour                         replace_html_colours_in_this_string } # === Colours.parse_html_colour

  # ========================================================================= #
  # === Colours.underline
  #
  # This method will "puts" the result of applying underline to a string,
  # on the console/terminal. If you wish to do the output on your own
  # then you have to use the method Colours.return_underline or its
  # alias called Colours.string_underline.
  #
  # To test this, try:
  #
  #   Colours.underline('Hello world!')
  #
  # ========================================================================= #
  def self.underline(
      i            = '', 
      make_newline = false,
      &block
    )
    e return_underline(i, make_newline, &block)
  end

  # ========================================================================= #
  # === Colours.colour_method_or_display_the_content_of_the_file_or_use_via_pipe
  #
  # This method has an awful name, but its core use case is simple.
  #
  # It will handle commandline instructions such as:
  #
  #   orange Hey there
  #   slateblue how are you doing?
  #
  # Usage example from within ruby:
  #
  #   Colours.testing123('abc', :orange)
  #
  # ========================================================================= #
  def self.colour_method_or_display_the_content_of_the_file_or_use_via_pipe(
      i                  = ARGF,
      name_of_the_colour = $PROGRAM_NAME
    )
    require 'colours/autogenerated/html_colours_methods.rb'
    # ======================================================================= #
    # === :read
    # ======================================================================= #
    if i.respond_to? :read
      i = i.read
    elsif i and File.exist?(i)
      i = File.read(i)
    end
    i = Colours.remove_trailing_end_from(i)
    if i.is_a? Array
      i = i.join(' ').strip
    end
    if name_of_the_colour
      if name_of_the_colour.respond_to?(:include?) and
         name_of_the_colour.include?('/')
        name_of_the_colour = File.basename(name_of_the_colour)
      end
    end
    name_of_the_colour = name_of_the_colour.to_sym
    return ::Colours::HtmlColoursMethods.send(name_of_the_colour, i)
  end

  # ========================================================================= #
  # === Colours.generate_shell_file_containing_the_html_colours
  #
  # This method will generate a shell file into the current directory
  # by default.
  #
  # The code in that file will generate a shell script that holds all
  # the HTML-colours, via UPCASED names.
  #
  # These entries will then look like this:
  #
  #   SADDLEBROWN:  "\e[38;2;139;69;19m"
  #   PERU:         "\e[38;2;205;133;63m"
  #   CHOCOLATE:    "\e[38;2;210;105;30m"
  #
  # This can also be generated from the commandline, thanks to the
  # bin/colours executable, via:
  #
  #   colours --generate_shell_file_containing_the_html_colours
  #
  # ========================================================================= #
  def self.generate_shell_file_containing_the_html_colours(
      generate_the_shell_file_into_this_directory = Dir.pwd
    )
    require 'colours/html_colours/random_html_colour.rb'
    require 'save_file/module'
    unless generate_the_shell_file_into_this_directory.end_with? '/'
      if generate_the_shell_file_into_this_directory.frozen?
        generate_the_shell_file_into_this_directory = generate_the_shell_file_into_this_directory.dup
      end
      generate_the_shell_file_into_this_directory << '/'
    end
    result = ''.dup
    result << "# This is a shell file that contains the HTML colours\n"
    available_html_colours?.each {|this_colour|
      colour_code_to_use = ::Colours.html_colourize(this_colour.to_sym,'',false).inspect
      result << "export #{this_colour.upcase}=#{colour_code_to_use}\n"
    }
    generate_the_shell_file_into_this_directory << 'shell_file_containing_the_html_colours.sh'
    into = generate_the_shell_file_into_this_directory
    e "Now creating a shell file at `#{into}`."
    SaveFile.write_what_into(result, into)
    # ======================================================================= #
    # Generate files on my home system as well:
    # ======================================================================= #
    if is_on_roebe?
      into = '/home/x/programming/ruby/src/'\
             'roebe/lib/roebe/shell_scripts/'+
             File.basename(generate_the_shell_file_into_this_directory)
      puts "And also saving into `#{into}`."
      SaveFile.write_what_into(result, into)
    end
  end; self.instance_eval { alias autogenerate_shell_file_containing_the_html_colours generate_shell_file_containing_the_html_colours } # === Colours.autogenerate_shell_file_containing_the_html_colours

  # ========================================================================= #
  # === Colours.is_on_roebe?
  # ========================================================================= #
  def self.is_on_roebe?
    ENV['IS_ROEBE'].to_s == '1'
  end

  # ========================================================================= #
  # === Colours.open_this_file
  #
  # This method will make use of the editor called bluefish to open
  # this file.
  # ========================================================================= #
  def open_this_file(
      shall_we_exit = false
    )
    case shall_we_exit
    # ======================================================================= #
    # === :then_exit
    # ======================================================================= #
    when :then_exit
      shall_we_exit = true
    end
    _ = "bluefish #{__FILE__}"
    esystem(_)
    exit if shall_we_exit
  end

  # ========================================================================= #
  # === Colours.convert_hex_code_to_RGBA_array
  #
  # RGBA stands for "red, green, blue, alpha". Alpha indicates how
  # opaque each pixel is.
  #
  # The usual values for the alpha parameter, aka the last parameter,
  # is a number between 0.0 (which means "fully transparent") and
  # the number 1.0 (which means "not transparent at all").
  #
  # Note that this method is similar to Colours.convert_hex_to_rgb(hex),
  # but it has a fourth argument, aka A (for Alpha), on top of
  # the RGB values.
  #
  # Usage example:
  #
  #   Colours.convert_hex_code_to_RGBA_array('#baf185') # => [186, 241, 133]
  #
  # ========================================================================= #
  def self.convert_hex_code_to_RGBA_array(
      i,
      default_alpha_value = 1.0
    )
    rgba_array = convert_hex_to_rgb(i)
    rgba_array << default_alpha_value
    return rgba_array
  end

  # ========================================================================= #
  # === Colours.convert_hex_to_rgb
  #
  # This method will convert e. g. #baf185 to [186, 241, 133]. Thus it
  # will return an Array, denoting the R, G, B values.
  #
  # How to do this conversion on your own?
  #
  #   (1) Get the 2 left digits of the hex color code and convert
  #       to decimal value to get the red color level.
  #   (2) Get the 2 middle digits of the hex color code and
  #       convert to decimal value to get the green color level.
  #   (3) Get the 2 right digits of the hex color code and
  #       convert to decimal value to get the blue color level.
  #
  # Usage example:
  #
  #   Colours.convert_hex_to_rgb('#baf185') # => [186, 241, 133]
  #
  # ========================================================================= #
  def self.convert_hex_to_rgb(hex)
    if hex.is_a? Array
      hex = hex.first
    end
    hex = hex.to_s.dup
    hex.delete!('#') if hex.include? '#'
    array = [] # We will return this Array.
    r = hex[0,2].to_i(16)
    g = hex[2,2].to_i(16)
    b = hex[4,2].to_i(16)
    array << r << g << b
    return array
  end; self.instance_eval { alias hex_to_rgb convert_hex_to_rgb } # === Colours.hex_to_rgb

  # ========================================================================= #
  # === Colours.sanitize_line
  #
  # This method will replace one line with the proper R,G,B valid entries.
  # ========================================================================= #
  def self.sanitize_line(entry)
    all_potential_matches = entry.scan(/<(\w+)>/).flatten
    all_potential_matches.each {|substring|
      entry.gsub!(/<\/>/, rev)
      entry.gsub!(/<#{substring}>/, rgb_format(substring))
    }
    begin
      require 'roebe/modules/remove_html.rb'
    rescue LoadError; end
    if Object.const_defined?(:Roebe) and
       Roebe.respond_to?(:remove_html)
      entry = Roebe.remove_html[entry]
    end
    return entry
  end

  # ========================================================================= #
  # === Colours.cliner
  # ========================================================================= #
  def self.cliner(
      i = 78, &block
    )
    yield if block_given?
    e '=' * i
  end

  # ========================================================================= #
  # === Colours.return_a_unique_array_containing_all_available_colours
  # ========================================================================= #
  def self.return_a_unique_array_containing_all_available_colours
    array = []
    array << YAML.load_file(FILE_HTML_COLOURS).keys                # (1) First the HTML colours
    array << YAML.load_file(FILE_256_COLOURS).keys.map(&:downcase) # (2) Then the basic colours
    array << YAML.load_file(FILE_BASIC_COLOURS)                    # (3) And finally the basic colours
    array.flatten!
    array.uniq!
    array.map!(&:strip)
    array.sort
  end; self.instance_eval { alias all_available_colour_methods? return_a_unique_array_containing_all_available_colours } # === Colours.all_available_colour_methods?

  # ========================================================================= #
  # === Colours.bold
  #
  # ANSI colour escape code for bold is "1".
  #
  # The method has to call Colours.rev() at the end, because that way
  # downstream users can modify the default rev-colour in use.
  #
  # Usage example:
  #
  #   puts ' ok | '+Colours.bold('Hello world!')+' | ok'
  #
  # ========================================================================= #
  def self.bold(
      show_this_text = 'This must be bold.'
    )
    return "\x1b[1m#{show_this_text}#{::Colours.rev}"
  end; self.instance_eval { alias return_bold bold } # === Colours.return_bold

  # ========================================================================= #
  # === italic
  # ========================================================================= #
  def italic(
      i            = '',
      make_newline = false
    )
    ::Colours.italic(i, make_newline)
  end

  # ========================================================================= #
  # === Colours.clear_screen
  #
  # Simply perform 'clear' here.
  # ========================================================================= #
  def self.clear_screen
    system 'clear'
  end; self.instance_eval { alias clear clear_screen } # === Colours.clear_screen

  # ========================================================================= #
  # === Colours.bold_and_italic
  #
  # Usage example:
  #
  #   puts Colours.bold_and_italic 'Hello world!'
  #
  # ========================================================================= #
  def self.bold_and_italic(
      i = 'This must be bold.'
    )
    if i.is_a? Array
      i = i.join(' ')
    end
    italic(bold(i))
  end

  # ========================================================================= #
  # === Colours.html_colour_to_hex_value
  #
  # This method will return a String, such as "FFFF00".
  #
  # Usage example:
  #
  #   Colours.html_colour_to_hex_value('yellow') # => "#FFFF00"
  #
  # ========================================================================= #
  def self.html_colour_to_hex_value(
      this_html_colour     = nil,
      return_with_hash_key = true
    )
    if this_html_colour.is_a? Array
      this_html_colour = this_html_colour.first
    end
    # ======================================================================= #
    # Obtain the path to the file that keeps the html-colours. On my home
    # system this may be a path like this:
    #
    #   /usr/lib/ruby/site_ruby/3.2.0/colours/yaml/html_colours.yml
    #
    # ======================================================================= #
    _ = file_html_colours_to_rgb?
    if File.exist? _
      # ===================================================================== #
      # Load the yaml-file next:
      # ===================================================================== #
      dataset = YAML.load_file(_)
      if dataset.has_key? this_html_colour
        _ = dataset[this_html_colour].last
        if return_with_hash_key
          _ = _.dup if _.frozen?
          _ = _.to_s unless _.is_a? String
          _[0,0] = '#' if _.respond_to? :[]=
        end
        return _
      end
    end
    return this_html_colour
  end

  # ========================================================================= #
  # === Colours.return_underline
  #
  # The point of this method here is to make a given text (String) appear
  # "underlined", via ANSI escape sequences. For underline the String
  # that should be used is "\u001b[4m".
  #
  # This can be tested on the commandline such as via:
  #
  #   echoen "\u001b[4m Underline \u001b[0m"
  #
  # Usage examples:
  #
  #   puts ' ok | '+Colours.return_underline('Hello world!')+' | ok'
  #   puts ' ok | '+Colours.return_underline('Hello world!') { :slateblue }+' | ok'
  #
  # In January 2022 the second variant was removed, though. Let's keep things
  # simple for now - perhaps in the future this may be re-enabled.
  # ========================================================================= #
  def self.return_underline(
      show_this_text          = 'This must be underline.',
      make_newline            = false,
      use_this_as_revert_code = REVERT
    )
    # ===================================================================== #
    # Build up our main string that codes for underline.
    # ===================================================================== #
    result = "\u001b[#{UNDERLINE_CODE}m".dup
    # if block_given?
    #   # ===================================================================== #
    #   # Right now we assume that this must be a colour if it is supplied
    #   # via a block. However had, in January 2022 I realised that this
    #   # assumption is not always correct, so this was disabled for now.
    #   # At a later moment in time we may reconsider this.
    #   # ===================================================================== #
    #   result << ";38;2;"
    #   colour_replacement = colour_to_rgb_value(yield)
    #   result << colour_replacement
    # end
    # result << 'm'
    if make_newline # Append a newline in this case.
      show_this_text = "#{show_this_text}\n"
    end
    result << show_this_text
    result << use_this_as_revert_code
    return result
  end; self.instance_eval { alias string_underline return_underline } # === Colours.string_underline

  # ========================================================================= #
  # === Colours.random_value?
  #
  # We will obtain a random value between 0 and 255, hence why we will
  # use rand(256).
  # ========================================================================= #
  def self.random_value?
    rand(256)
  end; self.instance_eval { alias random_value random_value? } # === Colours.random_value
       self.instance_eval { alias rvalue       random_value? } # === Colours.rvalue
       self.instance_eval { alias r?           random_value? } # === Colours.r?
       self.instance_eval { alias g?           random_value? } # === Colours.g?
       self.instance_eval { alias b?           random_value? } # === Colours.b?

  # ========================================================================= #
  # === random_value
  #
  # We will obtain a random value between 0 and 255, hence why we will
  # use rand(256). 
  # ========================================================================= #
  def random_value?
    ::Colours.random_value?
  end; alias rvalue       random_value? # === rvalue
       alias random_value random_value? # === random_value
       alias r?           random_value? # === r?
       alias g?           random_value? # === g?
       alias b?           random_value? # === b?

  # ========================================================================= #
  # === Colours.italic
  #
  # This method will "puts" the result of applying italic to a string,
  # on the console/terminal. If you wish to do the output on your own
  # then you have to use the method Colours.return_italic or its
  # alias called Colours.string_italic.
  #
  # To test this, try:
  #
  #   Colours.italic('Hello world!')
  #
  # ========================================================================= #
  def self.italic(
      i            = '', 
      make_newline = false,
      &block
    )
    e return_italic(i, make_newline, &block)
  end

  # ========================================================================= #
  # === Colours.remove_escape_sequence
  #
  # The method Colours.remove_escape_sequence() will remove all Ansi 
  # Escape sequences from a given string.
  # ========================================================================= #
  def self.remove_escape_sequence(i = ARGV)
    if i.is_a? Array
      i = i.join("\n")
    end
    i = i.to_s.dup
    # ======================================================================= #
    # Iterate over the registered ansi-colours next.
    # ======================================================================= #
    ARRAY_REGISTERED_ANSI_COLOURS.each {|entry|
      if i.include? entry
        entry = Regexp.quote(entry)
        i.sub!(/#{entry}/, '')
      end
      # ===================================================================== #
      # Next, check for KDE konsole colours. We must use "" there, not ''.
      # ===================================================================== #
      if i.include?("\e[") and i =~ /\d+m/ # Such as: "\e[38;2;220;20;60m|"
        # =================================================================== #
        # The next regex will (hopefully) remove all escape-characters
        # from the given String.
        # =================================================================== #
        regex_for_html_colours = # Detect entries such as: \e[38;2;106;90;205m
          /(\e\[\d{2};\d{1};\d{3};\d{1,2};\d{3}m)/ # See: https://rubular.com/r/tG3XeOK5NPsfmI
        i.gsub!(regex_for_html_colours, '')
        # =================================================================== #
        # See: https://rubular.com/r/SdS28fAGSxIELn
        # =================================================================== #
        # /(\\e\[\d{1,2};?\d{1,2};?\d{0,3};?\d{0,3};?\d{1,2}m|\\e\[\d{0,2}m|\\e\[K|\\e\[\d{0,2};\d{0,2}m|(\\e\[.+\dm)|\\e\[0;37m)/
        [
          /\e\[\d{1,2};?\d{1,2};?\d{0,3};?\d{0,3};?\d{1,2}m/,
          /\\e\[\d{0,2}m|\\e\[K|\\e\[\d{0,2};\d{0,2}m/,
          /\\e\[.+\dm/,
          /\\e\[0;37m/
        ].each {|regex_to_use|
          i.gsub!(regex_to_use, '')
        }
      end
    }
    return i
  end; self.instance_eval { alias remove                  remove_escape_sequence } # === Colours.remove
       self.instance_eval { alias remove_escape_sequences remove_escape_sequence } # === Colours.remove_escape_sequences
       self.instance_eval { alias remove_colours          remove_escape_sequence } # === Colours.remove_colours
       self.instance_eval { alias escape                  remove_escape_sequence } # === Colours.escape

  # ========================================================================= #
  # === Colours.does_this_string_include_a_html_number?
  #
  # This method will return true if the string includes tags such as
  # <one> or <two> and so forth.
  # ========================================================================= #
  def self.does_this_string_include_a_html_number?(i)
    i.include?('<one>')   or
    i.include?('<two>')   or
    i.include?('<three>') or
    i.include?('<four>')  or
    i.include?('<five>')
  end

  # ========================================================================= #
  # === Colours.eliminate_html
  #
  # This method simply combines two other methods, without any further
  # checks inside of this method. The method will thus remove entries
  # such as <one> or <steelblue>.
  # ========================================================================= #
  def self.eliminate_html(
      i,
      use_this_colour_for_the_default_colour = :default,
      use_this_as_replacement_hash           = :default_hash
    )
    i = i.dup
    i = Colours.replace_number_words_with_the_corresponding_html_colour(
      i,
      use_this_colour_for_the_default_colour,
      use_this_as_replacement_hash
    )
    i = i.dup
    i = Colours.replace_html_colours_in_this_string(
      i,
      use_this_colour_for_the_default_colour
    )
    return i.dup
  end; self.instance_eval { alias away_with_html_colours_and_special_numbers eliminate_html } # === Colours.away_with_html_colours_and_special_numbers
       self.instance_eval { alias remove_html_tags_and_special_words         eliminate_html } # === Colours.remove_html_tags_and_special_words
       self.instance_eval { alias remove_crap                                eliminate_html } # === Colours.remove_crap

  # ========================================================================= #
  # === Colours.does_this_string_include_a_html_colour?
  # ========================================================================= #
  def self.does_this_string_include_a_html_colour?(i)
    result = (i =~ REGEX_FOR_HTML_COLOURS)
    result = false if result.nil?
    result
  end

  # ========================================================================= #
  # === Colours.show_basic_colour_palette
  #
  # This will show a colour palette on the commandline.
  # ========================================================================= #
  def self.show_basic_colour_palette
    e
    MAIN_COLOURS.reject {|e| e == :black }.each { |entry|
      make_colour(entry) # make_colour() is defined in this file here.
    }
    cliner
    SECONDARY_COLOURS.each { |entry|
      make_colour(entry)
    }
    e
  end; self.instance_eval { alias show_palette        show_basic_colour_palette } # === Colours.show_palette
       self.instance_eval { alias show_colour_palette show_basic_colour_palette } # === Colours.show_colour_palette

  # ========================================================================= #
  # === Colours.reset_the_line
  #
  # This method will reset the current line. This can then be used in a
  # progress indicator application.
  # ========================================================================= #
  def self.reset_the_line
    STDOUT.write("\u001b[1000D") # Move to the left by 1000 characters. Aka reset.
    STDOUT.flush # This line will force the output to appear immediately, 
  end

  # ========================================================================= #
  # === Colours.remove_trailing_end_from
  #
  # The second argument to this method can be the escape sequence that you
  # wish to remove.
  #
  # Invocation example:
  #
  #   x = Colours.remove_trailing_end_from("\e[38;2;70;130;180m\e[0;37m") # => "\e[38;2;70;130;180m"
  #
  # ========================================================================= #
  def self.remove_trailing_end_from(
      i,
      remove_this_escape_sequence = revert?
    )
    quoted = Regexp.quote(remove_this_escape_sequence)
    # ======================================================================= #
    # Anchor it at the end via $.
    # ======================================================================= #
    return i.sub(
      /#{quoted}$/, ''
    )
  end; self.instance_eval { alias remove_trailing_escape_part       remove_trailing_end_from } # === Colours.remove_trailing_escape_part
       self.instance_eval { alias remove_trailing_ansii_escape_code remove_trailing_end_from } # === Colours.remove_trailing_ansii_escape_code
       self.instance_eval { alias remove_trailing_ANSII_escape_code remove_trailing_end_from } # === Colours.remove_trailing_ANSII_escape_code
       self.instance_eval { alias remove_trailing_escape_code       remove_trailing_end_from } # === Colours.remove_trailing_escape_code
       self.instance_eval { alias remove_trailing_code              remove_trailing_end_from } # === Colours.remove_trailing_code

end

if __FILE__ == $PROGRAM_NAME
  alias e puts
  alias ee print
  _text = 'Hello world!'
  include Colours
  e Colours.string_italic('Hello world!')+' - and this is not in italic anymore.'
  e
  e 'Colours.rev is: '+Colours.rev
  e
  Colours.show_basic_colour_palette
  e
  if ARGV.empty?
    e Colours.bold_and_italic('Hello world!')+' All is fine.'
  else
    e Colours.bold_and_italic(ARGV)
  end
  e
  pp Colours.convert_hex_to_rgb('#baf185') # => [186, 241, 133]
  e
  e Colours.bold('Hello world!')
  e
  if ARGV.empty?
    require 'colours/requires/require_all_colour_methods.rb'
    COLOURS = Colours::AllColourMethods
    system 'clear'
    Colours.italic(txt)
    e COLOURS.palegreen('italic in palegreen:')
    ee '  '; e Colours.string_italic(txt) { :palegreen }
    e COLOURS.slateblue('italic in slateblue:')
    ee '  '; e Colours.string_italic(txt) { :slateblue }
    e COLOURS.orange('italic in orange:')
    ee '  '; e Colours.string_italic(txt) { :orange }
    e COLOURS.crimson('italic in crimson:')
    ee '  '; e Colours.string_italic(txt) { :crimson }
    e 'Italic without colours:'
    ee '  '; e 'Die '+Colours.string_italic('R-Gruppe bei')+
               ' Serin (S) besteht aus ...'
  else
    ee '  '; e Colours.string_italic(ARGV.join(' ').strip)
    ee '  '; e Colours.string_italic(ARGV.join(' ').strip) { :crimson }
  end
  # ========================================================================= #
  # Example code demonstrating the method defined above:
  # ========================================================================= #
  index = 1
  loop {
    Colours.reset_the_line
    print 'OK! '+
           Random.rand(11).to_s 
    index += 1
    break if index == 50
    sleep 0.3
  }
  e
  alias e puts
  begin
    require 'colours/autoinclude'
  rescue LoadError; e 'the colours gem is not available.'; end
  include Colours
  e
  e 'Next testing an escape sequence (which will be shown via pp only):'
  e
  x = "\e[01m\e[Kcc1:\e[m\e[K \e[01;35m\e[Kwarning: \e[m\e[K-Wabi won't warn about anything [\e[01;35m\e[K-Wabi\e[m\e[K]

A\e[38;2;220;20;60m|AGCTT"
  pp x
  e
  x = Colours.remove_escape_sequence(x)
  e
  pp x
  e x
  pp Colours.remove_escape_sequences(Colours.slateblue('Hello world!')) # => "\e[38;2;106;90;205mHello world!"
  p Colours.slateblue('Hello world!') # => "\e[38;2;106;90;205mHello world!\e[0;37m"
  e
  if ARGV.empty?
    e Colours.underline('Hello world!')
    e 'Hello world!'
    # ======================================================================= #
    # And then run some more tests:
    # ======================================================================= #
    require 'colours/toplevel_methods/use_colours.rb'
    Colours.enable_html_colours
    e Colours.palegreen('in palegreen:')
    print '  '; print Colours.underline(txt) { :palegreen }
    e Colours.slateblue('in slateblue:')
    print '  '; print Colours.underline(txt) { :slateblue }
    e Colours.orange('in orange:')
    print '  '; print Colours.underline(txt) { :orange }
    e Colours.crimson('in crimson:')
    print '  '; print Colours.underline(txt) { :crimson }
  else
    e Colours.underline(ARGV.join(' ').strip)
    e Colours.underline(ARGV.join(' ').strip) { :crimson }
  end
  e
  input = ARGV
  if input.empty?
    input << 'steelblue'
  end
  e
  e 'Testing: Colours.html_colour_to_hex_value(ARGV) next'
  e
  e Colours.html_colour_to_hex_value(input)
  e "Testing: Colours.does_this_string_include_a_html_number?('abc <one>def</one>') next"
  puts Colours.does_this_string_include_a_html_number?('abc <one>def</one>')
  # ========================================================================= #
  # Test the behaviour of the method next:
  # ========================================================================= #
  this_line = "- <lightseagreen>hey</lightseagreen> yo <green>there</green>"
  puts Colours.replace_html_colours(
    this_line
  )
  puts Colours.does_this_string_include_a_html_colour?('<steelblue>')
  e
  e 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?"
  e Colours.replace_number_words_with_the_corresponding_html_colour(x)
  e Colours.replace_html_colours_in_this_string("<one>Methämoglobin</one> ist <steelblue>ungeeignet</steelblue>.")
  e
  string = "- <teal>Asbestbedingte Erkrankungen</teal> haben eine <two>Latenzzeit</two> von etwa n Jahren? Etwa <steelblue>30 Jahren</steelblue>."
  e string
  e Colours.replace_html_colours_in_this_string(
    string
  )
  e
  result = Colours.replace_all_html_colours_in_this_line(' |<steelblue>E</steelblue>]', :default, :revert)
  e result
  e
  e 'And it looks like this via pp:'
  e
  pp result
  e
  # pp Colours.return_a_unique_array_containing_all_available_colours
  # e
  Colours.autogenerate_the_module_for_the_html_colours
  Colours.autogenerate_shell_file_containing_the_html_colours
  Colours.autogenerate_the_module_for_the_256_colours
  Colours.autogenerate_toplevel_basic_colour_methods
  e Colours.html_colourize('slateblue', 'Hello world 1!')
  e Colours.html_colourize(:crimson, 'Hello world 2!')
  e
end # toplevel_methods.rb