module Floorplanner
  class Asset
    LIBRARY_GEOMETRIES   = '/COLLADA/library_geometries/geometry'
    LIBRARY_EFFECTS      = '/COLLADA/library_effects/effect'
    LIBRARY_MATERIALS    = '/COLLADA/library_materials/material'
    LIBRARY_NODES        = '/COLLADA/library_nodes/node'
    LIBRARY_IMAGES       = '/COLLADA/library_images/image'
    VISUAL_SCENE_QUERY   = '/COLLADA/library_visual_scenes/visual_scene/node'
    
    NO_NS_NAME = %w{ param }

    CACHE_PATH = File.join(Floorplanner.config['dae_cache_path'], 'kmz')

    attr_reader :id, :name, :title, :dae_path

    def self.get(asset_id,asset_title,asset_url3d)
      FileUtils.mkdir_p(CACHE_PATH)
      asset_url = Floorplanner.config['content_base_url'] + URI.escape(asset_url3d)
      
      cached_path = File.join(CACHE_PATH,asset_id)
      if File.exists?(cached_path)
        $stderr.puts("Cached asset: %s" % asset_id)
        @kmz = Keyhole::Archive.new(cached_path)
        Asset.new(asset_id,asset_title,@kmz)
      else
        $stderr.puts("Downloading asset: %s" % asset_url)
        cached = File.new(cached_path,'w')
        remote = open(asset_url)
        cached.write(remote.read)
        cached.close

        @kmz = Keyhole::Archive.new(cached_path)
        asset = Asset.new(asset_id,asset_title,@kmz)
        asset.adjust_paths!
        asset
      end
    end

    def initialize(id,title,kmz)
      @dae_path = kmz.dae_path(id)
      @kmz   = kmz
      @id    = id
      @title = title
      @xml   = XML::Document.string(File.read(@dae_path).gsub(/xmlns=".+"/, ''))
      @name  = File.basename(@dae_path.gsub(/\.|dae/,''))
      @images_dict = {}
    end

    def measurement_unit
    end

    def library_materials
      return @materials if @materials
      materials = @xml.find(LIBRARY_MATERIALS)
      materials.each {|mat| namespace!(mat)}
      @materials = materials
    end

    def library_effects
      return @effects if @effects
      effects = @xml.find(LIBRARY_EFFECTS)
      effects.each {|eff| namespace!(eff)}
      @effects = effects
    end

    def library_geometries
      return @geometries if @geometries
      geometries = @xml.find(LIBRARY_GEOMETRIES)
      geometries.each{|geo| namespace!(geo)}
      @geometries = geometries
    end

    def library_nodes
      return @nodes if @nodes
      nodes = @xml.find(LIBRARY_NODES)
      nodes.each{|nod| namespace!(nod)}
      @nodes = nodes
    end

    def library_images
      return @images if @images
      images = @xml.find(LIBRARY_IMAGES)
      images.each{|img| namespace!(img) && update_path!(img)}
      @images = images
    end

    def visual_scene_node
      return @scene_node if @scene_node
      @scene_node = namespace!(@xml.find(VISUAL_SCENE_QUERY).first)
    end

    def bounding_box
      mesh = Collada::Geometry.doc @xml
      mesh.bounding_box
    end

    def bounding_box_size
      box = bounding_box
      Geom::Number3D.new(
        box[:max].distance_x(box[:min]),
        box[:max].distance_y(box[:min]),
        box[:max].distance_z(box[:min])
      )
    end

    def scale_ratio(target_size)
      bbox_size = bounding_box_size
      result = Geom::Number3D.new

      result.x = target_size.x / bbox_size.x
      result.y = target_size.y / bbox_size.y
      result.z = 0.01 # (result.x + result.y) / 2.0 # TODO: correct Z size in FML

      result
    end

    def save_textures(root_path)
      images = @xml.find(LIBRARY_IMAGES)
      FileUtils.mkdir_p(root_path) unless images.length.zero?

      images.each do |image|
        relative_to_dae = image.find('init_from').first.content
        img_path = @kmz.image_path(@id,relative_to_dae)
        target_path = File.join(root_path,@id)
        FileUtils.mkdir_p target_path

        target = open(File.join(target_path,File.basename(img_path)),'w')
        target.write(File.read(img_path))
        target.close
        @images_dict[relative_to_dae] = relative_to_dae[3..-1]
      end
    end

    def adjust_paths!
      images = @xml.find(LIBRARY_IMAGES)

      images.each do |image|
        init_from = image.find('init_from').first
        img_path = @kmz.image_path(@id,init_from.content,true)
        init_from.content = img_path
      end
      open(@dae_path, 'w') do |f|
        f.write @xml.to_s
      end
    end

    private

    def namespace!(node)
      node['id'] = "#{@name}_#{node['id']}" if node['id']
      node['sid'] = "#{@name}_#{node['sid']}" if node['sid'] && node['sid'] != 'COMMON'
      node['name'] = "#{@name}_#{node['name']}" if node['name'] && !NO_NS_NAME.include?(node.name)
      node['symbol'] = "#{@name}_#{node['symbol']}" if node['symbol']
      node['material'] = "#{@name}_#{node['material']}" if node['material']


      node['url'] = "##{@name}_#{node['url'].gsub('#','')}" if node['url']
      node['target'] = "##{@name}_#{node['target'].gsub('#','')}" if node['target']
      node['source'] = "##{@name}_#{node['source'].gsub('#','')}" if node['source']
      node['texture'] = "#{@name}_#{node['texture'].gsub('#','')}" if node['texture']

      if node.name == 'surface'
        n = node.find('init_from').first
        n.content = "#{@name}_#{n.content}"
      end

      if node.name == 'sampler2D'
        n = node.find('source').first
        n.content = "#{@name}_#{n.content}"
      end

      node.children.each do |children|
        namespace!(children)
      end
      node
    end

    # updates image path to export output folder
    def update_path!(node)
      init_from_node = node.find('init_from').first
      relative = init_from_node.content
      init_from_node.content = @images_dict[relative]
    end
  end
end