#!/usr/bin/env ruby
# $Id: rwmuim,v 1.15 2006/09/14 13:42:03 masahino Exp $

require 'pp'
require 'iconv'
require 'optparse'

require 'uim'
if UIM::VERSION < '0.1.1'
  "#{$0} require uim >= 0.1.1"
  exit
end

require 'dockapp'
if DockApp::VERSION < '0.2.3'
  "#{$0} require ruby-dockapp >= 0.2.3"
  exit
end

$KCODE='e'

$uim_im_switcher="uim-im-switcher-gtk"
$uim_pref="uim-pref-gtk"
$uim_dict="uim-dict-gtk"
$version="0.3.0"


class Iconv
  def self.toeuc (charset, str)
    return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # us-ascii
    begin
      return Iconv.conv("euc-jp", charset, str)
    rescue Iconv::IllegalSequence
      return str
    end
    raise 'unable to convert to EUC-JP'
  end
end

class PropList
  attr_accessor :charset
end

class UIMHelper
  UIMIcon_dir = '/usr/share/uim/pixmaps'
  # ʬ
  def initialize(argv)
    @uim_helper = UIM::Helper.new
    @uim_helper.init_client
    @uim_helper.im_list_get
    @uim_helper.get_prop_list

    @input_mode_list = Array.new
    @input_method_list = Array.new
    @im_list = Array.new

    create_toolbar()
    set_timer()
  end

  def set_timer
    input_mode_timer = DockApp::Timer.new(100) do 
      @uim_helper.read(1) do |msg|
        message_array = msg.split("\n")
        check_message(message_array)
      end
    end

    im_list_timer = DockApp::Timer.new(3000) do
      if @uim_helper.writable
        @uim_helper.im_list_get
      end
    end
    input_mode_timer.start
    #    im_list_timer.start
  end

  def im_change(im)
    #    @uim_helper.send_message("im_change_whole_desktop\n"+im+"\n")
    @uim_helper.im_change(UIM::Helper::WHOLE_DESKTOP, im)
    @uim_helper.im_list_get
  end

  def check_im_list
    @uim_helper.im_list_get
  end
  
  def parse_message(message_array)
    charset = message_array.shift
    dummy, charset = charset.split("=")
    return charset, message_array
  end    

  def branch_to_array(message_array)
    result = Array.new
    while message_array
      if message_array[0] =~ %r|^branch| or message_array.size == 0
        break
      end
      prop = Hash.new
      dummy, prop[:indication_id], prop[:iconic_label], prop[:label_string], prop[:short_desc], prop[:action_id], prop[:activity] =
        message_array.shift.split("\t")
      result.push(prop)
    end
    return result
  end

  def im_list_update(message_array)
    charset, message_array = parse_message(message_array)
    #iminfo = imname "\t" imlang "\t" imshort_description "\t" selectedflag"\n"
    update = Hash.new
    @im_list = Array.new
    message_array.each do |d|
      im = Hash.new
      im[:imname], im[:imlang], im[:imshort_description], im[:selected] =
        d.split("\t")
      @im_list.push(im)
      if im[:selected] == 'selected'
        update = im
      end
    end
    update_im(@im_list, update)
  end

  def prop_list_update(message_array)
    # 1st IM
    charset, message_array = parse_message(message_array)
    branch = message_array.shift
    branch = Iconv.toeuc(charset, branch)
    while message_array
      if message_array[0] =~ %r|^branch| or message_array.size == 0
        break
      end
      message_array.shift.split("\t")
    end

    if message_array.size == 0
      return
    end
    # 2nd input_mode
    branch = message_array.shift
    branch = Iconv.toeuc(charset, branch)
    update = Hash.new
    dummy, update[:indication_id], update[:iconic_label], update[:label_string] = branch.split("\t")
    message_array.map! {|str| str = Iconv.toeuc(charset, str)}
    @input_mode_list = branch_to_array(message_array)

    update_input_mode(@input_mode_list, update)
    
    if message_array.size == 0
      return
    end
    # 3rd Input_Method
    branch = message_array.shift
    branch = Iconv.toeuc(charset, branch)
    update = Hash.new
    dummy, update[:indication_id], update[:iconic_label], update[:label_string] = branch.split("\t")

    @input_method_list = branch_to_array(message_array)
    update_input_method(@input_method_list, update)
  end

  def check_message(message_array)
    puts message_array[0] if $DEBUG
    if message_array[0] == "prop_list_update"
      prop_list_update(message_array[1..-1])
      check_im_list
    elsif message_array[0] == "prop_activate"
      puts "prop_activate" if $DEBUG
    elsif message_array[0] == "im_list"
      im_list_update(message_array[1..-1])
    elsif message_array[0] == "focus_out"
      # not implement
    elsif message_array[0] == "focus_in"
      # not implement
    elsif message_array[0] == "im_list_get"
      puts "im_list_get" if $DEBUG
    else
      puts "not yet implement" if $DEBUG
      puts message_array[0] if $DEBUG
    end
  end
  def show_version
    puts $version
    exit
  end


  def create_toolbar
  end

  def update_input_mode(input_mode_list, update)
  end

  def update_input_method(input_method_list, update)
  end

  def update_im(im_list, update)
  end

  def run_config
  end

  def start
  end
