# $Id: sharedance.rb 867 2007-01-22 18:29:18Z j $

class Sharedance
  require 'socket'
  require 'timeout'

  SHAREDANCE_DEFAULT_PORT = 1042
  SHAREDANCE_DEFAULT_TIMEOUT = 10
  attr_accessor :host, :port, :timeout

  def initialize(params)
    @host = params[:host]
    @port = params[:port] || SHAREDANCE_DEFAULT_PORT
    @timeout = params[:timeout] || SHAREDANCE_DEFAULT_TIMEOUT
  end

  def self.open(params)
    yield(self.new(params))
  end

  def fetch(params)
    result = sharedance_command(
      "F" + params[:key].encode_size_for_sharedance + params[:key])
    result.empty? ? nil : result
  end
  
  def store(params)
    raise "Storage failed" unless sharedance_command(
      "S" + params[:key].encode_size_for_sharedance +
            params[:data].encode_size_for_sharedance +
            params[:key] + params[:data]) === "OK\n"
  end
  
  def delete(params)
    raise "Deletion failed" unless sharedance_command(
      "D" + params[:key].encode_size_for_sharedance + params[:key]) === "OK\n"
  end
  
  def [](key)
    fetch(:key => key)
  end
  
  def []=(key, data)
    data.nil? ? delete(:key => key) : store(:key => key, :data => data)
  end
  
protected
  @sock = nil

  def connect_to_server
    @sock = TCPSocket.new(@host, @port) unless @sock
  end

  def close_from_server
    @sock.close if @sock
    @sock = nil
  end

  def sharedance_command(command)
    result = nil  
    begin
      Timeout.timeout(@timeout) do
        connect_to_server
        @sock << command
        result = @sock.read
      end
    ensure
      close_from_server
    end
    result    
  end
end

class String
  def encode_size_for_sharedance
    raise "Overflow" if (size = self.size) > 0xffffffff
    [(size >> 24) & 0xff, (size >> 16) & 0xff,
     (size >>  8) & 0xff, size & 0xff].pack("cccc")
  end
end
