#
# Main class to interact with. In fact this is the only class you should interact with
# when using the library.
#
# === Usage example
#  pretty = PrettyDiff::Diff.new(udiff)
#  pretty.to_html
#
# Keep in mind that Diff will automatically escape all HTML tags from the intput string
# so that it doesn't interfere with the output.
#
module PrettyDiff
  class Diff
    CHUNK_REGEXP = /^@@ .+? @@.*\n?$/

    attr_reader :unified_diff, :generator, :out_encoding, :options

    #
    # Create new Diff object.
    # Accept a String in unified diff format and options hash.
    # Currrent options:
    # * generator -- your own custom implementation of HTML generator. Will use BasicGenerator by default.
    # * out_encoding -- convert encoding of diffs to the specififed encoding. utf-8 by default.
    #
    def initialize(unified_diff, options={})
      @unified_diff = unified_diff
      @generator = validate_generator(options[:generator] || BasicGenerator)
      @out_encoding = options[:out_encoding] || 'utf-8'
      @options = options
    end

    def metadata
      @_metadata ||= begin
        ''.tap do |result|
          unified_diff.each_line do |l|
            result << l
            break if l =~ /\+\+\+\s*/
          end
        end
      end
    end

    def contents
      # We have to strip metadata from the rest of the diff
      # to enforce encoding. It's not uncommon for metadata to be in Unicode
      # while the diff itself is in some other encoding.
      @_contents ||= enforce_encoding(unified_diff.lines[metadata.lines.size..-1].join(''))
    end

    def to_html
      generator.new(self).generate
    end

    def chunks
      @_chunks ||= find_chunks
    end

  private

    def find_chunks
      chunks_meta = contents.scan(CHUNK_REGEXP)
      [].tap do |chunks|
        split = contents.split(CHUNK_REGEXP)
        split.shift
        split.each_with_index do |lines, index|
          chunks << Chunk.new(self, chunks_meta[index], cleanup(lines))
        end
      end
    end

    def validate_generator(gen)
      if valid_generator?(gen)
        gen
      else
        raise InvalidGeneratorError, "#{gen.inspect} is not a valid PrettyDiff generator"
      end
    end

    def valid_generator?(generator)
      !generator.nil? &&
      generator.kind_of?(Class) &&
      generator.ancestors.include?(PrettyDiff::AbstractGenerator) &&
      generator.instance_methods.include?(:generate)
    end

    def enforce_encoding(text)
      Encoding.enforce(out_encoding, text)
    end

    def cleanup(line)
      line[1..-1]
    end

  end
end
