
# 2009-01-10 katoy

require 'rubygems'
require 'xml/libxml'
require 'pp'
require 'benchmark'

require 'parse_schema'

class Footnotelink
  attr_reader :role
  attr_reader :type

  def parse(reader, file='', dir='')
    @role = reader['xlink:role'].to_sym
    @type = reader['xlink:type'].to_sym
    reader.next
    self
  end

  def to_s
    "role='#{@role.to_s}' type='#{@type}'"
  end

end

class Unit
  attr_reader :measure
  attr_reader :unitNumerator
  attr_reader :unitDenominator

  def parse(reader, file='', dir='')

    id = reader['id'].to_sym

    node = reader.expand
    childs = node.children

    node.each_element do |c|
      case c.name
      when 'measure'
        @measure = c.first.to_s.to_sym
      when 'divide'        
        c.each_element do |cc|
          case cc.name
          when 'unitNumerator'
            cc.each_element do |ccc|
              case ccc.name
              when 'measure'
                @unitNumerator = ccc.first.to_s.to_sym
              else
                puts "******* ignore(0) #{ccc.name} in unit at line:#{reader.line_number}"
              end
            end
          when 'unitDenominator'
            cc.each_element do |ccc|
              case ccc.name
              when 'measure'
                @unitDenominator = ccc.first.to_s.to_sym
              else
                puts "******* ignore(1) #{ccc.name} in unit at line:#{reader.line_number}"
              end
            end
          else
            puts "******* ignore(2) #{ccc.name} in unit at line:#{reader.line_number}"
          end
        end
      else
        puts "******* ignore(3) #{c.name} in unit at line:#{reader.line_number}"
      end
    end
    
    reader.next

    {:id=>id, :unit=>self}
  end

  def to_s
    if @measure != nil
      "unit id='#{@id.to_s}' measure='#{@measure.to_s}'"
    else
      "unit id='#{@id.to_s}' unitNumerator='#{@unitNumerator.to_s}' unitDenominator='#{@unitDenominator.to_s}'"
    end
  end

end

class Context
  attr_reader :identifier
  attr_reader :scheme
  attr_reader :instant
  attr_reader :startdate
  attr_reader :enddate
  attr_reader :forever
  attr_reader :scenario
  attr_reader :segment

  def parse(reader, file='', dir='')

    id = reader['id']

    node = reader.expand
    childs = node.children

    node.each_element do |c|
      case c.name
      when 'entity'
        c.each_element do |cc|
          case cc.name
          when 'identifier'
            @identifier = cc.first.to_s.to_sym
          when 'segment'
            @segment = cc
          else
            puts "******* ignore(0)  #{cc.name} in context at line:#{reader.line_number}"
          end 
        end
      when 'period'
        c.each_element do |cc|
          case cc.name
          when 'instant'
            @instant = cc.first.to_s.to_sym
          when 'startDate'
            @startdate = cc.first.to_s.to_sym
          when 'endDate'
            @enddate = cc.first.to_s.to_sym
          when 'forever'
            @forever = true
          else
            puts "******* ignore(1) #{cc.name} in context at line:#{reader.line_number}"
          end
        end
      when 'scenario'
        @scenario = c
      else
        puts "******* ignore(2)  #{c.name} in context at line:#{reader.line_number}"
      end
    end
    reader.next

    {:id=>id.to_sym, :context=>self}
  end

  def to_s
    if @instant != nil
      "context id='#{@id.to_s}' identifier='#{@identifier}' instant='#{instant}' scenario='#{@scenario}' segment='#{@segment}'"
    elsif @startdate != nil or @enddate != nil
      "context id='#{@id.to_s}' identifier='#{@identifier}' startdate='#{@startdate}' enddate='#{@enddate}' scenario='#{@scenario}' segment='#{@segment}'"
    elsif @forever != nil
      "context id='#{@id.to_s}' identifier='#{@identifier}' forever='#{@forever}' scenario='#{@scenario}' segment='#{@segment}'"
    else
      "context id='#{@id.to_s}' identifier='#{@identifier}' scenario='#{@scenario}' segment='#{@segment}'"
    end
  end

  def to_period
    if @instant != nil
      "#{instant}"
    elsif @startdate != nil or @enddate != nil
      "#{@startdate} - #{@enddate}"
    elsif @forever != nil
      "forever"
    else
      "context id='#{@id.to_s}' identifier='#{@identifier}' scenario='#{@segment}' scenario='#{@segment}'"
    end

  end
