#!/usr/bin/env ruby

require "qte"
require "qpe"
require "thread"
include Qte
include Qpe

$version = "0.1.1"
$shoutcast = "http://www.shoutcast.com/"

$workdir = "/home/zaurus/Applications/shoutcastplayer"
if !File.exist?("#{$workdir}")
   Dir.mkdir("#{$workdir}")
end
$tmpfile = "/home/zaurus/Applications/shoutcastplayer/shoutcast.tmp"
$tmpfile2 = "/home/zaurus/Applications/shoutcastplayer/shoutcast2.tmp"
$tmpfile3 = "/home/zaurus/Applications/shoutcastplayer/shoutcast3.tmp"
$sleepfile = "/home/zaurus/Applications/shoutcastplayer/sleepfile.sh"
# $atsleep = "/home/zaurus/Applications/shoutcastplayer/atsleepfile.sh"


#  Configure File
$conffile = "/home/zaurus/Settings/shoutcastplayer.conf"
$conf = Hash.new
#  各初期値
$conf['cache'] = '350'     # mplayerキャッシュサイズ
$conf['pdelay'] = '2'      # plsファイル取得から再生までのディレイ
$conf['sleep'] = 'no'      # スリープタイマー無効
$conf['sleeptime'] = '5'   # スリープまでの時間（分）
$conf['atsleepfile'] = ''  # スリープ時atdに登録するファイル名（除く拡張子）
$conf['autoplay'] = 'no'   # 起動後自動再生しない


class ShoutcastPlayer < QMainWindow
   def initialize()
      super()
      setCaption(tr("SHOUTcast Player Ver#{$version}"))

      # ジャンル
      @lab1 = QLabel.new("Genre", self)
      @lab1.setGeometry(0,0,60,35)
      @lab1.setAlignment(AlignRight | AlignVCenter)
      @cb1 = QComboBox.new( self )
      @cb1.setGeometry(60,0,210,35)
      connect(@cb1,QSIGNAL("activated(int)"), self, 'changeCategory')
      @cb1.insertItem("TopTen")    # 0
      @cb1.insertItem("Alternative")
      @cb1.insertItem("Classical")
      @cb1.insertItem("Comedy")
      @cb1.insertItem("Country")
      @cb1.insertItem("Dance")
      @cb1.insertItem("Funk")
      @cb1.insertItem("Jazz")
      @cb1.insertItem("Metal")
      @cb1.insertItem("Mixed") 
      @cb1.insertItem("Pop")
      @cb1.insertItem("Rap")
      @cb1.insertItem("RnB")
      @cb1.insertItem("Rock")
      @cb1.insertItem("Talk")
      @cb1.insertItem("Techno")
      @cb1.insertItem("80s")
      @cb1.insertItem("70s")
      @cb1.insertItem("World")
