require_relative './exception_type'

# frozen_string_literal: true

module Hypertube
  module Utils
    module Exceptions
      class ExceptionThrower
        def self.throw_exception(exception_command)
          exception_code = exception_command.payload[0]
          stack_command = exception_command.payload[1]
          exception_name = exception_command.payload[2]
          exception_message = exception_command.payload[3]

          exception_object = get_exception_object(exception_code, exception_message)

          stack_trace = get_local_stack_trace(exception_command.payload[4], exception_command.payload[5],
                                              exception_command.payload[6], exception_command.payload[7])
          exception_object.set_backtrace(stack_trace)
          exception_object
        end

        def self.get_local_stack_trace(stack_trace_classes, stack_trace_methods, stack_trace_lines, stack_trace_files)
          stack_classes_array = stack_trace_classes.split('|')
          stack_methods_array = stack_trace_methods.split('|')
          stack_lines_array = stack_trace_lines.split('|')
          stack_files_array = stack_trace_files.split('|')

          stack_trace = ''
          stack_classes_array.each_with_index do |class_name, i|
            stack_trace += "#{stack_files_array[i]}:" if i < stack_files_array.length and stack_files_array[i] != ''
            stack_trace += "#{stack_lines_array[i]} " if i < stack_lines_array.length and stack_lines_array[i] != ''
            stack_trace += "in '#{class_name}#" if class_name != ''
            stack_trace += "#{stack_methods_array[i]}'\n" if i < stack_methods_array.length and stack_methods_array[i] != ''
          end

          stack_trace
        end

        def self.get_exception_object(exception_code, exception_message)
          case exception_code
          when Hypertube::Utils::Exceptions::ExceptionType::EXCEPTION
            Exception.new('Exception ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::IO_EXCEPTION
            IOError.new('IOError ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::FILE_NOT_FOUND_EXCEPTION
            Errno::ENOENT.new('Errno::ENOENT ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::RUNTIME_EXCEPTION
            RuntimeError.new('RuntimeError ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::ARITHMETIC_EXCEPTION
            ZeroDivisionError.new('ZeroDivisionError ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::ILLEGAL_ARGUMENT_EXCEPTION
            ArgumentError.new('ArgumentError ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::INDEX_OUT_OF_BOUNDS_EXCEPTION
            IndexError.new('IndexError ' + exception_message)
          when Hypertube::Utils::Exceptions::ExceptionType::NULL_POINTER_EXCEPTION
            TypeError.new('TypeError ' + exception_message)
          else
            Exception.new(exception_code.to_s + ' ' + exception_message)
          end
        end
      end
    end
  end
end