end

class WmUIMHelper < UIMHelper
  # ʲϲ̴ط
  def create_toolbar
    @dockapp = DockApp.new("WMUIM")
    @dockapp.openwindow
    @input_mode = DockApp::Item.new(56, 16)
    @description = DockApp::Item.new(56, 16)
    @imname = DockApp::Text.new("", 6, 1, 0)
    @input_method = DockApp::Item.new(14, 14)

    @menu_popup = DockApp::PopUp.new(1, 1)
    @im_popup = DockApp::PopUp.new(1, 1)

    @dockapp.add( 0,  0, @input_mode)
    @dockapp.add( 0, 20, @description)
    @dockapp.add(16, 40, @imname)
    @dockapp.add( 0, 40, @input_method)
    @dockapp.add( 0,  0, @menu_popup)
    @dockapp.add( 0,  0, @im_popup)

    @menu_popup.add_item(["preference", "dictonary", "IM switch"])
    @menu_command = [$uim_pref, $uim_dict, $uim_im_switcher]
    set_button_callback
  end

  def set_button_callback
    @input_mode.signal_connect("button_press_event") do |event|
      if event.button == 1
        low = 2
        high = 2
        puts "event.x = "+event.x.to_s if $DEBUG
        for i in 0..@input_mode_list.size-1
          high += @dockapp.text_width(@input_mode_list[i][:iconic_label])
          puts "high = "+high.to_s if $DEBUG
          if low <= event.x && event.x <= high
            @uim_helper.prop_activate(@input_mode_list[i][:action_id])
            break
          end
          low = high
        end
      end
    end
    
    @description.signal_connect("button_press_event") do |event|
      #      if event.button == 1
      case event.button
      when 1
        # now selected 
        for i in 0..@input_mode_list.size-1
          if @input_mode_list[i][:activity] == "*"
            n = i+1
            if n > @input_mode_list.size-1
              n = 0
            end
            @uim_helper.prop_activate(@input_mode_list[n][:action_id])
            break
          end
        end
      else
      end
    end
    
    @imname.signal_connect("button_press_event") do |event|
      case event.button
      when 1
        ## todo
        #        system($uim_im_switcher)
        #      when 2
        @im_popup.popup() do |n|
          if n != nil
            im = @im_list[n][:imname]
            self.im_change(im)
          end
        end
      else
      end
    end

    @input_method.signal_connect("button_press_event") do |event|
      if event.button == 1
        for i in 0..@input_method_list.size-1
          if @input_method_list[i][:activity] == "*"
            n = i+1
            if n > @input_method_list.size-1
              n = 0
            end
            @uim_helper.prop_activate(@input_method_list[n][:action_id])
            break
          end
        end
      end
    end

    @dockapp.signal_connect("button_press_event") do |event|
      if event.button == 3
        @menu_popup.popup() do |n|
          if n != nil and 0 <= n and n <= 2
            fork do
              exec(@menu_command[n])
            end
          end
        end
      end
    end
  end

  def update_input_mode(input_mode_list, update)
    p update if $DEBUG
    @input_mode.clear
    x = 2
    for i in 0..input_mode_list.size-1
      text = input_mode_list[i][:iconic_label]
      if input_mode_list[i][:iconic_label] == update[:iconic_label]
        @input_mode.draw_string(x, 12, text)
        @input_mode.set_tip(input_mode_list[i][:short_desc])
        @description.clear
        @description.draw_string(3, 12, input_mode_list[i][:label_string][0..8])
        @description.set_tip(input_mode_list[i][:label_string])
      else
        @input_mode.draw_string(x, 12, text, "#000049244103")
      end
      x += @dockapp.text_width(text)
    end
  end

  def update_input_method(input_method_list, update)
    @input_method.clear
    @input_method.draw_string(1, 11, update[:iconic_label])
    @input_method.set_tip(update[:label_string])
  end

  def update_im(im_list, update)
    im_a = []
    im_list.each do |im|
      im_a.push(im[:imname])
    end
    @imname.set_tip(update[:imshort_description])
    @im_popup.add_item(im_a)
    @imname.set_text(update[:imname])
  end


  def start
    puts "start" if $DEBUG
    @dockapp.start
  end


