module Mittsu
  class Loader
    def initialize(show_status = false)
      @image_loader = ImageLoader.new
    end

    def init_materials(materials, texture_path)
      materials.map do |m|
        create_material(m, texture_path)
      end
    end

    def needs_tangents(materials)
      materials.each do |m|
        return true if m.is_a?(ShareMaterial)
      end
      false
    end

    def create_material(m, texture_path)
      # defaults

      mtype = 'MeshLambertMaterial'
      mpars = {
        color: 0xeeeeee,
        opactity: 1.0,
        map: nil,
        light_map: nil,
        normal_map: nil,
        bump_map: nil,
        wireframe: false
      }

      # parameters from model file

      if m.shading
        shading = m.shading.downcase

        if shading == 'phong'
          mtype = 'MeshPhongMaterial'
        elsif shading == 'basic'
          mtype = 'MeshBasicMaterial'
        end
      end

      if m.blending && Mittsu.const_get(m.blending)
        mpars[:blending] = Mittsu.const_get(m.blending)
      end

      mpars[:transparent] = m.transparent if !m.transparent.nil?
      mpars[:transparent] = true if m.opacity && m.opacity < 1.0
      mpars[:depth_test] = m.depth_test if !m.depth_test.nil?
      mpars[:depth_write] = m.depth_write if !m.depth_write.nil?
      mpars[:visible] = m.visible if !m.visible.nil?
      mpars[:flip_sided] = BackSide if !m.flip_sided.nil?
      mpars[:double_sided] = DoubleSide if !m.double_sided.nil?
      mpars[:wireframe] = m.wireframe if !m.wireframe.nil?

      if !m.vertex_colors.nil?
        if m.vertex_colors == 'face'
          mpars[:vertex_colors] = FaceColors
        elsif !m.vertex_colors.empty?
          mpars[:vertex_colors] = VertexColors
        end
      end

      # colors

      if m.color_diffuse
        mpars[:color] = rgb2hex(m.color_diffuse)
      elsif m.dgb_color
        mpars[:color] = m.dgb_color
      end

      if m.color_specular
        mpars[:specular] = rgb2hex(m.color_specular)
      end

      if m.color_emissive
        mpars[:emissive] = rgb2hex(m.color_emissive)
      end

      # modifiers

      if !m.transparency.nil?
        puts "WARNING: Mitsu::Loader: transparency has been renamed to opacity"
        m.opacity = m.transparency
      end

      if !m.opacity.nil?
        mpars[:opacity] = m.opacity
      end

      if m.specular_coef
        mpars[:shininess] = m.specular_coef
      end

      # textures

      if m.map_diffuse && texture_path
        create_texture(mpars, 'map', m.map_diffuse, m.map_diffuse_repeat, m.map_diffuse_offset, m.map_diffuse_wrap, m.map_diffuse_anisotropy)
      end

      if m.map_light && texture_path
        create_texture(mpars, 'light_map', m.map_light, m.map_light_repeat, m.map_light_offset, m.map_light_wrap, m.map_light_anisotropy)
      end

      if m.map_bump && texture_path
        create_texture(mpars, 'bump_map', m.map_bump, m.map_bump_repeat, m.map_bump_offset, m.map_bump_wrap, m.map_bump_anisotropy)
      end

      if m.map_normal && texture_path
        create_texture(mpars, 'normal_map', m.map_normal, m.map_normal_repeat, m.map_normal_offset, m.map_normal_wrap, m.map_normal_anisotropy)
      end

      if m.map_specular && texture_path
        create_texture(mpars, 'specular_map', m.map_specular, m.map_specular_repeat, m.map_specular_offset, m.map_specular_wrap, m.map_specular_anisotropy)
      end

      if m.map_alpha && texture_path
        create_texture(mpars, 'alpha_map', m.map_alpha, m.map_alpha_repeat, m.map_alpha_offset, m.map_alpha_wrap, m.map_alpha_anisotropy)
      end

      #

      if m.map_bump_scale
        mpars[:bump_scale] = m.map_bump_scale
      end

      if m.map_normal_factor
        mpars[:normal_scale] = Vector2.new(m.map_normal_factor, m.map_normal_factor)
      end

      Mittsu.const_get(mtype).new(mpars).tap do |material|
        material.name = m.dbg_name if !m.dbg_name.nil?
      end
    end

    module Handlers
      def self.add(regex, loader)
        @@handlers ||= {}
        @@handlers[regex] = loader
      end

      def self.get(file)
        @@handlers ||= {}
        @@handlers.find(-> () { [nil, nil] }) { |regex, loader| regex =~ file }[1]
      end
    end

    private

    def nearest_pow2(n)
      l = ::Math.log(n) / Math::LN2
      ::Math.pow(2, ::Math.round(l))
    end

    def create_texture(where, name, source_file, repeat, offset, wrap, anisotropy)
      full_path = File.join(texture_path, source_file)

      loader = Handlers.get(full_path)

      if !loader.nil?
        texture = loader.load full_path
      else
        texture = Texture.new

        loader = @image_loader
        image = loader.load full_path

        if !Math.power_of_two?(image.width) || !Math.power_of_two?(image.height)
          # TODO: resize image to power of two
        else
          texture.image = image
        end

        texture.needs_update = true
      end

      texture.source_file = source_file

      if repeat
        texture.repeat.set(repeat[0], repeat[1])

        texture.wrap_s = RepeatWrapping if repeat[0] != 1
        texture.wrap_t = RepeatWrapping if repeat[1] != 1
      end

      if offset
        texture.offset.set(offset[0], offset[1])
      end

      if wrap
        wrap_map = {
          repeat: RepeatWrapping,
          mirror: MirroredRepeatWrapping
        }

        texture.wrap_s = wrap_map[wrap[0]] unless wrap_map[wrap[0]].nil?
        texture.wrap_t = wrap_map[wrap[1]] unless wrap_map[wrap[1]].nil?
      end

      texture.anisotropy = anisotropy if anisotropy

      where[name] = texture
    end

    def rgb2hex(rgb)
      (rgb[0] * 255 << 16) + (rgb[1] * 255 << 8) + rgb[2] * 255
    end
  end
end
