require 'cli/ui'

module CLI
  module UI
    class Progress
      # A Cyan filled block
      FILLED_BAR = "\e[46m"
      # A bright white block
      UNFILLED_BAR = "\e[1;47m"

      # Add a progress bar to the terminal output
      #
      # https://user-images.githubusercontent.com/3074765/33799794-cc4c940e-dd00-11e7-9bdc-90f77ec9167c.gif
      #
      # ==== Example Usage:
      #
      # Set the percent to X
      #   CLI::UI::Progress.progress do |bar|
      #     bar.tick(set_percent: percent)
      #   end
      #
      # Increase the percent by 1 percent
      #   CLI::UI::Progress.progress do |bar|
      #     bar.tick
      #   end
      #
      # Increase the percent by X
      #   CLI::UI::Progress.progress do |bar|
      #     bar.tick(percent: 5)
      #   end
      def self.progress(width: Terminal.width)
        bar = Progress.new(width: width)
        print CLI::UI::ANSI.hide_cursor
        yield(bar)
      ensure
        puts bar.to_s
        CLI::UI.raw do
          print(ANSI.show_cursor)
        end
      end

      # Initialize a progress bar. Typically used in a +Progress.progress+ block
      #
      # ==== Options
      # One of the follow can be used, but not both together
      #
      # * +:width+ - The width of the terminal
      #
      def initialize(width: Terminal.width)
        @percent_done = 0
        @max_width = width
      end

      # Set the progress of the bar. Typically used in a +Progress.progress+ block
      #
      # ==== Options
      # One of the follow can be used, but not both together
      #
      # * +:percent+ - Increment progress by a specific percent amount
      # * +:set_percent+ - Set progress to a specific percent
      #
      def tick(percent: 0.01, set_percent: nil)
        raise ArgumentError, 'percent and set_percent cannot both be specified' if percent != 0.01 && set_percent
        @percent_done += percent
        @percent_done = set_percent if set_percent
        @percent_done = [@percent_done, 1.0].min # Make sure we can't go above 1.0

        print to_s
        print CLI::UI::ANSI.previous_line + "\n"
      end

      # Format the progress bar to be printed to terminal
      #
      def to_s
        suffix = " #{(@percent_done * 100).round(2)}%"
        workable_width = @max_width - Frame.prefix_width - suffix.size
        filled = [(@percent_done * workable_width.to_f).ceil, 0].max
        unfilled = [workable_width - filled, 0].max

        CLI::UI.resolve_text [
          FILLED_BAR + ' ' * filled,
          UNFILLED_BAR + ' ' * unfilled,
          CLI::UI::Color::RESET.code + suffix
        ].join
      end
    end
  end
end