end

class WmUIMHelper2 < UIMHelper
  # ʲϲ̴ط
  def create_toolbar
    @dockapp = DockApp.new("WMUIM")
    @dockapp.openwindow

    @description = DockApp::Item.new(56, 17)
    @imicon = DockApp::Item.new(16, 16, DockApp::Item::Box, DockApp::Item::Button)
    @imname = DockApp::Item.new(36, 16)
    @input_method = DockApp::Item.new(56, 16)

    @menu_popup = DockApp::PopUp.new(1, 1)
    @im_popup = DockApp::PopUp.new(1, 1)
    @dockapp.add( 0, 20, @description)
    @dockapp.add( 1,  1, @imicon)
    @dockapp.add(20,  0, @imname)
    @dockapp.add( 0, 40, @input_method)
    @dockapp.add( 0,  0, @menu_popup)
    @dockapp.add( 0,  0, @im_popup)

    @menu_popup.add_item(["preference", "dictonary", "IM switch"])

    @menu_command = [$uim_pref, $uim_dict, $uim_im_switcher]
    set_button_callback
  end

  def set_button_callback
    @description.signal_connect("button_press_event") do |event|
      #      if event.button == 1
      case event.button
      when 1
        # now selected 
        for i in 0..@input_mode_list.size-1
          if @input_mode_list[i][:activity] == "*"
            n = i+1
            if n > @input_mode_list.size-1
              n = 0
            end
            @uim_helper.prop_activate(@input_mode_list[n][:action_id])
            break
          end
        end
      else
      end
    end
    
    @imname.signal_connect("button_press_event") do |event|
      case event.button
      when 1
        ## todo
        #        system($uim_im_switcher)
        #      when 2
        @im_popup.popup() do |n|
          if n != nil
            im = @im_list[n][:imname]
            self.im_change(im)
          end
        end
      else
      end
    end

    @input_method.signal_connect("button_press_event") do |event|
      if event.button == 1
        for i in 0..@input_method_list.size-1
          if @input_method_list[i][:activity] == "*"
            n = i+1
            if n > @input_method_list.size-1
              n = 0
            end
            @uim_helper.prop_activate(@input_method_list[n][:action_id])
            break
          end
        end
      end
    end

    @dockapp.signal_connect("button_press_event") do |event|
      if event.button == 3
        @menu_popup.popup() do |n|
          if n != nil and 0 <= n and n <= 2
            fork do
              exec(@menu_command[n])
            end
          end
        end
      end
    end
  end

  def update_input_mode(input_mode_list, update)
    p update if $DEBUG
    x = 2
    for i in 0..input_mode_list.size-1
      text = input_mode_list[i][:iconic_label]
      if input_mode_list[i][:iconic_label] == update[:iconic_label]
        @description.clear
        @description.draw_string(3, 12, input_mode_list[i][:label_string][0..8])
        @description.set_tip(input_mode_list[i][:label_string])
      end
      x += @dockapp.text_width(text)
    end
  end

  def update_input_method(input_method_list, update)
    @input_method.clear
    @input_method.draw_string(1, 11, update[:label_string])
    @input_method.set_tip(update[:label_string])
  end

  def update_im(im_list, update)
    im_a = []
    im_list.each do |im|
      im_a.push(im[:imname])
    end
    @imname.set_tip(update[:imshort_description])
    @im_popup.add_item(im_a)
    @imicon.draw_rect(2, 2, 12, 12, "#AEAAAE")
    @imname.clear()
    icon_filename = UIMIcon_dir+"/"+update[:imname]+".png"
    @imicon.set_image_at_size(icon_filename, 0, 0, 12, 12)
    @imname.draw_string(2, 12, update[:imname][0..4])
  end


  def start
    puts "start" if $DEBUG
    @dockapp.start
  end
end