#     @cb1.insertItem("?s=SearchWord")  # 検索をメニュー登録する場合

      # 検索
      @lab2 = QLabel.new("Search", self)
      @lab2.setGeometry(0,35,60,35)
      @lab2.setAlignment(AlignRight | AlignVCenter)
      @eb = QLineEdit.new(self)
      @eb.setGeometry(60,35,210,35)

      # 順位200位まで
      @lab3 = QLabel.new("Rank", self)
      @lab3.setGeometry(270,0,100,35)
      @lab3.setAlignment(AlignRight | AlignVCenter)
      @cb3 = QComboBox.new( self )
      @cb3.setGeometry(370,0,150,35)
      from = 1
      to = 20
      10.times{            # 200位以降も表示する場合は10を大きく
         @cb3.insertItem("#{from} - #{to}")
         from += 20
         to += 20
      }

      # ビットレート
      @lab4 = QLabel.new("MaxBitrate", self)
      @lab4.setGeometry(270,35,100,35)
      @lab4.setAlignment(AlignRight | AlignVCenter)
      @cb4 = QComboBox.new( self )
      @cb4.setGeometry(370,35,150,35)
      @cb4.insertItem("All")       # 0
      @cb4.insertItem("128")       # 1
      @cb4.insertItem("96")        # 2
      @cb4.insertItem("80")        # 3
      @cb4.insertItem("64")        # 4
      @cb4.insertItem("56")        # 5
      @cb4.insertItem("32")        # 6

      @list = QListView.new(self)
      @list.setGeometry(0,70,640,300)
      @list.setSorting( 6, true)  # ソート対象
      @list.addColumn("rate")     # カラム追加 0
      @list.addColumn("listner")  # 1
      @list.addColumn("cate")     # 2
      @list.addColumn("Title")    # 3
      @list.addColumn("Playing")  # 4
      @list.addColumn("path")     # 5
      @list.addColumn("No.")      # 6
      @list.setAllColumnsShowFocus(true)
      @list.columns.times { |i|
         @list.setColumnWidth(i, 20)
      }
         @list.setColumnAlignment(0, AlignRight)
         @list.setColumnAlignment(1, AlignRight)
         @list.setColumnAlignment(6, AlignRight)
      connect(@list, QSIGNAL("clicked(QListViewItem *)"), self, "tapped")
      connect(@list, QSIGNAL("returnPressed(QListViewItem *)"), self, "tapped")

      @pbget = QPushButton.new(tr("情報取得"), self)
      @pbget.setGeometry(525,0,110,35)
      connect(@pbget,QSIGNAL("clicked()"), self, 'fetchInfo')
      @pbstop = QPushButton.new(tr("停止"), self)
      @pbstop.setGeometry(525,35,110,35)
      connect(@pbstop,QSIGNAL("clicked()"), self, 'stop')

      @msgsts = QLineEdit.new(tr(""), self) 
      @msgsts.setReadOnly(true)
      @msgsts.setFocusPolicy( NoFocus )
      @msgsts.setGeometry(0,370,640,35)

      loadConf       # load shoutcastplayer.conf
      setSleep

      # sleep時刻の検出および終了処理スレッド
      if $conf['sleep'] == 'yes'
         system("/home/QtPalmtop/bin/qcop QPE/System 'setScreenSaverIntervals(int,int,int)' 0 0 0")
         Thread.new{
            Thread.pass
            loop{
               if $conf['sleep'] == 'yes' && !atfileExist?($conf['atsleepfile'])
                  system("killall mplayer")
                  system("qcop QPE/Network 'stop()'")
                  sleep 20
                  system("/home/QtPalmtop/bin/qcop QPE/System 'setScreenSaverIntervals(int,int,int)' -1 -1 -1")
                  system("qcop QPE/Application/shoutcastplayer.rb 'quit()'")
                  system("qcop QPE/Application/suspend 'raise()'")
               end
               sleep 60
               system("/home/QtPalmtop/bin/qcop QPE/System 'setScreenSaverIntervals(int,int,int)' 0 0 0")
            }
         }
      end

      autoplay
      @msgsts.setText("mplayer cache size = #{$conf['cache']}kB")

      catchEvent()
   end

   def fetchInfo
      if !online?
         @msgsts.setText(tr("!!! Offline !!!"))
         return
      end
      @list.clear
      @list.columns.times { |i|
         @list.setColumnWidth(i, 20)
      }

      path = ""
      genre, maxbitrate, rank = "", "", ""

      # ジャンル/検索  検索欄が空ならばジャンルで絞りこみ
      search = @eb.text.to_str
      if search != ""
         genre = "?s=#{search}"
      else
         genre = "directory/?sgenre=#{@cb1.currentText.to_str}"
      end

      # 順位
      if @cb3.currentItem == 0
         rank = ""
      else
         i = @cb3.currentItem * 20
         rank = "\\&startat=#{i.to_s}"   # \ は２個必要
      end

      # ビットレート
      if @cb4.currentText.to_str == "All"
         maxbitrate = ""
      else
         maxbitrate = "\\&maxbitrate=#{@cb4.currentText.to_str}"  # \ は２個必要
      end

      path = genre + maxbitrate + rank
      makeList("#{$shoutcast}#{path}")
      @msgsts.setText(tr("再生するchををタップしてください"))
   end

   def makeList(url)
       if !online?
          @msgsts.setText(tr("!!! Offline !!!"))
          return
       end
      @msgsts.setText(tr("情報取得中"))
      ret = `wget -O #{$tmpfile} #{url}`
