#!/opt/QtPalmtop/bin/ruby

=begin

--------------------------------------------------

 RQPosition for Linux Zaurus

 Version 2007.03.10

 Written by moyashi (komugi) [ http://moyashi.air-nifty.com/ ]

--------------------------------------------------

 Description:
 
 ruby-serialportを使ってPHSカードから位置情報を得て
 とりあえずマピオンで現在位置を表示するスクリプト
 接続を切ったりなんだりは全自動のはず

=end

#require 'datum_conv'
#require 'position'
require 'serialport'

module Phs

  PORT = "/dev/ttyS3"
  ConfDir = "/home/zaurus/Applications/Network/modules/"

  DEBUG = true

  SSleep = 0.5
  LSleep = 1

  STimeout = 15
  LTimeout = 30

  @sp = nil
  @verbose = true

  # function: 2 (serial)
  # function: 6 (network)

  # 位置情報格納のためのオブジェクト
  LatLon = Struct.new(:pc,:n1,:n2,:n3,:e1,:e2,:e3)

  def verbose=(flag)
    @verbose = flag
  end

  def verbose
    return @verbose
  end

  # PHSは存在するか?
  def exists?
    puts "Phs.exists?" if DEBUG
    if (`cardctl ident 0` =~ /function: 2 \(serial\)/)
      return true
    else
      return false
    end
  end

  # PHSの状態がBUSYか?
  def busy?
    puts "Phs.busy?" if DEBUG
    if (`cardctl status 0` =~ /busy/)
      puts "Phs.busy? => true" if DEBUG
      return true
    else
      puts "Phs.busy? => false" if DEBUG
      return false
    end
  end

  # PHSはSUSPENDしているか?
  def suspend?
    puts "Phs.suspend?" if DEBUG
    if (`cardctl status 0` =~ /suspend/)
      return true
    else
      return false
    end
  end

  # PPP中か?
  def online?
    puts "Phs.online?" if DEBUG
    if (`netstat -i` =~ /ppp0/)
      return true
    else
      return false
    end    
  end

  # PHSがSUSPENDするまでの待ち時間を作る
  def wait_for_suspend
    timeout = LTimeout
    puts "Phs.wait_for_suspend" if DEBUG
    while(busy?)
      sleep(SSleep)
      if timeout == 0
        mes("Phs.wait_for_suspend timeout.") if @verbose
        close
        return false
      end
      timeout -= 1
    end
    return true
  end

  # PHSがBUSY状態になるまでの待ち時間を作る
  def wait_for_busy
    timeout = LTimeout
    puts "Phs.wait_for_busy" if DEBUG
    while(suspend?)
      sleep(SSleep)
      if timeout == 0
        mes("Phs.wait_for_busy timeout.") if @verbose
        close
        return false
      end
      timeout -= 1
    end
    return true
  end

  # ダイヤルアップを開始する
  # 初期値は "DialUp.conf"
  def connect(conf = "DialUp.conf")
    if (not File.exists?(ConfDir + conf)) then
      puts "ERROR: Specified *.conf file not exists."
      close
      return false
    end
    
    mes('PPP connecting...') if @verbose
    system("qcop QPE/Network 'start(QString,QString)' " + 
           ConfDir + conf + ">/dev/null 2>&1")
    timeout = LTimeout
    while (!online?)
      sleep(LSleep)
      mes('Wait for connect...') if @verbose
      timeout -= 1
      if (timeout == 0) || (!exists?)
        mes("Phs.connect timeout.") if @verbose
        close
        return false
      end
    end
    system("qcop QPE/Network 'up()' >/dev/null 2>&1")
    mes('Connection established.') if @verbose
    return true
  end

  # ネットワーク接続を解除する
  def disconnect
    timeout = STimeout
    if (online? && exists?) then
      puts "Phs.disconnect" if DEBUG
      begin
        mes('PPP disconnecting...') if @verbose
        system("qcop QPE/Network 'stop()' >/dev/null 2>&1")
        while (online?)
          mes("Wait for disconnect...") if @verbose
          sleep(LSleep)
          if ((timeout == 0) || (!exists?)) then
            raise
          end
          timeout -= 1
        end
        # PHSカードがbusyじゃなくなるまで待つ
        wait_for_suspend
      rescue
        mes("Phs.disconnect timeout.") if @verbose
        close
        return false
      end
      return true
    end
    return true
  end

  # PHSカードを起こし、BUSYになるまで待つ
  def resume
    timeout = LTimeout
    puts "Phs.resume" if DEBUG

    if (suspend?)
      system("cardctl resume 0")
    end

    while (suspend?)
      sleep(SSleep)
      if (timeout == 0)
        mes("Phs.resume timeout.") if @verbose
        close
        return false
      end
      timeout -= 1
    end
    if (wait_for_busy)
      return true
    else
      return false
    end
  end

  # PHSカードをSUSPENDさせ
  # SUSPEND完了するまで待つ
  def suspend
    timeout = LTimeout
    puts "Phs.suspend" if DEBUG

    if (busy?)
      system("cardctl suspend 0")
    end

    while (busy?)
      sleep(SSleep)
      if (timeout == 0)
        mes("Phs.suspend timeout.") if @verbose
        close
        return false
      end
      timeout -= 1
    end
    puts "Phs.suspend finish." if DEBUG
    return true
  end

  # シリアルポートを開く(PHSの利用を開始する)
  def open
    puts "Phs.open" if DEBUG
    begin
      @sp = SerialPort.new(PORT, 1200, 8, 1, SerialPort::NONE)
      @sp.flow_control = (SerialPort::HARD | SerialPort::SOFT)
      # SerialPort::NONE, SerialPort::HARD, SerialPort::SOFT
      # or (SerialPort::HARD | SerialPort::SOFT)
      #puts @sp.closed?
      #puts @sp.read_timeout
    rescue
      mes("Phs.open failed.") if @verbose
      return false
    end
    return true
  end

  # シリアルポートを閉じて(PHSの使用を終了して)
  # SUSPENDするまで待つ
  def close
    puts "Phs.close" if DEBUG
    if (@sp != nil)
      if (!@sp.closed?)
        @sp.close
      end
      while (busy?)
        if(!suspend)
          return false
        end
      end
      return true
    end
  end

  # 位置情報取得のための初期化
  def init
    mes("Port Initializing...") if @verbose
    timeout = LTimeout
    puts "Phs.init" if DEBUG
    @sp.puts "AT@LBC1\r"
    while (res = @sp.gets)
      puts res if DEBUG
      if (res =~ /OK/)
        break
      end
      if (res =~ /OK/ || timeout == 0)
        mes("Phs.init timeout.") if @verbose
        close
        return false
      end
      timeout -= 1
    end
    return true
  end

  # 位置情報の取得
  def position_info(output = 0)
    return false if not exists?
    if (busy? and not online?)
      result = suspend
    else
      result = true
    end
    if(result)
      if(online?)
        result = disconnect
      else
        result = true
      end
      if(result)
        if(resume)
          if(open)
            if(init)
              timeout1 = 1000
              timeout2 = STimeout
              mes("Getting position info...") if @verbose
              puts "Phs.position_info" if DEBUG
              cnt = 0
              #pos = Position.new
              #postal code初期値
              pc = "0000000"

              while true
                buf = res = ""
                @sp.puts "AT@LBC?\r"
                if (timeout1 == 0)
                  mes("Phs.position_info timeout.") if @verbose
                  close
                  return false
                end

                while (res = @sp.gets)
                  puts cnt += 1 if DEBUG
                  puts res if DEBUG
                  puts timeout2 if DEBUG

                  if res =~ /^\d{7}/
                    pc = $&
                  end

                  if res =~ /^N/
                    n1,n2,n3 = res.chomp.sub(/^N/,"").split(":")
                    #pos.lat = Position.dms2deg(n1, n2, n3)
                  end

                  if res =~ /^E/
                    if(close)
                      e1,e2,e3 = res.chomp.sub(/^E/,"").split(":")
                      latlon = LatLon.new(pc,n1,n2,n3,e1,e2,e3)
                      puts latlon if DEBUG
                      #pos.lon = Position.dms2deg(e1, e2, e3)
                      #pos.tokyo2wgs84!
                      #puts pos.to_s
                      mes("Successed!!") if @verbose
                      return latlon
                    else
                      return false
                    end
                  end

                  if (res =~ /OK/ || timeout2 == 0)
                    break
                  end
                  timeout2 -= 1
                end
                timeout1 -= 1
              end
            else
              return false
            end
          else
            return false
          end
        else
          return false
        end
      else
        return false
      end
    else
      return false
    end
  end

  # タスクバーにメッセージを表示
  def mes(str)
    system("qcop QPE/TaskBar 'message(QString)' '" + str +
           "' >/dev/null 2>&1")
  end
  
  module_function :busy?, :suspend?, :exists?, :mes
  module_function :wait_for_busy, :wait_for_suspend
  module_function :resume, :suspend, :online?, :connect, :disconnect
  module_function :open, :close, :init, :position_info
  module_function :verbose, :verbose=

end

def usage
  puts ""
  puts "*.conf file locate at '" + Phs::ConfDir + "'."
  puts ""
  puts "USAGE: rqposition.rb some.conf"
  puts ""
end

def main
  if $0 == __FILE__ then
    unless (ARGV[0]) then
      puts ""
      puts "ERROR: PPP conf file must be specified."
      usage()
      exit
    else
      if (File.exists?(Phs::ConfDir + ARGV[0])) then

        # タスクバーへの状況表示
        # デフォルトは表示する
        Phs.verbose = true

        # 位置情報を取得
        p = Phs.position_info

        # 位置情報が取得失敗した場合はfalseが返るので
        # 事前処理が必要
        if(p == false)
          Phs.mes("Failed get position info.")
          exit
        end

        # URLを生成 とりあえず日本測地系で、2桁ごとにバラしてます
        url = "http://www.mapion.co.jp/c/f?uc=1&nl=" +
          "#{p.n1}/#{p.n2}/#{p.n3}&el=#{p.e1}/#{p.e2}/#{p.e3}" +
          "&scl=10000&size=665,460&grp=Air"

        # 郵便番号(postal code)の取得(取得できなかった場合は"0000000"が返る
        #puts "postal code: " + p.pc

        # ダイヤルアップ開始
        # 引き数無しの場合は"DialUp.conf"になります
        # /home/zaurus/Applications/Network/modules/ にあるダイヤルアップの
        # confを指定します
        if(Phs.connect(ARGV[0]))

          # ブラウザで表示
          system("qcop QPE/System 'execute(QString,QString)' " +
                 "netfront3 '#{url}'" )
        end
      else
        puts "ERROR: Specified *.conf file not exists."
        usage()
      end
    end
  end
end

main()