#!/usr/bin/env ruby

$:.push File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
$:.push File.expand_path(File.dirname(__FILE__))

case RUBY_PLATFORM
when /win32/, /mingw/, /cygwin/
  require 'win32.rb'
else
  require 'linux.rb'
end

#[ruby-list:42851] Object#define_singleton_method
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/41483
unless defined?( "".define_singleton_method )
  class Object
    def define_singleton_method(m, &b)
      (class<<self;self;end).instance_eval{define_method(m, &b)}
    end
  end
end

# Phi::APPLICATION.create_handle

module Phi
  EventHandle = {}
  class Persistent
  private
    def singleton_method_added(id)
p [:s, id] if $DEBUG
      case id.to_s
      when /^on_/, /^after_/, /^before_/
        self.event_handle id
      end
    end

    def self.method_added(id)
p [:m, id] if $DEBUG
      case id.to_s
      when /^on_/, /^after_/, /^before_/
        if EventHandle[self].nil?
           EventHandle[self] = []
        end
        EventHandle[self].push id
      end
    end

    def extend(mod)
      super(mod)
p [:e, mod] if $DEBUG
      mod.instance_methods(false).each do |name|
        case name
        when /^on_/, /^after_/, /^before_/
          self.event_handle name.intern
        end
      end
    end
    public :extend

    def self.include(mod)
      super(mod)
p [:i, mod] if $DEBUG
      mod.instance_methods(false).each do |name|
        case name
        when /^on_/, /^after_/, /^before_/
          if EventHandle[self].nil?
             EventHandle[self] = []
          end
          EventHandle[self].push name.intern
        end
      end
    end

    def initialize(*args)
p [:initialize, EventHandle, self.class] if $DEBUG
      super(*args)
      if EventHandle[self.class]
         EventHandle[self.class].each do|id|
           begin
             event_handle id
           rescue DelphiError
             #
           end
         end
      end
    end

  end
end

Phi.date_format = "yyyy/mm/dd"
Phi.time_format = "hh:mm:ss"

module Phi
  module Enumerable
    include ::Enumerable
    def each
      (0...self.count).each{|i| yield self[i]}
    end
  end

  module ItemsEnumerable
    include ::Enumerable
    def each
      (0...self.items.count).each{|i| yield self.items[i]}
    end
  end

  class Strings     ; include Phi::Enumerable ; end
  class MenuItem    ; include Phi::Enumerable ; end
  class TreeNode    ; include Phi::Enumerable ; end
  class TreeNodes   ; include Phi::Enumerable ; end
  class ListItems   ; include Phi::Enumerable ; end
  class ListColumns ; include Phi::Enumerable ; end
  class PopupMenu   ; include Phi::ItemsEnumerable ; end
  class TreeView    ; include Phi::ItemsEnumerable ; end
end

module Phi
  module MenuContainer
  private
    def define_menu_child_attr(this)
      return unless this
p [:menu, this.name] if $DEBUG
      define_singleton_method(Phi.downcase(this.name)){ this } unless this.name.empty?
      this.each do |item|
        define_menu_child_attr(item)
      end
    end
  public
    def menu_attr_flatten
      define_menu_child_attr(defined?(self.menu) ? menu.items : items)
    end

  end

  class Form        ; include Phi::MenuContainer ; end
  class MainMenu    ; include Phi::MenuContainer ; end
  class PopupMenu   ; include Phi::MenuContainer ; end
end

module Phi
  module ControlContainer
  private
    def define_control_child_attr(controls)
      controls.each do |this|
        define_singleton_method(Phi.downcase(this.name)){ this } unless this.name.empty?
        define_control_child_attr(this.controls) if this.respond_to? :controls
      end
    end
  public
    def control_attr_flatten
      define_control_child_attr(controls)
    end
  end

  class Form        ; include Phi::ControlContainer ; end
end

module Phi
  class Control
    def right
      self.left + self.width
    end
    def bottom
      self.top + self.height
    end
    # ap-dev:0748
    def right=(v)
      self.left = v - self.width
    end
    def bottom=(v)
      self.top = v - self.height
    end
  end
end

# ap-list:2135
module Phi
  class MaskEdit
    alias edit_mask_= edit_mask=
    def edit_mask=(v)
      tx = self.text
      self.edit_mask_= v
      self.text = tx
    end
  end
end

module Phi
  class Control
    def parent_form
      form = self
      form = form.parent while not ( form.is_a? Form )
      return form
    end
  end
end

# ap-list:2779
module Phi
  class Rect
    def ===(point)
      (left..right) === point.x && (top..bottom) === point.y
    end
  end
