#
#   Copyright (C) Stonesoft Corporation 2010 - 2013.
#   All rights reserved.
#
#   The StoneGate software, manuals, and technical
#   literature may not be reproduced in any form or
#   by any means except by permission in writing from
#   Stonesoft Corporation.
#

module IPForge
  class BrokenIPv4Address < BrokenAddressException
  end
  
  class IPv4Address
    attr_accessor :IP_num
    
    def initialize(value)
      @IP_num = 0
          
      if value.instance_of? String
        a1,a2,a3,a4 = value.split(/\./)
        if a1.nil? or a2.nil? or a3.nil? or a4.nil? or a4.instance_of? Array
          raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, should be in form \"a.b.c.d\". Got #{a1.inspect},#{a2.inspect},#{a3.inspect},#{a4.inspect}")
        end
        if not a1 =~ /^\d+$/
          raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, first octet (#{a1}) is not an integer")
        end
        if not a2 =~ /^\d+$/
          raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, second octet (#{a2}) is not an integer")
        end
        if not a3 =~ /^\d+$/
          raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, third octet (#{a3}) is not an integer")
        end
        if not a4 =~ /^\d+$/
          raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, fourth octet (#{a4}) is not an integer")
        end
        a1 = a1.to_i
        a2 = a2.to_i
        a3 = a3.to_i
        a4 = a4.to_i
        if (a1 < 0 or a1 > 255) or (a2 < 0 or a2 > 255) or (a3 < 0 or a3 > 255) or (a4 < 0 or a4 > 255)
          raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, contains values outside range [0,255]")
        end
       
        @IP_num = (a1 << 24) + (a2 << 16) + (a3 << 8) + a4
      elsif value.instance_of? Fixnum or value.instance_of? Bignum
        @IP_num = value
        
        if @IP_num < 0 or @IP_num > 0xFFFFFFFF
          raise BrokenIPv4Address.new("Value #{value.to_s(base=16)} could not be converted into a legal IP address, should be between [0x0,0xFFFFFFFF]")
        end
      elsif value.instance_of? IPv4Address
        @IP_num = value.IP_num
      else
        raise BrokenIPv4Address.new("Input to IPv4Address.new() in invalid format, expected either Fixnum or String and got #{value.class}")
      end
    end
    
    def ==(other)
      return ((other.instance_of? IPv4Address) and (@IP_num == other.IP_num))
    end
    
    def +(other)
      if other.instance_of? Fixnum
        return IPv4Address.new(@IP_num + other)
      elsif other.instance_of? IPv4Address
        return IPv4Address.new(@IP_num + other.IP_num)
      else
        raise "HELL"
      end
    end
    
    def -(other)
      if other.instance_of? Fixnum
        return IPv4Address.new(@IP_num - other)
      elsif other.instance_of? IPv4Address
        return IPv4Address.new(@IP_num - other.IP_num)
      else
        raise "HELL"
      end
    end
    
    def IP_string
      return IPv4Address.i_to_s(@IP_num)
    end
    
    def to_s
      return IPv4Address.i_to_s(@IP_num)
    end
    
    def to_i
      return @IP_num
    end
    
    def broadcast?
      return @IP_num == 0xffffffff
    end
    
    def multicast?
      return ((@IP_num >> 24) & 0xe0 > 0)
    end
    
    def IPv4Address.broadcast_from_net_and_mask(net, mask)
      broadcast = net.to_i | ~mask.to_i
      if broadcast < 0 #unsigned fix
        broadcast = 0x100000000 + broadcast
      end
      return IPv4Address.new(broadcast)
    end
    
    def IPv4Address.net_from_ip_and_mask(ip, mask)
      return IPv4Address.new(ip.to_i & mask.to_i)
    end
      
    def IPv4Address.s_to_i(string)
      a1,a2,a3,a4 = string.split(/\./)
      if a1.nil? or a2.nil? or a3.nil? or a4.nil? or a4.instance_of? Array
        raise BrokenIPv4Address.new("String #{value} could not be converted into a legal IP address, should be in form \"a.b.c.d\". Got #{a1.inspect},#{a2.inspect},#{a3.inspect},#{a4.inspect}")
      end
       
      return (a1.to_i << 24) + (a2.to_i << 16) + (a3.to_i << 8) + a4.to_i
    end
    
    def IPv4Address.i_to_s(integer)
      return ((integer & 0xFF000000) >> 24) .to_s + "." + ((integer & 0xFF0000) >> 16).to_s + "." + ((integer & 0xFF00) >> 8).to_s + "." + (integer & 0xFF).to_s
    end
    
    def <=>(obj)
      self.to_i <=> obj.to_i
    end
    
    def eql?(obj)
      obj.class == self.class and self.to_i.eql?(obj.to_i)
    end
    
    # I'm not a number, I'm a free IP Address
    def hash
      return @IP_num & 0x7FFFFFFF
    end
  end
  
  BROADCAST_IPV4 = IPv4Address.new("224.0.0.0")
end
