$KCODE = 'u'

require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'nkf'
require 'uri'
require 'timeout'
require 'yaml'
require 'optparse'
require 'net/http'

class Starbucks
  attr_accessor :storeId
  attr_accessor :name
  attr_accessor :address
  attr_accessor :lat
  attr_accessor :lng
  attr_accessor :tel
  attr_accessor :open_at
  
  def initialize(id, name, lat, lng, addr, tel, op)
    @storeId = id
    @name = name
    @lat = lat
    @lng = lng
    @address = addr
    @tel = tel
    @open_at = op
  end
  
  def open_eng
    @open_at.gsub(/\s/, "") \
      .gsub("定休日", "Reg.Holiday") \
      .gsub("不定休", "unfixed") \
      .gsub(/(曜日)|曜/, "") \
      .gsub(/(祝日)|祝/, "Holiday") \
      .gsub("〜", "-") \
      .gsub("・", "") \
      .gsub("は", ": ") \
      .gsub("ドライブスルー", "DriveThru ") \
      .gsub("/", "/ ") \
      .gsub("月", "Mon.") \
      .gsub("火", "Tue.") \
      .gsub("水", "Wed.") \
      .gsub("木", "Thu.") \
      .gsub("金", "Fri.") \
      .gsub("土", "Sat.") \
      .gsub("日", "Sun.")
  end
  
  def open_jpn
    @open_at.gsub(/\s/, "")
  end
  
  def to_s
    "%d,%8.5f,%8.5f,%s,%s" % [@storeId, @lng, @lat, @name, @address]
  end
  
  def to_poi(lang)
    case lang
    when :japanese
      "%8.5f,%8.5f,\"Starbucks %s\", \"%s\"" % [@lng, @lat, @name, open_jpn()]
    else
      "%8.5f,%8.5f,\"Starbucks\", \"%s\"" % [@lng, @lat, open_eng()]
    end
  end
  
  def to_kml(lang)
    case lang
    when :japanese
      "<Placemark>\n" + \
      "<name>Starbucks #{name}</name>\n" + \
      "<description>#{open_jpn()}</description>\n" + \
      "<Point><coordinates>#{@lng},#{@lat},0</coordinates></Point>\n" + \
      "<Style><IconStyle><Icon><href>http://www.starbucks.co.jp/common/images/p_logo.gif</href></Icon><scale>0.7</scale></IconStyle></Style>" + \
      "</Placemark>"
    else
      "<Placemark>\n" + \
      "<name>Starbucks</name>\n" + \
      "<description>#{open_eng()}</description>\n" + \
      "<Point><coordinates>#{@lng},#{@lat},0</coordinates></Point>\n" + \
      "<Style><IconStyle><Icon><href>http://www.starbucks.co.jp/common/images/p_logo.gif</href></Icon><scale>0.7</scale></IconStyle></Style>" + \
      "</Placemark>"
    end
  end
  
end