#     system("wget -O #{$tmpfile} #{url}")
      system("killall wget")
      @msgsts.setText(tr("解析開始"))

      # bitwarp対応タグの後に改行をいれる
      f1 = File.open($tmpfile)
      f2 = File.open($tmpfile2, "w")
      while line = f1.gets
         line.chomp!
         line.gsub!(/>/, ">\n")
         f2.puts line + "\n"
      end
      f1.close
      f2.close
      f = File.open($tmpfile2)

      # ここから取得したhtmlの解析
      bline1 = ""
      while line = f.gets
         line.chomp!
         # playlist.plsを検索
         if line =~ %r|^.*a href="/(.+?playlist\.pls.+?)">.*$|
            number, listner, cate, title, playing, path, bitrate = "", "", "", "", "", "", ""
            # plsファイルへのパス
            path = $1
            # 順位
            bline1 =~ %r|^(\d+)</b>$|
            number = sprintf("%3d",$1.to_i)
            # カテゴリ
            nil while f.gets.chomp! !~ %r|^.*(\[.+?\])|
            cate = $1
            # タイトル
            nil while f.gets.chomp! !~ %r|^(.+?)</a>$|
            title = $1
            # リスナー数
            while line = f.gets 
               if line =~ %r|^\D*(\d+/\d+)</font>$|
                  break
               end
               # Now Playing:は存在しない場合があるので
               # リスナー数検出の中でチェックを行う
               if line =~ %r|^.*Now Playing:</font>|
                  f.gets.chomp! =~ %r|^\s*(.+?)</font.*$|
                  playing = $1
               end
            end
            listner = $1
            # ビットレート
            nil while f.gets.chomp! !~ %r|^\D*(\d+)</font>$|
            bitrate = $1
            # リストにitem追加
            QListViewItem.new(@list, bitrate, "[#{listner}]", cate, tr(title), playing, path, number)
         end
         # 順位らしき行を保持しておく
         if line =~ %r|</b>|
            bline1 = line
         end
      end
      f.close
      @msgsts.setText(tr("解析終了"))
      i = @list.firstChild()
      @list.setSelected(i, true)  # 一番上のitemにフォーカスをあてる
   end

   # リストをタップされた時の処理
   def tapped(item)
      system("killall mplayer")
      @msgsts.setText(tr("plsファイル取得中"))
      path = item.text(5).to_str

      # systemでwgetを行うと$tmpfile3が作成される前にmplayerが実行されることがある
      ret = `wget -O #{$tmpfile3} #{$shoutcast}/#{path}`
      # 念の為confで可変にしておく
      sleep $conf['pdelay'].to_i
      @msgsts.setText(item.text(3).to_str)
      @msgsts.home(false)
      system("mplayer -quiet -cache #{$conf['cache']} -playlist #{$tmpfile3} &")
   end

   # ジャンルを変更した時の処理
   def changeCategory(i)
      @eb.clear
   end

   # 再生停止
   def stop
      @msgsts.setText("stopped")
      system("killall wget")
      system("killall mplayer")
   end

   def autoplay
      return if $conf['autoplay'] == "no"
      @msgsts.setText("AutoPlay")
      return if !File.exist?($tmpfile3)

      3.times{
         if !online?
            system("qcop QPE/Network 'connectRequest()'")
            sleep 20
         else
            break
         end
      }
      if online?
         system("mplayer -quiet -cache #{$conf['cache']} -playlist #{$tmpfile3} &")
      end
   end

   # shoutcastplayer.conf load
   def loadConf
      if File.exist?("#{$conffile}")
         file = File.open("#{$conffile}")
         while line = file.gets
            line.chomp!
            key, value = line.split(/=/)
            $conf[key] = value
         end
      end
   end

   # shoutcastplayer.conf save
   def saveConf
      file = File.open($conffile, "w")
      $conf.each{ |key, val|
         file.puts "#{key}=#{val}"
      }
      file.close
   end


   # 終了時の処理
   def closeEvent(e)
      # kill prosess
      system("killall wget")
      system("killall mplayer")
      # delete tmpfile
#     system("rm #{$tmpfile}")
#     system("rm #{$tmpfile2}")
      # save shoutcastplayer.conf
      saveConf
#     close()
      e.accept()  #これが無いとクローズしない
   end

   # ネットに接続の有無を確認
   def online?
     i = nil
     i = `route -n | grep '^0\.0\.0\.0'`
     if i == ""
        false
        return
     end
     true
   end

   # atdに登録したファイルの有無を確認
   def atfileExist?(timestr)
      atfile = false
      re = Regexp.new(timestr)
      dir = Dir.open("/var/spool/at")
      while file = dir.read
         if file =~ re
            atfile = true
            break
         end
      end
      dir.close
      if atfile
         return true
      end
      false
   end

   # スリープ時刻ををatdに登録
   def setSleep
      if $conf['sleep'] == 'yes'
         # スクリーンセーバを無効にする
         now = Time.new
         sleep = $conf['sleeptime'].to_i
         sleep = 2 if sleep < 2
         stime = now +  sleep * 60
         strtime = stime.strftime("%Y.%m.%d-%H:%M:%S")
         $conf['atsleepfile'] = stime.to_i.to_s  # atdに登録されるファイル名（除く拡張子）
         system("su -c 'echo | at #{strtime}'")
      end
   end
end


$defaultCodec = QTextCodec.codecForName("utf8")
app = QPEApplication.new([$0]+ARGV)
app.setDefaultCodec($defaultCodec)
QApplication.setFont(QFont.new("lcfont",18))
app.showMainWidget(ShoutcastPlayer.new())
app.exec