end

# ap-dev:0750
module Phi
  class DateTime

    alias :succ           :inc_day

    alias :begin_of_day   :start_of_day
    alias :begin_of_month :start_of_month
    alias :begin_of_year  :start_of_year

    #alias :last_of_day    :end_of_day
    #alias :last_of_month  :end_of_month
    #alias :last_of_year   :end_of_year

  end
end

# partition( &block )
#   alias divide
#   ブロックを評価した値が真であった要素を全て含む配列と
#   ブロックを評価した値が偽であった要素を全て含む配列の
#   組(配列)を返す。
#   see: ruby-talk:12330
if RUBY_VERSION < '1.8.0'
  module Enumerable
    def partition
      matching, nonmatching = [], []
      each{|e| yield(e) ? matching.push(e) : nonmatching.push(e)}
      return matching, nonmatching
    end
    alias divide  partition
  end
end
# partition!( &block )
#   alias divide!
#   ブロックを評価した値が真であった要素を全て含む配列を返す。
#   self の内容は
#   ブロックを評価した値が偽であった要素を全て含む配列の内容で
#   置き換えられる。
#   see: Enumerable#partition, Array#replace

class Array
  def partition!( &block )
    ret1, ret2 = partition( &block )
    self.replace ret2
    ret1
  end
  alias divide! partition!
end

module Phi
  @@uniq_id = 0

  #  ユニークな ID らしきものを返す
  def Phi.uniq_id(prefix='id_')
    @@uniq_id += 1
    "#{prefix}#{@@uniq_id}".intern
  end

  # name が省略可能で
  # on_click と hint と add を引数で指定可能な Phi::new_item
  #   Symbol なら name
  #   String なら hint
  #   Array  なら add で加えるサブアイテムの配列
  #   Proc   なら on_click のメソッド
  def Phi.new_item_ex(caption, shortcut='', *args)
    action, name, hint, proc, subs, *rest = []
    args.each{|e|
      case e
      when Action
        if action then raise ArgumentError, "too many Actions for #{[action,e].inspect}" end
        action = e
      when Symbol
        if name then raise ArgumentError, "too many Symbols for name" end
        name = e
      when String
        if hint then raise ArgumentError, "too many Strings for hint" end
        hint = e
      when Proc
        if proc then raise ArgumentError, "too many Procs for on_click" end
        proc = e
      when Array
        if subs then raise ArgumentError, "too many Arrays for items" end
        subs = e
      else
        rest.push e
      end
    }
    item = Phi.new_item(caption, shortcut, name || uniq_id('mi_'), *rest)
    item.hint     = hint if hint
    item.on_click = proc if proc
    item.action = action if action
    item.add(*subs)      if subs
    item
  end
end

# [ap-dev:1743]
module Phi
  class Strings
    alias _assign assign
    def assign(v)
      case v
      when Array
        self.clear
        v.each{|i|self.add(i)}
      when String
        self.text = v
      else
        _assign(v)
      end
    end
  end
end

# [ap-list:3404]
# [ap-dev:1808]
# [ap-list:3425]
module Phi
  def self.dfm_to_afm(dfm,form_class=:obsolete)
    afm = dfm.
      sub(/: \S*/,': TPhiForm').
      gsub( /^\s*(On|Before|After)\w+ = .*/, '' )
  end

  def self.load_from_dfm(dfm_path, form_class=:obsolete )
    dfm = File.open(dfm_path,"rb"){|f|f.read}
    afm = dfm_to_afm(dfm)
    Component._load(afm)
  end
end

# [ap-dev:1834]
module Phi
  if const_defined? :DELPHIAN_AUTO_NAME
    remove_const :DELPHIAN_AUTO_NAME
  end
  DELPHIAN_AUTO_NAME = true
module_function
  def delphian_auto_name(obj)
    klsname = obj.class.to_s.gsub(/^.+::/, '')
    reg = Regexp.new(klsname + '(\d+)')
    tmp_ary = [0]
    ObjectSpace.each_object(obj.class) {|tmp_obj|
      if reg =~ tmp_obj.name
        tmp_ary.push($1.to_i)
      end
    }
    klsname + (tmp_ary.sort.last + 1).to_s
  end
end

module Phi
  class Graphic
    def data=(v)
      self.load(Phi::StringStream.new(v.gsub(/[\n\s]/,"").gsub(/.{2}/){ $&.hex.chr }))
      return v
    end

    def data
      strm = Phi::StringStream.new
      self.save(strm)
      strm.data_string.gsub(/./nm){"%02x" % $&[0]}
    end
  end
end