end

class Item
  attr_reader :name
  attr_reader :uri

  attr_reader :id
  attr_reader :fact
  attr_reader :decimals
  attr_reader :contextref
  attr_reader :unitref

  def parse(reader, file='', dir='')

    @name = reader.local_name.to_sym
    @uri = reader.namespace_uri.to_sym if reader.namespace_uri != nil

    @id = reader['id']

    @decimals = reader['decimals'].to_s if reader['decimals'] != nil
    @contextref = reader['contextRef'].to_sym if reader['contextRef'] != nil
    @unitref = reader['unitRef'].to_sym if reader['unitRef'] != nil
    @fact = reader.expand.child

    reader.next
    self
  end
 
  def to_s
    "#{@name.to_s} id='#{@id.to_s}' decimals='#{@decimals.to_s}' contextRef='#{@contextref.to_s}' unitRef='#{@unitref.to_s}' fact='#{@fact}'"
  end
end

class Instance

  attr_reader :schema
  attr_reader :footnotelinks
  attr_reader :roles
  attr_reader :contexts
  attr_reader :units
  attr_reader :items

  def check_instance(reader)
    while reader.read
      n = reader.name
      break if (n != '#comment') and (n != '#text')
    end

    return (reader.namespace_uri == 'http://www.xbrl.org/2003/instance') && (reader.local_name == 'xbrl') 
  end

  def parse(file, dir='')

    begin
      reader = XML::Reader.file(file)
    rescue
      puts "---- error reading #{file}"
      reader.close if reader != nil
      return self
    end

    @schema = []
    @footnotelinks = {}
    @roles = {}
    @contexts = {}
    @units = {}
    @items = []

    if !check_instance(reader)
      puts "***** not instance file: #{file}"
      return nil
    end
    
    while reader.read
      
      name = "#{reader.namespace_uri}:#{reader.local_name}"
      case name
      when 'http://www.xbrl.org/2003/instance:xbrl'
      when ':#text'

      when 'http://www.xbrl.org/2003/linkbase:schemaRef'
        schema << Schema.new.parse(reader, file, dir)
      when 'http://www.xbrl.org/2003/linkbase:roleRef'
        r = RoleRef.new.parse(reader, file, dir)
        roles[r[:roleuri]] = r[:role]
      when 'http://www.xbrl.org/2003/instance:context'
        c = Context.new.parse(reader, file, dir)
        contexts[c[:id]] = c[:context]
      when 'http://www.xbrl.org/2003/instance:unit'
        u = Unit.new.parse(reader, file, dir)
        units[u[:id]] = u[:unit]
      when 'http://www.xbrl.org/2003/linkbase:footnoteLink'
        foot = Footnotelink.new.parse(reader, file, dir)
        footnotelinks[foot.role] = foot
      else
        item = Item.new.parse(reader, file, dir)
        items << item
      end
    end

    self
  end

end

if $0 == __FILE__

  # xml = '../data/xbrl.xbrl'
  # xml = '../data/msft/msft-20080930.xml'
  xml = "../data/td-net/081220090203088072/tdnet-qcedjpfr-33500-2008-11-30-01-2009-02-20.xbrl"
  # xml = 'http://www.sec.gov/Archives/edgar/data/789019/000119312508215214/msft-20080930.xml'
  # xml = "http://www.sec.gov/Archives/edgar/data/796343/000079634308000007/adbe-20080916.xml"  

  # xml = ARGV[0]

  puts Benchmark.measure {

    inst = Instance.new.parse(xml)
    exit if inst == nil

    # pp inst
    # pp inst.footnotelinks.size
    # pp inst.roles.size
    # pp inst.units.size
    # pp inst.contexts.size
    # inst.contexts.each { |cr| pp cr}
    # pp inst.items.size
    # inst.items.each { |item| pp item }
    # pp inst.schema[0]

    inst.items.each { |item|
      puts "#{item.name} #{item.fact} [#{inst.contexts[item.contextref].to_period}]"
    }
    pp inst.schema[0]
  }

end
