#
# Author:: Adam Jacob (<adam@opscode.com>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'chef/provider/file'
require 'chef/provider/directory'
require 'chef/rest'
require 'chef/mixin/find_preferred_file'
require 'chef/resource/directory'
require 'chef/resource/remote_file'
require 'chef/platform'
require 'uri'
require 'tempfile'
require 'net/https'

class Chef
  class Provider
    class RemoteDirectory < Chef::Provider::Directory

      include ::Chef::Mixin::FindPreferredFile
      
      def action_create
        super
        do_recursive
      end
    
      protected
      
      def do_recursive
        if Chef::Config[:solo]
          Chef::Log.debug("Doing a local recursive directory copy for #{@new_resource}")
        else
          Chef::Log.debug("Doing a remote recursive directory transfer for #{@new_resource}")
        end
          
        files_to_transfer.each do |remote_file_source|
          fetch_remote_file(remote_file_source)
        end
      end
      
      def files_to_transfer
        file_list = Chef::Config[:solo] ? generate_solo_file_list : generate_client_file_list
        Chef::Log.debug("Generated file manifest for #{@new_resource}:\n#{file_list.join("\n")}")
        file_list
      end
      
      def generate_solo_file_list
        # Pulled from chef-server-slice files controller
        directory = find_preferred_file(
          @new_resource.cookbook_name, 
          :remote_file, 
          @new_resource.source, 
          @node[:fqdn],
          @node[:platform],
          @node[:platform_version]
        )

        unless (directory && ::File.directory?(directory))
          raise NotFound, "Cannot find a suitable directory"
        end

        file_list = Array.new
        Dir[::File.join(directory, '**', '*')].sort.reverse.select do |file|
          unless ::File.directory?(file)
            file_list << file[/^#{directory}\/(.+)$/, 1]
          end
        end
        file_list
      end
      
      def generate_client_file_list
        r = Chef::REST.new(Chef::Config[:remotefile_url])
        r.get_rest(generate_url(@new_resource.source, "files", { :recursive => "true" }))
      end
      
      def fetch_remote_file(remote_file_source)
        full_path = ::File.join(@new_resource.path, remote_file_source)
        
        ensure_directory_exists(::File.dirname(full_path))
        
        file_to_fetch = provider_for_remote_file(full_path, remote_file_source)
        file_to_fetch.load_current_resource
        file_to_fetch.action_create
        @new_resource.updated = true if file_to_fetch.new_resource.updated        
      end
      
      def provider_for_remote_file(path, source)
        remote_file = Chef::Resource::RemoteFile.new(path, nil, @node)
        remote_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
        remote_file.source(::File.join(@new_resource.source, source))
        remote_file.mode(@new_resource.files_mode) if @new_resource.files_mode
        remote_file.group(@new_resource.files_group) if @new_resource.files_group
        remote_file.owner(@new_resource.files_owner) if @new_resource.files_owner
        remote_file.backup(@new_resource.files_backup) if @new_resource.files_backup
        
        Chef::Platform.provider_for_node(@node, remote_file)
      end
      
      def ensure_directory_exists(path)
        unless ::File.directory?(path)
          directory_to_create = provider_for_directory(path)
          directory_to_create.load_current_resource
          directory_to_create.action_create
          @new_resource.updated = true if directory_to_create.new_resource.updated  
        end
      end
      
      def provider_for_directory(path)
        new_dir = Chef::Resource::Directory.new(path, nil, @node)
        new_dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name            
        new_dir.mode(@new_resource.mode)
        new_dir.group(@new_resource.group)
        new_dir.owner(@new_resource.owner)
        new_dir.recursive(true)
        
        Chef::Platform.provider_for_node(@node, new_dir)
      end
            
      def action_create_if_missing
        raise Chef::Exceptions::UnsupportedAction, "Remote Directories do not support create_if_missing."
      end

    end
  end
end