# prime/engine/engine-network.rb
# $Id: engine-network.rb,v 1.2 2005/03/07 07:51:32 komatsu Exp $
#
# Copyright (C) 2005 Hiroyuki Komatsu <komatsu@taiyaki.org>
#     All rights reserved.
#     This is free software with ABSOLUTELY NO WARRANTY.
#
# You can redistribute it and/or modify it under the terms of 
# the GNU General Public License version 2.
#

require 'prime/engine/engine'
require 'net/telnet'

$engine_class_name = 'PrimeEngineNetwork'

class PrimeEngineNetwork < PrimeEngine
  NetworkServer = Struct.new(:server, :options)

  def initialize()
    super

    @name = "Network engine"
    @id   = "network"
    @description = "An engine to look up words via network."
    initialize_server()
  end

  def initialize_server ()
    @servers = []

    PRIME_ENV['engine_network_servers'].each { | server_uri |
      ( hostname, port, options ) = parse_server_uri( server_uri )
      unless options.has_key?("search") then
        options["search"] = "true"
      end
      begin
        server = Net::Telnet.new("Host" => hostname,
                                 "Port" => port,
                                 "Telnetmode" => false,
                                 "Prompt" => /^\n$/)
        @servers.push( NetworkServer.new( server, options ) )
      rescue
        $stderr.puts("PRIME Warning: Connection failure" +
                       " with the remote server #{hostname}:#{port}")
      end
    }
  end

  def close
    @servers.each { | server_data |
      server_data.server.cmd("close")
    }
    return true
  end

  def search ( query )
    words = PrimeWordList.new()
    if query.input == [""] then
      return words
    end

    ## FIXME: Make new protocols instead of existing commands.
    ## FIXME: (2005-02-26) <Hiroyuki Komatsu>
    case query.method
    when :prefix then
      command = "lookup_prefix"
    when :exact then
      command = "lookup_exact"
    else
      return words
    end

    @servers.each { | server_data |
      if server_data.options["search"] != "true" then
        next
      end

      query.input.each { | string |
        results = server_data.server.cmd( "#{command}\t#{string}" )
        word_list = results.split("\n")
        ## The first line of word_list means the result flag.
        flag_success = word_list.shift()
        if flag_success != "ok" then
          next
        end
        word_list.each { | word_string |
          words << parse_word( word_string )
        }
      }
    }
    return words
  end

  def learn_word (pron, literal, pos, context, suffix, rest)
    if $PRIME_NO_SAVE then
      return true
    end
    command =
      "learn_word\t" + [pron, literal, pos, context, suffix, rest].join("\t")

    @servers.each { | server_data |
      if server_data.options["learning"] != "true" then
        next
      end
      server_data.server.cmd( command )
    }
    return true
  end

#   def get_pos_data ( string )
#   end

  private
  ## parse_server_uri ( "127.0.0.1:1180?learning=on&foo=bar" )
  ## => ["127.0.0.1", "1180", { "learning" => "on", "foo" => "bar" } ]
  def parse_server_uri ( server_uri )
    (host_uri, options_uri) = server_uri.split("?")
    (hostname, port) = host_uri.split(":")
    port = (port or 1180)
    options = {}
    if options_uri then
      options_uri.split("&").each { | option_uri |
        (key, value) = option_uri.split("=")
        options[key] = value
      }
    end
    return [hostname, port, options]
  end

  ## This parses a string line generated by lookup_all.
  def parse_word ( word_string )
    ## arg1 and arg2 reflect the reading and the literal of a whole string.
    (arg1, arg2, *data_list) = word_string.split("\t")
    data = {}
    data_list.each { | item |
      (key, value) = item.split("=")
      data[key] = value
    }

    if data.has_key?("base") then
      literal = data["base"]
      data.delete("base")
    end
    if data.has_key?("basekey") then
      reading = data["basekey"]
      data.delete("basekey")
    end
    if data.has_key?("part") then
      pos = data["part"]
      data.delete("part")
    end
    if data.has_key?("priority") then
      score = data["priority"]
      data.delete("priority")
    end
    if data.has_key?("conjugation") then
      conjugation = data["conjugation"]
      data.delete("conjugation")
    end
    if data.has_key?("rest") then
      rest = data["rest"]
      data.delete("rest")
    end
    return PrimeWord.new(reading, literal, pos, score, data)
  end
end