class WmUIMHelperIcon < WmUIMHelper
  ButtonSize = 18
  InputStateSize = 27
  def create_toolbar
    # 
    @dockapp = DockApp.new("WMUIM")
    @dockapp.openwindow

    @input_mode_item = DockApp::Item.new(InputStateSize, InputStateSize,
                                         DockApp::Item::Box,
                                         DockApp::Item::Button)
    @input_method_item = DockApp::Item.new(InputStateSize, InputStateSize,
                                         DockApp::Item::Box,
                                         DockApp::Item::Button)
    @im_item = DockApp::Item.new(ButtonSize, ButtonSize, DockApp::Item::Box,
                                 DockApp::Item::Button)
    @config_item = DockApp::Item.new(ButtonSize, ButtonSize,
                                     DockApp::Item::Box,
                                     DockApp::Item::Button)
    @im_item = DockApp::Item.new(ButtonSize, ButtonSize, DockApp::Item::Box,
                                 DockApp::Item::Button)
    @dict_item = DockApp::Item.new(ButtonSize, ButtonSize, DockApp::Item::Box,
                                   DockApp::Item::Button)

    @im_popup = DockApp::PopUp.new(1, 1, DockApp::PopUp::Text)
    @input_method_popup = DockApp::PopUp.new(1, 1, DockApp::PopUp::Text)
    @input_mode_popup = DockApp::PopUp.new(1, 1, DockApp::PopUp::Text)

    @dockapp.add( 0,  4, @input_mode_item)
    @dockapp.add(29,  4, @input_method_item)
    @dockapp.add( 0, 37, @im_item)
    @dockapp.add(19, 37, @config_item)
    @dockapp.add(38, 37, @dict_item)
    @config_item.set_image_at_size(UIMIcon_dir+"/uim-icon.png", 0, 0,
                                   ButtonSize-4, ButtonSize-4)
    @dict_item.set_image_at_size(UIMIcon_dir+"/uim-dict.png", 0, 0,
                                   ButtonSize-4, ButtonSize-4)
    @dockapp.add( 0,  0, @im_popup)
    @dockapp.add( 0,  0, @input_method_popup)
    @dockapp.add( 0,  0, @input_mode_popup)

    @config_item.set_tip("ġ뵯ư")
    @dict_item.set_tip("ġ뵯ư")
    #    @menu_popup.add_item(["preference", "dictonary", "IM switch"])
    #    @menu_command = [$uim_pref, $uim_dict, $uim_im_switcher]
    #    set_timer
    set_button_callback
  end

  def set_button_callback
    @input_method_item.signal_connect("button_press_event") do |event|
      case event.button
      when 1
        @input_method_popup.popup() do |n|
          pp n
          if n != nil && n != false
            @uim_helper.prop_activate(@input_method_list[n][:action_id])
          end
        end
      else
      end
    end

    @input_mode_item.signal_connect("button_press_event") do |event|
      case event.button
      when 1
        @input_mode_popup.popup() do |n|
          pp n
          if n != nil && n != false
            @uim_helper.prop_activate(@input_mode_list[n][:action_id])
          end
        end
      else
      end
    end

    @im_item.signal_connect("button_press_event") do |event|
      case event.button
      when 1
        @im_popup.popup() do |n|
          pp n
          if n != nil && n != false
            im = @im_list[n][:imname]
            self.im_change(im)
          end
        end
      else
      end
    end

  end

  def update_input_mode(input_mode_list, update)
    icon_filename = UIMIcon_dir+"/"+update[:indication_id]+".png"
    @input_mode_item.draw_rect(2, 2,
                               InputStateSize-4, InputStateSize-4, "#AEAAAE")
    @input_mode_item.set_image_at_size(icon_filename, 1, 1,
                                       InputStateSize-6, InputStateSize-6)
    @input_mode_item.set_tip(update[:label_string])
    a = []
    input_mode_list.each do |m|
      a.push(m[:label_string])
    end
    @input_mode_popup.add_item(a)
  end

  def update_input_method(input_method_list, update)
    icon_filename = UIMIcon_dir+"/"+update[:indication_id]+".png"
    @input_method_item.draw_rect(2, 2,
                               InputStateSize-4, InputStateSize-4, "#AEAAAE")
    @input_method_item.set_image_at_size(icon_filename, 1, 1,
                                       InputStateSize-6, InputStateSize-6)
    @input_method_item.set_tip(update[:label_string])
    a = []
    input_method_list.each do |m|
      a.push(m[:label_string])
    end
    @input_method_popup.add_item(a)
  end

  def update_im(im_list, update)
    icon_filename = UIMIcon_dir+"/"+update[:imname]+".png"
    @im_item.draw_rect(2, 2, ButtonSize-4, ButtonSize-4, "#AEAAAE")
    @im_item.set_image_at_size(icon_filename, 0, 0, ButtonSize-4, ButtonSize-4)
    @im_item.set_tip(update[:imshort_description])
    im_a = []
    im_list.each do |im|
      im_a.push(im[:imname])
    end
    @im_popup.add_item(im_a)
  end
end

style = 'Normal'
opt = OptionParser.new
opt.on('-v') {|v| show_version }
opt.on('-s [normal|icon|text]') {|v| style = v}
opt.parse!(ARGV)

puts style
# ץɽˡѤ롣
case style
when 'icon'
  wmuim = WmUIMHelperIcon.new(ARGV)
when 'text'
  wmuim = WmUIMHelper2.new(ARGV)
else
  wmuim = WmUIMHelper.new(ARGV)
end  
wmuim.start
