module Origen
  class Generator
    # A job is responsible for executing a single pattern source
    class Job # :nodoc: all
      attr_accessor :output_file_body, :pattern
      attr_reader :split_counter

      def initialize(pattern, options)
        @testing = options[:testing]
        @options = options
        @requested_pattern = pattern
        @no_comments = options[:no_comments]
      end

      # Returns true if the job is a test job, will only be true in a test scenario
      def test?
        @testing
      end

      def no_comments?
        @no_comments
      end

      def inc_split_counter
        @split_counter ||= 0
        @split_counter += 1
      end

      def requested_pattern
        @requested_pattern
      end
      alias_method :requested_file, :requested_pattern

      # Returns a full path to the output pattern, note that this is not available
      # until the job has been run
      def output_pattern
        "#{output_pattern_directory}/#{output_pattern_filename}"
      end
      alias_method :output_file, :output_pattern

      def reference_pattern
        "#{reference_pattern_directory}/#{output_pattern_filename}"
      end
      alias_method :reference_file, :reference_pattern

      def output_pattern_filename
        return '' if @testing
        # If the pattern name has been overridden by an interator use that
        return @output_pattern_filename if @output_pattern_filename
        if !@pattern && !@output_file_body
          fail 'Sorry the output_pattern is not available until the job has been run'
        end
        body = @output_file_body ? @output_file_body : File.basename(@pattern, '.rb')
        output_prefix + body + output_postfix + split_number + output_extension
      end

      # This can be modified at runtime by the pattern generator in response to
      # iterator substitutions
      def output_pattern_filename=(val)
        @output_pattern_filename = val
      end

      def reset_output_pattern_filename
        @output_pattern_filename = nil
      end

      def output_pattern_directory
        @output_pattern_directory ||= begin
          dir = Origen.app.config.pattern_output_directory
          if tester.respond_to?(:subdirectory)
            dir = File.join(dir, tester.subdirectory)
          end
          FileUtils.mkdir_p(dir) unless File.exist?(dir)
          dir
        end
      end

      def reference_pattern_directory
        @reference_pattern_directory ||= begin
          dir = Origen.file_handler.reference_directory
          if tester.respond_to?(:subdirectory)
            dir = File.join(dir, tester.subdirectory)
          end
          FileUtils.mkdir_p(dir) unless File.exist?(dir)
          dir
        end
      end

      def output_prefix
        p = Origen.config.pattern_prefix ? Origen.config.pattern_prefix + '_' : ''
        p = "_#{p}" if Origen.tester.doc?
        p
      end

      def output_postfix
        Origen.config.pattern_postfix ? '_' + Origen.config.pattern_postfix : ''
      end

      def output_extension
        '.' + Origen.tester.pat_extension
      end

      def split_number
        if split_counter
          "_part#{split_counter}"
        else
          ''
        end
      end

      def run
        Origen.app.current_job = self
        begin
          if @options[:compile]
            Origen.generator.compiler.compile(@requested_pattern, @options)
          elsif @options[:job_type] == :merge
            Origen.generator.compiler.merge(@requested_pattern)
          elsif @options[:action] == :program
            Origen.flow.reset
            Origen.resources.reset
            OrigenTesters::Generator.execute_source(@pattern)
          else
            Origen.generator.pattern.reset      # Resets the pattern controller ready for a new pattern
            # Give the app a chance to handle pattern dispatch
            skip = false
            Origen.app.listeners_for(:before_pattern_lookup).each do |listener|
              skip ||= !listener.before_pattern_lookup(@requested_pattern)
            end
            unless skip
              @pattern = Origen.generator.pattern_finder.find(@requested_pattern, @options)
              if @pattern.is_a?(Hash)
                @output_file_body = @pattern[:output]
                @pattern = @pattern[:pattern]
              end
              load @pattern unless @pattern == :skip  # Run the pattern
            end
          end
        rescue Exception => e
          if @options[:continue] || Origen.running_remotely?
            Origen.log.error "FAILED - #{@requested_pattern} (for target #{Origen.target.name})"
            Origen.log.error e.message
            e.backtrace.each do |l|
              Origen.log.error l
            end
            if @options[:compile]
              Origen.app.stats.failed_files += 1
            else
              Origen.app.stats.failed_patterns += 1
            end
          else
            raise
          end
        end
      end
    end
  end
end