module Judo
  ### sdb
  ### name {
  ###   "version"    => [ server.version ],
  ###   "devs"       => [ "/dev/sde1:snap-abc123", "/dev/sde2:snap-abc456" ],
  ###   "server"     => server.id
  ###   "group"      => server.group.name
  ###   "virgin"     => server.virgin
  ###   "note"       => server.note
  ###   "data"       => server.data
  ###   "created_at" => unixtime
  class Snapshot
    attr_accessor :name, :server_id

    def initialize(base, name, server_id)
      @base = base
      @name = name
      @server_id = server_id
    end

    def server_name
      server.name rescue '(deleted)'
    end

    def server
      @server ||= @base.servers.detect { |s| s.id == server_id }
    end

    def fetch_state
      @base.sdb.get_attributes(@base.snapshot_domain, name)[:attributes]
    end

    def state
      @base.snapshots_state[name] ||= fetch_state
    end

    def group_name
      get("group")
    end

    def created_at
      Time.at(get("created_at").to_i)
    end

    def version
      get("version").to_i
    end

    def note
      get("note")
    end

    def data
      get("data")
    end

    def virgin
      get("virgin").to_s == "true"
    end

    def devs
      (state["devs"] || []).inject({}) { |out, kv| k, v = kv.split(':'); out[k] = v; out }
    end

    def create
      raise JudoError,"snapshot already exists" unless state.empty?
      raise JudoError,"server has no disks to clone: #{server.volumes}" if server.volumes.empty?
      @base.task("Snapshotting #{server.name}") do
        devs = server.volumes.map do |dev,vol|
          "#{dev}:#{@base.ec2.create_snapshot(vol)[:aws_id]}"
        end
        @base.sdb.put_attributes(@base.snapshot_domain, name, {
          "version" => server.version,
          "virgin" => server.virgin?,
          "note" => server.note,
          "data" => server.data,
          "devs" => devs,
          "server" => server.id,
          "group" => server.group.name,
          "created_at" => Time.now.to_i.to_s
        }, :replace)
        server.add "snapshots", name
      end
    end

    def animate(new_server)
      raise JudoError, "cannot animate, snapshotting not complete" unless completed?
      @base.create_server(new_server, group_name, :version => version, :snapshots => devs, :virgin => virgin, :note => note, :data => data , :clone => name)
    end

    def delete
      ### TODO - wait for snapshotting to finish
      @base.sdb.delete_attributes(@base.snapshot_domain, name)
      server.remove("snapshots", name) if server
    end

    def get(key)
      state[key] && [state[key]].flatten.first
    end

    def destroy
      devs.each do |dev,snapshot_id|
        @base.task("Deleting snapshot #{snapshot_id}") do
          begin
            @base.ec2.delete_snapshot(snapshot_id)
          rescue Object => e
            puts "Error destrotying snapshot #{e.message}"
          end
        end
      end
      delete
    end

    def ec2_ids
      devs.values
    end

    def ec2_data
      @base.ec2_snapshots.select { |s| ec2_ids.include? s[:aws_id] }
    end

    def completed?
      not ec2_data.detect { |s| s[:aws_status] != "completed" }
    end

    def progress
      "#{(ec2_data.inject(0) { |sum,a| sum + a[:aws_progress].to_i } / ec2_data.size).to_i}%"
    end

    def size(snap_id)
      @base.ec2_snapshots.detect { |s| s[:aws_id] == snap_id }
    end

    def version_desc
      group.version_desc(version)
    end

    def group
      @group ||= @base.groups.detect { |g| g.name == group_name }
    end

    def fetch_state
      @base.sdb.get_attributes(@base.snapshot_domain, name)[:attributes]
    end
  end
end