module Pelusa
  module Lint
    class CollectionWrappers
      def initialize
        @violations = Set.new
      end

      def check(klass)
        iterate_lines!(klass)

        return SuccessfulAnalysis.new(name) if @violations.empty?

        FailedAnalysis.new(name, @violations) do |violations|
          "Using an instance variable apart from the array ivar in lines #{violations.to_a.join(', ')}"
        end
      end

      private

      def name
        "Doesn't mix array instance variables with others"
      end

      def iterate_lines!(klass)
        # the array of actual assignment nodes -- we only want to assign once
        array_assignments = {}
        # the name of all the assigned arrays, no others allowed
        array_values = {}

        ClassAnalyzer.walk(klass) do |node|
          if node.is_a?(Rubinius::ToolSets::Runtime::ToolSet::AST::InstanceVariableAssignment) &&
            (node.value.is_a?(Rubinius::ToolSets::Runtime::ToolSet::AST::ArrayLiteral) ||
             node.value.is_a?(Rubinius::ToolSets::Runtime::ToolSet::AST::EmptyArray))
            array_assignments[node] = true
            array_values[node.name] = true
          end
        end

        unless array_assignments.empty?
          ClassAnalyzer.walk(klass) do |node|
            # if this is where we assign the node for the first time, good
            unless array_assignments[node]
              # otherwise, if it's an instance variable assignment, verboten!
              if node.is_a?(Rubinius::ToolSets::Runtime::ToolSet::AST::InstanceVariableAssignment)
                @violations << node.line
              # or if we access any other ivars
              elsif node.is_a?(Rubinius::ToolSets::Runtime::ToolSet::AST::InstanceVariableAccess) &&
                !array_values[node.name]
                @violations << node.line
              end
            end
          end
        end
      end
    end
  end
end