class StarbucksDownloader
  def initialize(lang, dl, verbose)
    @lang = lang
    @dl = dl
    @verbose = verbose
  end

  # Timeout時のretry付きでuriを開く
  def openURI(uri)
    retries = 5
    begin
      timeout(30){
        Hpricot.parse(NKF.nkf('-w', open(uri).read))
      }
    rescue Timeout::Error
      retries -= 1
      if retries > 0
        sleep 5 and retry
      else
        raise
      end
    end
  end

  def download_store_ids
    # 都道府県ごとにstore idを取得する
    storeIds = []
    prefs = ["北海道","青森県","岩手県","宮城県","秋田県","山形県","福島県","茨城県","栃木県","群馬県","埼玉県","千葉県","東京都","神奈川県","新潟県","富山県","石川県","福井県","山梨県","長野県","岐阜県","静岡県","愛知県","三重県","滋賀県","京都府","大阪府","兵庫県","奈良県","和歌山県","鳥取県","島根県","岡山県","広島県","山口県","徳島県","香川県","愛媛県","高知県","福岡県","佐賀県","長崎県","熊本県","大分県","宮崎県","鹿児島県","沖縄県"]
    prefs.each do |pref|
      storeIds.concat download_store_ids_by_prefecture(pref)
    end
    storeIds
  end
   
  def download_store_ids_by_prefecture(pref)
    storeIds = []
    uri = URI.escape("http://www.starbucks.co.jp/search/result_store.php?SearchString=#{NKF.nkf('-s', pref)}")
    while uri != nil do # 「次の10件」がなくなるまでidの読み取りを繰り返す
      ids, uri = download_store_ids_in_uri(uri)
      storeIds.concat ids
    end
    puts "Number of Starbucks in #{pref} : #{storeIds.size}"
    storeIds
  end
    
  # store IDの取得
  def download_store_ids_in_uri(uri)
    storeIds = []
    nxt_uri = nil
    doc = openURI(uri)
    (doc/:a).each do |a|
      nxt_uri = "http://www.starbucks.co.jp/search/result_store.php" + a[:href] if a.inner_text =~ /次の10件/
      if a[:href] =~ /storeId=\d+/ then
        storeIds << a[:href].scan(/storeId=(\d+)/).flatten[0].to_i
      end
    end
    [storeIds, nxt_uri]
  end
  
  # 改行や?などの記号を除去
  def strip(str)
    str.gsub!("?", "")
    str.gsub("\n", "/")
  end
  
  # 日本測地系(秒単位)から世界測地系へ変換
  def conv(ln, la) # 経度、緯度 (単位:度)
    lng = ln - la * 0.000046038 - ln * 0.000083043 + 0.010040;
    lat = la - la * 0.00010695 + ln * 0.000017464 + 0.0046017;
    [lng, lat]
  end
  
  # 店舗情報の取得
  def download_store(id)
    uri = "http://www.starbucks.co.jp/search/map/result.php?storeId=#{id}&lang=ja"
    doc = openURI(uri)
    html = doc.to_original_html
    lng = html.scan(/reqX\s*=\s*(\d+\.\d+)/).flatten[0].to_f # 経度(reqX)
    lat = html.scan(/reqY\s*=\s*(\d+\.\d+)/).flatten[0].to_f # 緯度(reqY)
    lng /= 3600.0 # 秒->度
    lat /= 3600.0
    lng, lat = conv(lng, lat)
    name = strip(doc.at("th/[text()*='店舗名']").parent.next_sibling.to_plain_text)
    addr = strip(doc.at("th/[text()*='住所']").parent.next_sibling.to_plain_text)
    tel = strip(doc.at("th/[text()*='電話番号']").parent.next_sibling.to_plain_text)
    op = strip(doc.at("th/[text()*='営業時間']").parent.next_sibling.to_plain_text)
    Starbucks.new(id, name, lat, lng, addr, tel, op)
  end
  
  def download_stores
    storeIds = download_store_ids
    stores = []
    storeIds.each do |id|
      begin
        s = download_store(id)
        stores << s if s != nil
      rescue => e
        puts "Error in getting store #{id}. Skip..."
      end
      puts s.to_s if @verbose
    end
    stores
  end
  
  def get_stores
    if Dir::glob("starbucks.yaml").size > 0 && !@dl then
      YAML.load_file("starbucks.yaml")
    else
      stores = download_stores
      dump_yaml(stores)
      stores
    end 
  end
  
  def dump_yaml(stores)
    YAML.dump(stores, File.open('starbucks.yaml', 'w'))
  end
  
  # POIファイルを出力
  def dump_poi
    stores = get_stores
    open("starbucks_poi.csv", 'w') do |f|
      stores.each do |s|
        f.puts NKF.nkf('-s', s.to_poi(@lang))
      end
    end
  end

  def dump_kml
    stores = get_stores
    open("starbucks.kml", 'w') do |f|
      f.puts "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + \
        "<kml xmlns=\"http://earth.google.com/kml/2.2\">\n" + \
        "<Folder>"
      stores.each do |s|
        f.puts s.to_kml(@lang)
      end
      f.puts "</Folder>\n</kml>"
    end
  end
end

lang = :english
dl = false
verbose = false
kml = false

OptionParser.new {|opt|
  opt.on('-v', 'verbose mode') {verbose = true}
  opt.on('-j', 'output japanese') {lang = :japanese}
  opt.on('-f', 'download info. instead of existing yaml file') {dl = true}
  opt.on('-k', 'output kml') {kml = true}
}.parse!(ARGV)

sd = StarbucksDownloader.new(lang, dl, verbose)
unless kml then
  sd.dump_poi
else
  sd.dump_kml
end
