
# MSN ファイナンスの株価データページから財務諸表の情報を得る。
#  usage: ruby finance05.rb company_code
#
# 2009-09-20 katoy
# 2009-09-23 katoy  業種情報を追加、企業名の末尾の ':' を削除

require 'rubygems'
require 'open-uri'
require 'hpricot'
require 'google_chart'
require 'ya2yaml'
require 'kconv'
require 'pp'
require 'benchmark'

class ChartX < GoogleChart::BarChart
  attr_accessor :zero_p

  def to_url_x
    ret = to_url
    ret = ret + "&chp=#{@zero_p}"  if @zero_p != nil
    URI.decode(ret)
  end
end

VIEW_KIND = [:Income, :Balance, :CashFlow]

class Msn_finance
  VERSION = '2009-09-23'
  attr_reader :company, :cid, :category
  attr_reader :data, :name, :company, :graph, :path

  def initialize(company, cid = '', category = '')
    @version = VERSION
    @company = company
    @cid = cid
    @category = category
    @data = { }
    @name = ''
    @graph = { }
    @path = ''
    read
  end

  def read
    VIEW_KIND.each do |view|
      datasub = []
      @path = "http://jp.moneycentral.msn.com/investor/invsub/results/statemnt.aspx?Symbol=#{company}&lstStatement=#{view.to_s}&stmtView=Ann"
      doc = Hpricot(open(path).read)
      @name = doc.search("#lblCompanyName").inner_text  # 会社名
      if @name == ''
        raise ArgumentError, "---- Illegal company_code: #{@company}"        
      end
      # 末尾の : を取り除く
      @name = @name[0 .. @name.length - 2] if @name.end_with?(':')

      doc.search("table.ftable").each do |t|    
        t.search("tr").each do |r|
          rs = r.search("td")
          if (rs.size > 0)
            datasub << { rs[0].inner_text => [ rs[1].inner_text.strip,
                rs[2].inner_text.strip,
                rs[3].inner_text.strip,
                rs[4].inner_text.strip,
                rs[5].inner_text].reverse
            }
          end
        end
      end
      @data[view] = datasub
    end
  end

  def make_graph_income(datas, min, max, options = { })
    flc = ChartX.new(options[:size], options[:title], :vertical, false) do |chart|
      if options[:show_axis]
        chart.axis(:x, :color => 'ff00ff', :labels => options[:labels])
        chart.axis(:y, :color => 'ff00ff', :range => [min, max])
      end

      if options[:width_spacing]
        chart.width_spacing_options(options[:width_spacing])
      end

      chart.fill(:background, :gradient, :angle => 0, :color => [['76A4FB', 1], ['ffffff', 0]])
      
      datas.each do |p| 
        chart.data p[:label], p[:plot] , p[:color]
      end
    end
    flc.zero_p = -min/(max - min)  if min < 0.0
    flc
  end

  def show_income(kids)
    return if kids == nil or kids.size == 0

    table = @data[:Income]
    labels = table[0][table[0].keys[0]]

    d = []
    kids.each { |id| 
      val = '0'
      table.each do |tr|
        if tr.keys[0] == id[:index]
          val = tr[tr.keys[0]]
          break
        end
      end
      d << val
    }

    p = []
    d.each { |data| p << data.map { |v| v.gsub(',', '').to_f } }

    max = Float::MIN
    min = 0
    p.each do |data|
      p_max = data.max
      p_min = data.min
      max = p_max if max < p_max
      min = p_min if p_min < min
    end

    p.each { |data| data.map! { |v| 100.0 * (v - min) / (max - min)} }

    tt = []
    kids.each { |id| tt << "#{id[:index]}" }
    title = "#{@name} " + tt.join(',')
    
    plots = []
    p.each_with_index do |data, i|
      plots << { :label => kids[i][:index], :color => kids[i][:color] , :plot => data }
    end

    g = { }
    g[:big] = make_graph_income(plots, min, max, {:title => title, :size => "600x400", :labels => labels, :show_axis => true})
    g[:small] = make_graph_income(plots, min, max, {:size => "60x40", :width_spacing => { :bar_width => 3, :bar_spacing => 0, :group_spacing => 2}})

    save_image(g[:big].to_url_x, "images/big/#{@company[3 .. -1]}.png")
    save_image(g[:small].to_url_x, "images/small/#{@company[3 .. -1]}.png")

    # data.each_with_index { |v, i| p "#{i}: #{v.keys}" } 
    @graph[:Income] = { :g => g, :title => title, :plot => [d, p] }
  end

  def show_balance(kids)
    return if kids == nil or kids.size == 0
  end

  def show_cashflow(kids)
    return if kids == nil or kids.size == 0
  end

  def show(kids)
    show_income(kids[:Income])
    show_balance(kids[:Balance])
    show_cashflow(kids[:CashFlow])
  end

  def save_image(url, path)
    File.open(path, 'wb') do |file|
      open(URI.escape(url)) do |data|
        file.write(data.read)
      end
    end
  end

  def save_yaml(options = { })
    s = self.ya2yaml
    open("data/#{@company[3 .. -1]}.yaml", "w") { |f|
      f.write s
    }
    
    if (options[:verify])
      ss = Msn_finance.load_yaml(@company).ya2yaml
      exit if ss != s
    end
  end

  def self.load_yaml(company)
    obj = nil
    File.open( "data/#{company[3 .. -1]}.yaml" ) { |io|
      YAML.load_documents(io) do |y|
        obj = y
      end
    }
    obj
  end

end
