require 'concurrent/array'
require 'concurrent/map'
require 'childprocess'
require 'json'

module WebpackDriver

    class Process
        READ_CHUNK_SIZE = 1024

        extend Forwardable

        def_delegators :@proc, :alive?, :environment, :wait

        attr_reader :config, :assets, :messages, :error,
                    :last_compilation_message, :last_status

        def initialize(script, config)
            self.reset!
            @config = config
            args = ["./node_modules/.bin/#{script}"] + config.flags
            config.logger.info("Starting webpack using command:\n#{args.join(' ')}")
            @proc = ::ChildProcess.build(*args)

            @proc.environment.merge!(
                config.environment
            )
            if config.directory
                config.logger.info("In directory: #{config.directory}")
                @proc.cwd = config.directory
            end
        end

        def start
            self.reset!
            @output, w = IO.pipe
            @proc.io.stdout = @proc.io.stderr = w
            @proc.start
            w.close
            @listener = listen_for_status_updates
        end

        def stop
            @proc.stop
            @output.close unless @output.closed?
            @listener.join
        end

        def in_progress?
            @last_status == 'compiling'
        end

        protected

        def reset!
            @assets = Concurrent::Map.new
            @messages = Concurrent::Array.new
            @last_compilation_message = {}
        end

        def record_error(error)
            @error = error
            config.logger.error(
                "#{error['name']}: #{error['resource']}\n#{error['message']}"
            )
        end

        def record_status(status)
            @last_status = status
        end

        def record_message(msg)
            unless msg['type'] == 'compile'
                @messages << msg
                config.logger.debug(msg)
            end
            case msg['type']
            when 'status'
                record_status(msg['value'])
            when 'asset'
                Asset.record(@assets, msg['value'])
            when 'error'
                record_error(msg['value'])
            when 'config'
                config.output_path = Pathname.new(msg['value']['output_path'])
            end
        end


        def listen_for_status_updates
            Thread.new do
                @output.each_line do | l |
                    begin
                        match = l.match(/^STATUS: (.*)/)
                        if match
                            record_message(JSON.parse(match[1]))
                        else
                            config.logger.info(l.chomp)
                        end
                    rescue => e
                        config.logger.error "Exception #{e} encountered while processing line #{l}"
                    end
                end
            end
        end

    end
end