# encoding: utf-8

# Copyright (C) 2011 Clemens Fuchslocher <clemens@vakuumverpackt.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

require 'find'

require_relative 'BlockCommentDetector'

module VoidMeter

	class Meter
		def initialize
			@trailing_spaces = Regexp.new('[ \t]+[\n\r]+$')
		end

		def meter!(project, results)
			puts "VoidMeter::Meter#meter!: project.name == #{project.name}"
			project.releases.each do |release|
				meter_project(project, release, results)
			end
		end

		private

		class IndentType
			attr_reader :regexp, :type

			def initialize(regexp, type)
				@regexp = regexp
				@type = type
			end
		end

		def meter_project(project, release, results)
			if results.has_result?(project.name, release.version)
				puts "VoidMeter::Meter#meter!: release.version == #{release.version} (cached)"
				return
			end

			puts "VoidMeter::Meter#meter!: release.version == #{release.version}"

			@totals = Hash.new(0)
			@indents = Hash.new(0)
			@runtime = 0

			@indents_type = [
				IndentType.new(Regexp.new('^(\t+ )|( +\t)'), :both),
				IndentType.new(Regexp.new('^(?>\t+)(?! )'), :tabs), # Atomic Grouping, Negative Lookahead
				IndentType.new(Regexp.new('^(?> +)(?!\t)'), :spaces) # Atomic Grouping, Negative Lookahead
			]

			start = Time.now

			if !File.directory?(release.path)
				raise Errno::ENOENT, "!File.directory?(release.path); release.path == #{release.path}"
			end

			Find.find(release.path) do |file|
				begin
					meter_file(file, release)
				rescue Exception => e
					@totals[:errors] += 1
					puts e.message
				end
			end

			@runtime = Time.now - start

			results.set_result(project.name, release.version, {
				:totals => @totals,
				:indents => @indents,
				:runtime => @runtime
			})
			results.save
		end

		def meter_file(file, release)
			if !File.file?(file)
				return
			end

			extension = File.extname(file)
			if !release.files.contains_extension(extension)
				return
			end

			block_comment = release.files.get_block_comment(extension)
			if block_comment != nil
				detector = BlockCommentDetector.new(block_comment[:open], block_comment[:close])
			end

			File.open(file, "r:#{release.encoding}") do |file|
				file.each_line do |line|
					meter_line(line, detector)
				end
			end

			@totals[:files] += 1
		end

		def meter_line(line, detector)
			if detector != nil && detector.consume(line)
				@indents[:block_comment] += 1
			else
				@indents[indents_type(line)] += 1
			end

			if @trailing_spaces.match(line) != nil
				@indents[:trailing] += 1
			end

			@totals[:lines] += 1
		end

		def indents_type(line)
			type = @indents_type.find do |type|
				type.regexp.match(line) != nil
			end

			if type.nil?
				return :not
			end

			type.type
		end
	end

end
