require 'rrb/script'
require 'rrb/common_visitor'

module RRB

  class ExtractSuperclassVisitor < Visitor
    def initialize( namespace, new_class, targets, dumped_info )
      @new_superclass = namespace.nested(new_class).name
      @targets = targets
      @result = []
      @dumped_info = dumped_info
    end

    attr_reader :result
    
    def visit_class( namespace, node )
      classname = namespace.nested( node.name )
      return unless @targets.include?( classname )
      return if node.superclass == nil
      @result << Replacer.new_from_id(node.superclass.body,
                                      @dumped_info.shrink_const(namespace,
                                                                @new_superclass))
    end
  end
  
  class ScriptFile
    
    def extract_superclass( namespace, new_class, targets, dumped_info )
      visitor = ExtractSuperclassVisitor.new( namespace, new_class, targets,
                                              dumped_info)
      @tree.accept( visitor )
      @new_script = RRB.replace_str( @input, visitor.result )
    end

    def add_superclass_def( lines, lineno )
      indented = RRB.reindent_lines_node( lines, class_node_on( lineno ) ).join
      @new_script = RRB.insert_str( @new_script || @input, lineno, nil, indented )
    end

  end
  
  class Script
    
    def superclass_def( namespace, new_class, old_superclass, where )
      old_superclass = get_dumped_info.shrink_const(namespace, old_superclass.name)
      result = [
        "class #{new_class} < #{old_superclass}\n",
        "end\n"
      ]

      ns = namespace
      until ns == where
        result = RRB.reindent_lines( result, INDENT_LEVEL )
        result.unshift( "#{get_dumped_info[ns].type} #{ns.ary[-1]}\n" )
        result.push( "end\n" )
        ns = ns.chop
      end

      result
    end

    def add_new_class_to_dumped_info(namespace, new_class)
      if namespace == Namespace::Toplevel
        get_dumped_info[Namespace::Object].consts << new_class
      else
        get_dumped_info[namespace].consts << new_class
      end
    end
    
    def extract_superclass( namespace, new_class, targets, path, lineno )
      add_new_class_to_dumped_info(namespace, new_class)
      
      @files.each do |scriptfile|
        scriptfile.extract_superclass(namespace, new_class, targets,
                                      get_dumped_info )
      end
      
      deffile = @files.find{|scriptfile| scriptfile.path == path}
      new_superclass = get_dumped_info[targets.first].superclass.class_name
      def_str = superclass_def( namespace, new_class,
                                new_superclass,
                                deffile.class_on(lineno) )
      deffile.add_superclass_def( def_str, lineno )
    end
    
    def extract_superclass?( namespace, new_class, targets, path, lineno )
      # check namespace exists?
      if namespace != RRB::Namespace::Toplevel &&
         get_dumped_info[namespace].invalid?
        @error_message = "#{namespace.name}: No such namespace"
        return false
      end
      # check all targets exist?
      targets.each do |klass|
        @error_message = "#{klass.name}: No such class"
        return false unless get_dumped_info[klass].type == "class"
      end
      
      # check targets have the same superclass
      superclass = get_dumped_info[targets.first].superclass
      targets.each do |klass|
        @error_message = "#{targets.first.name} and #{klass.name} are not sibling classes"
        return false unless get_dumped_info[klass].superclass == superclass
      end

      # check name collision
      unless get_dumped_info.resolve_const( namespace, new_class ).nil? then
        @error_message = "#{new_class}: already exists"
        return false
      end

      # check where new class is defined
      if class_on( path, lineno ).nil? ||
         ! class_on( path, lineno ).contain?( namespace )
        @error_message = "Invalid Position to define new class"
        return false
      end
      
      return true
    end
    
  end
end


syntax highlighted by Code2HTML, v. 0.9.1