<?php

namespace ModulesGarden\ProxmoxAddon\App\Services\Ip;

class Ipv4Range
{

    protected  $pool;
    protected $cidr;
    protected $limit;
    protected $disableIpAddresses=[];
    const NET_MASK = [
            0  => "0.0.0.0",
            1  => "128.0.0.0",
            2  => "192.0.0.0",
            3  => "224.0.0.0",
            4  => "240.0.0.0",
            5  => "248.0.0.0",
            6  => "252.0.0.0",
            7  => "254.0.0.0",
            8  => "255.0.0.0",
            9  => "255.128.0.0",
            10 => "255.192.0.0",
            11 => "255.224.0.0",
            12 => "255.240.0.0",
            13 => "255.248.0.0",
            14 => "255.252.0.0",
            15 => "255.254.0.0",
            16 => "255.255.0.0",
            17 => "255.255.128.0",
            18 => "255.255.192.0",
            19 => "255.255.224.0",
            20 => "255.255.240.0",
            21 => "255.255.248.0",
            22 => "255.255.252.0",
            23 => "255.255.254.0",
            24 => "255.255.255.0",
            25 => "255.255.255.128",
            26 => "255.255.255.192",
            27 => "255.255.255.224",
            28 => "255.255.255.240",
            29 => "255.255.255.248",
            30 => "255.255.255.252",
            31 => "255.255.255.254",
            32 => "255.255.255.255"
    ];
    protected $firstIp = false;

    /**
     * Ipv4Range constructor.
     * @param $pool
     * @param $cidr
     */
    public function __construct($pool, $cidr)
    {
        $this->pool = $pool;
        $this->cidr = $cidr;
    }


    public function get(){

        $pool   = [];
        $ip_enc = ip2long($this->pool);
        //convert last (32-$mask) bits to zeroes
        $curr_ip         = $ip_enc | pow(2, (32 - $this->cidr)) - pow(2, (32 - $this->cidr));
        $ips             = [];
        $ip_nmask        = self::NET_MASK[$this->cidr];
        $ip_address_long = $ip_enc;
        $ip_nmask_long   = ip2long($ip_nmask);
        //caculate network address
        $ip_net = $ip_address_long & $ip_nmask_long;
        //caculate first usable address
        $ip_host_first = ((~$ip_nmask_long) & $ip_address_long);
        $ip_first      = ($ip_address_long ^ $ip_host_first) + 1;
        //caculate last usable address
        $ip_broadcast_invert = ~$ip_nmask_long;
        $ip_last             = ($ip_address_long | $ip_broadcast_invert) - 1;
        //caculate broadcast address
        $ip_last       = ($ip_address_long | $ip_broadcast_invert) - 1;
        $ip_last_short = long2ip($ip_last);
        $totalHost     = (float)pow(2, (32 - $this->cidr));
        if ($totalHost > 10000)
        {
            throw new \Exception(sprintf("Subnet %s/%s is too large. You are tring to add %s addresses.", $this->pool, $this->cidr, $totalHost));
        }
        for ($pos = 0; $pos < pow(2, (32 - $this->cidr)); ++$pos)
        {
            $ip     = long2ip((float)$curr_ip + $pos);
            if(!$this->firstIp && preg_match("/\.0$/", $ip)){
                continue;
            }
            if(in_array($ip, $this->disableIpAddresses)){
                continue;
            }
            $pool[] = $ip;
            if ($ip == $ip_last_short)
            {
                break;
            }
            if($this->limit && count($pool) == $this->limit){
                return  $pool;
            }
        }
        return $pool;
    }

    /**
     * @return mixed
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * @param mixed $limit
     * @return Ipv4Range
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;
        return $this;
    }

    /**
     * @param array $disableIpAddresses
     * @return Ipv4Range
     */
    public function disableIpAddresses($disableIpAddresses)
    {
        $this->disableIpAddresses = $disableIpAddresses;
        return $this;
    }

    public function firstIp($enable=false){

        $this->firstIp = $enable;
        return $this;
    }


}