<?php

namespace MGModule\DNSManager2\mgLibs\custom\dns\utils;

class IP {
    const LAST = 'last';
    const HALF = 'half';    
    
    private $ip;
    private $block = null;
    private $delimeter = '.';
    private $ip_array;
    
    function __construct($ip) {
        if(strpos($ip, '/') !== false) {
            $this->block = explode('/',$ip)[1];
            $ip = explode('/',$ip)[0];
        }
        $this->ip = $ip;
        if($this->isIPv6()) {
            $this->expand();
            $this->delimeter = ':';
        }
        $this->explode();
    }

    /** Parses list of IPs and IPs with CIDR block into list of IPs
     * @param $availableIps array
     * @return array
     */
    public static function getAvailableIpList($availableIps)
    {
        $result = array();
        foreach ($availableIps as $ip)
            if(strpos($ip, '/') !== false)
                $result = array_merge($result, Cidr::cidrAsIpList($ip));
            else
                array_push($result, $ip);
        return array_unique($result);
    }

    public static function getOnlyIpv4($ipArray)
    {
        return array_values(array_filter($ipArray, function ($ip) {
            return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
        }));
    }

    public static function getOnlyIpv6($ipArray)
    {
        return array_values(array_filter($ipArray, function ($ip) {
            return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
        }));
    }
    
    private function expand() {
        $hex = unpack("H*hex", inet_pton($this->ip));         
        $this->ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1);
    }
    
    public function explode() {
        $this->ip_array = explode($this->delimeter, $this->ip);
        return $this->ip_array;
    }

    public function isValid() {
        if($this->ip == null)
            return true;
        $cidrPart = true;
        if($this->block !== null) {
            $cidrPart = Cidr::validate($this->ip, $this->block);
        }
        return (filter_var($this->ip, FILTER_VALIDATE_IP) !== false) && $cidrPart;
    }
    
    public function isIPv4() {
        return filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
    }
    
    public function isIPv6() {
        return filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
    }
    
    public function isPrivateORReserved() {
        return filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
    }
    
    public function reverse() {
        $this->ip_array = array_reverse($this->ip_array);
    }
    
    public function slice($offset, $length = null) {
        $slice = array_slice($this->ip_array, $this->constToNumber($offset), $this->constToNumber($length) );
        return implode($this->delimeter, $slice);
    }
    
    private function constToNumber($var) {
        if(is_null($var)) return null;
        
        switch((string)$var) {
            case self::LAST:
                return count($this->ip_array) - 2;
            case self::HALF:
                return (int) (count($this->ip_array)/2);
            default:
                return $var;
        }
    }
    
    public function __toString() {
        return $this->ip;
    }
    
    public function isInNetwork($net_addr, $net_mask) {
//        if($this->isIPv4()) {
//            $ip_binary_string = sprintf("%032b", ip2long($this->ip));
//            $net_binary_string = sprintf("%032b", ip2long($net_addr));
//            return (substr_compare($ip_binary_string, $net_binary_string, 0, $net_mask) === 0);
//        } else {
            $subnet = inet_pton($net_addr);
            $addr = inet_pton($this->ip);
            $binMask = $this->maskToByteArray($net_mask);
            return ($addr & $binMask) == $subnet;
//        }
    }
    
    private function maskToByteArray($subnetMask) {
        $addr = str_repeat("f", $subnetMask / 4);
        switch($subnetMask % 4) {
            case 0:
                break;
            case 1:
                $addr .= "8";
                break;
            case 2:
                $addr .= "c";
                break;
            case 3:
                $addr .= "e";
                break;
        }
        $addr = str_pad($addr, 32, '0');
        $addr = pack("H*", $addr);
        return $addr;
    }
    
}