#
# Similar to "extensions/kerner.rb" require_relative()
# but it changes directory before require-ing a library.
#
def require2 filename
    puts caller[0]
    caller_file = ( caller[0] =~ /:\d+(:|\Z)/ ; $`)
    caller_dir = File.dirname(caller_file)
    cur_dir = Dir.pwd
    Dir.chdir caller_dir
    require filename
    Dir.chdir cur_dir
end

#
# Hash with keys can be accessed as methods
#   e.g.    h = Hash.new
#           h['key'] = 1
#           h.key    = 2
#           puts h.key
#
class Hash 
    def method_missing(method_name, *args)
        name = method_name.to_s
        if name.ends_with?('=')
            self[ name.chop ] = args[0]
        else
            self[ name ] || self[ name.to_sym ]
        end
    end
end

#
# FunctionHash -    hash that calls Proc objects in the hash 
#                   as its own methods
#
#   e.g.    h = FunctionHash.new( Proc.new{ return nil } )
#           h['add2'] = Proc.new{|x| x+2}
#           h['glue'] = Proc.new{|str1, str2| str1 + str2 }
#           h.add2(7)                 # returns 9
#           h.glue("wood","metal")    # returns "woodmetal"
#
class FunctionHash < Hash
    def method_missing(method_name, *args)
        self[method_name.to_s].call(*args)
    end
end


#
# NameArray allows accessing the elements by name (string) like 
# a hash, but preserves the order of elements like an array.
#       n = NameArray.new
#       n['John'] = 1
#       n['Mary'] = 33
#       n.each{|key, value| puts value }# => John Mary
#       n['John']                       # => 1
#       n[0]                            # => ['John', 1]
#
class NameArray < Array
    def [](idx)
        pair = self.assoc idx
        return pair[1]
    end
    def []=(idx, value)
        pair = [idx, value]
        self << pair
    end
end



class String
    def ends_with?(substr)
        len = substr.length
        self.reverse() [0 .. len-1].reverse == substr
    end
    
    def starts_with?(substr)
        len = substr.length
        self[0 .. len-1] == substr
    end
    
    alias start_with?  starts_with?
    alias begin_with?  starts_with?
    alias begins_with? starts_with?
    alias end_with?    ends_with?

    # String each() operator reads line-by-line
    # These functions return char-by-char
    def each_char
        self.each_byte{|x| yield x.chr }
    end
    def collect_char
        r = []
        self.each_byte{|x| r << x.chr }
        r
    end
end

#
# read_ini_file - reads .INI file into a hash.
# E.g. test.ini:
#      [Heading]       # this line ignored
#      color = red
#      size  = 7
#      # This is a comment
#
#      h = read_ini_file("test.ini")
#      => { 'color'=>'red', 'size'=>'7' }
#
def read_ini_file( filename )
    h = Hash.new
    
    File.open(filename,'r'){|f|
        f.each{|line|
            next if line.begins_with? '#'
            next if not line.include? '='
            m = line.match /\s*([a-z0-9_]+)\s*=(.+)/
            h[m[1]] = m[2].strip
        }
    }
    return h
end



#
# Utility functions to be use in console debugging
#
#
def pause
    print "\n\n\nHit    ENTER    to continue\n\n\n"
    readline
end

def message str
    print str + "\n"
end

def debug str
    print str + "\n"
end


#
# Usage: 
#   for line in file("hello.txt")
#       print line
#       break if line =~ /lo/     # This will orderly close the file
#   end
#   # File will be closed here
#
def file(filename)

    f = File.new(filename,'r')
    
    class << f
        def each(&block)
            super(&block)
        ensure 
            self.close
        end
    end
    
    return f
end

=begin
#
# Alternative implementation of file()
# FileIterator closes the file after using each{}
# Usage: FileIterator.new(fileaname).each{|line| ...}
#
class FileIterator
    def initialize(filename)
        @f = File.open(filename,"r")
    end
    def each(&block)
        begin
            @f.each(&block)
        ensure
            @f.close
        end
    end
end

def file(filename)
    return FileIterator.new(filename)
end

=end


#
# +def_enum+ defines constants, much like C/C++ enum
#
#       def_enum :OPEN, :CLOSED, :HIGH, :LOW
#
# is equivalent to 
#       OPEN   = :OPEN
#       CLOSED = :CLOSED
#       ...
#
def def_enum *constant_names
    constant_names.each{|sym|
        eval "#{sym.id2name} = :#{sym.id2name}"
    }
end




#
# filename_change_extension "/dir/file.txt", ".CSV"
# => "/dir/file.CSV"
#
# filename_change_extension "/dir/file",     ".CSV"
# => "/dir/file.CSV"
#

def filename_change_extension( filename, extension )
    filename =~ /(.*[\/\\])?    # match the path, up to the last slash
        ([^\/\\]+)              # match the filename (no slashes)
        $                       # up to the end of string
        /x                      # remove comments from regexp
    path, name = ($1 || ""), $2 # if no path given, use "" instead of nil
    ext      = File.extname(name)
    basename = File.basename(name, ext )
    
    return path + basename + extension 
end


if __FILE__ == $0

    require "test/unit"
    
    class TestFilenameChangeExtension < Test::Unit::TestCase
        def test_1
            test_cases = [
                [   "filename",               ".c" , "filename.c"],
                [   ".filename",              ".c" , ".filename.c"],
                [   ".a.b.c.ext",             ".c" , ".a.b.c.c"],
                [   "filename.ext",           "s"  , "filenames"],
                [   "filename.ext",           ""   , "filename"],
                [   "/dir/dir2/filename.txt", ".c" , "/dir/dir2/filename.c"],
                [   "/dir/dir2/filename",     ".c" , "/dir/dir2/filename.c"],
                [   "/dir/dir2/.filename",    ".c" , "/dir/dir2/.filename.c"],
                [   "/dir2.with.dot/filename",".c" , "/dir2.with.dot/filename.c"],
            ]
    
            test_cases.each{|filename, extension, expected|
                assert_equal expected, filename_change_extension( filename, extension) , "test case [#{filename.inspect}, #{extension.inspect}]" 
            }
        end
    end

end

