require 'fog/voxel'
require 'fog/compute'

module Fog
  module Compute
    class Voxel < Fog::Service

      requires :voxel_api_key, :voxel_api_secret
      recognizes :host, :port, :scheme, :persistent

      model_path 'fog/voxel/models/compute'
      model       :image
      collection  :images
      model       :server
      collection  :servers

      request_path 'fog/voxel/requests/compute'
      request :images_list
      request :devices_list
      request :devices_power
      request :voxcloud_create
      request :voxcloud_status
      request :voxcloud_delete

      class Mock
        include Collections

        def self.data
          @data ||= Hash.new do |hash, key|
            hash[key] = {
              :last_modified => { :servers => {}, :statuses => {}, :images => {} },
              :servers => [],
              :statuses => {},
              :images  => [
                {'id' => 1,   'name' => "CentOS 4, 32-bit, base install"},
                {'id' => 2,   'name' => "CentOS 4, 64-bit, base install"},
                {'id' => 3,   'name' => "CentOS 5, 32-bit, base install"},
                {'id' => 4,   'name' => "CentOS 5, 64-bit, base install"},
                {'id' => 7,   'name' => "Fedora 10, 32-bit, base install"},
                {'id' => 8,   'name' => "Fedora 10, 64-bit, base install"},
                {'id' => 10,  'name' => "OpenSUSE 11, 64-bit, base install"},
                {'id' => 11,  'name' => "Debian 5.0 \"lenny\", 32-bit, base install"},
                {'id' => 12,  'name' => "Debian 5.0 \"lenny\", 64-bit, base install"},
                {'id' => 13,  'name' => "Ubuntu 8.04 \"Hardy\", 32-bit, base install"},
                {'id' => 14,  'name' => "Ubuntu 8.04 \"Hardy\", 64-bit, base install"},
                {'id' => 15,  'name' => "Voxel Server Environment (VSE), 32-bit, base install"},
                {'id' => 16,  'name' => "Voxel Server Environment (VSE), 64-bit, base install"},
                {'id' => 32,  'name' => "Pantheon Official Mercury Stack for Drupal (based on VSE/64)"},
                {'id' => 55,  'name' => "Ubuntu 10.04 \"Lucid\", 64-bit, base install"} ]
            }
          end
        end

        def self.reset
          @data = nil
        end

        def initialize(options={})
          @voxel_api_key = options[:voxel_api_key]
        end

        def data
          self.class.data[@voxel_api_key]
        end

        def reset_data
          self.class.data.delete(@voxel_api_key)
        end

      end

      class Real
        include Collections

        def initialize(options = {})
          require 'time'
          require 'digest/md5'

          @voxel_api_key = options[:voxel_api_key]
          @voxel_api_secret = options[:voxel_api_secret]

          @connection_options = options[:connection_options] || {}
          @host               = options[:host]    || "api.voxel.net"
          @port               = options[:port]    || 443
          @scheme             = options[:scheme]  || 'https'
          @persistent         = options[:persistent] || false

          @connection_options[:ssl_verify_peer] = false

          @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
        end

        def request(method_name, options = {})
          begin
            parser = options.delete(:parser)
            options.merge!({ :method => method_name, :timestamp => Time.now.xmlschema, :key => @voxel_api_key })
            options[:api_sig] = create_signature(@voxel_api_secret, options)
            data = @connection.request(
              :method => "POST",
              :query  => options,
              :parser => parser,
              :path   => "/version/1.0/"
            )
            unless data.body['stat'] == 'ok'
              raise Fog::Compute::Voxel::Error, "#{data.body['err']['msg']}"
            end
            data
          rescue Excon::Errors::HTTPStatusError => error
            raise case error
            when Excon::Errors::NotFound
              Fog::Compute::Voxel::NotFound.slurp(error)
            else
              error
            end
          end
        end

        def create_signature(secret, options)
          to_sign = options.keys.map { |k| k.to_s }.sort.map { |k| "#{k}#{options[k.to_sym]}" }.join("")
          Digest::MD5.hexdigest( secret + to_sign )
        end
      end
    end
  end
end
