require 'rubygems/comparator/report/entry'

class Gem::Comparator

  ##
  # Gem::Comparator::Report can nest sections and print only those that
  # contain some messages.
  #
  # Usage:
  #   report = Gem::Comparator::Report.new
  #   report['section1'] << "Message 1"
  #   report['section1']['subsection1'].set_header 'Message 2 Header'
  #   report['section1']['subsection1'] << "Message 2" if false
  #   report['section1'].section
  #     nest('subsection2').section
  #       puts "Message 3"
  #     end
  #   end
  #   report.print
  #
  # This won't print Message 2 nor its header saving a lot of if/else.

  class Report

    module Signs
      def same
        Rainbow('SAME').green.bright
      end

      def different
        Rainbow('DIFFERENT').red.bright
      end
    end

    def self.new(name = 'main')
      Gem::Comparator::Report::NestedSection.new(name)
    end

    class NestedSection
      include Report::Signs
      include Gem::UserInteraction

      DEFAULT_INDENT = '  '

      attr_reader :header, :messages, :parent_section, :level
      attr_accessor :name, :sections

      def initialize(name, parent_section = nil)
        @name = name
        @header = Entry.new
        @messages = []
        @sections = []
        @level = 0

        set_parent parent_section if parent_section
      end

      def section(&block)
        instance_eval(&block)
      end

      def set_header(message)
        @header = Entry.new(message)
      end

      def puts(message)
        case message
        when String, Array
          @messages << Entry.new(message) unless message.empty?
        else
          @messages << Entry.new(message) unless message
        end
      end
      alias_method :<<, :puts

      def nest(name)
        @sections.each do |s|
          return s if s.name == name
        end
        NestedSection.new(name, self)
      end
      alias_method :[], :nest

      def print
        all_messages.each { |m| m.print }
      end

      def all_messages
        indent = DEFAULT_INDENT*@level

        if @header.empty?
          @messages.map do |m|
            m.set_indent!(indent)
          end + nested_messages
        else
          nested = @messages.map do |m|
            m.set_indent!(indent * 2)
          end + nested_messages
          return [] if nested.empty?

          @header.set_indent!(indent)
          nested.unshift(@header)
        end
      end

      def lines(line_num)
        all_messages[line_num]&.data
      end

      def nested_messages
        nested_messages = []
        @sections.each do |section|
          section.all_messages.each do |m|
            nested_messages << m
          end
        end
        nested_messages
      end

      private

        def set_parent(parent)
          parent.sections << self
          @level = parent.level + 1
        end

    end
  end
end
