use crate::ip_address::IpAddress;
use crate::error::Error;

#[derive(Debug)]
pub struct CidrIp{
    pub network:String,
    pub subnet:u32,
}

impl CidrIp{


    pub fn new(input:String) -> Result<Self,Error>{
        let mut parts = input.split("/");
        let ip=match parts.next(){
            Some(str)=>str,
            None =>return Err(Error::InvalidCidr),
        };
        let mask=match parts.next() {
            Some(str) => str,
            None => return Err(Error::InvalidSubnet)
        };
        let mask:u32=parts.next().unwrap().trim().parse().expect("Error in parsing the subnet mask");
        Ok(CidrIp{
            network:ip.to_string(),
            subnet:mask,
        })
    }


    pub fn get_network_mask(&self) -> String{
        let netmask: u32 = (0xffffffff << (32 - self.subnet)) & 0xffffffff;

        let first_octet = (netmask >> 24) & 255;
        let second_octet = (netmask >> 16) & 255;
        let third_octet = (netmask >> 8) & 255;
        let fourth_octet = netmask & 255;

        format!("{}.{}.{}.{}", first_octet, second_octet, third_octet, fourth_octet)
    }

    pub fn get_broadcast_address(&self) -> String{

        let host_bits=32-self.subnet;

        let ip_parts:Vec<u8> = IpAddress::get_ip_parts(&self.network);

        let ip_num=IpAddress::parts_to_decimal(ip_parts);

        let broadcast_num= ip_num | ((1 << host_bits)-1);

        let broadcast_parts = [
            (broadcast_num >> 24) & 0xFF,
            (broadcast_num >> 16) & 0xFF,
            (broadcast_num >> 8) & 0xFF,
            broadcast_num & 0xFF,
        ];        

        format!("{}.{}.{}.{}",broadcast_parts[0],broadcast_parts[1],broadcast_parts[2],broadcast_parts[3])
    }
    
    fn get_network_address(&self) -> String {

        let host_bits=32-self.subnet;

        let ip_parts:Vec<u8> = IpAddress::get_ip_parts(&self.network);

        let ip_num=IpAddress::parts_to_decimal(ip_parts);

        let network_num = ip_num & (!(2_u32.pow(host_bits) - 1));

        let network_parts = [
            (network_num >> 24) & 0xFF,
            (network_num >> 16) & 0xFF,
            (network_num >> 8) & 0xFF,
            network_num & 0xFF,
        ];        

        format!("{}.{}.{}.{}",network_parts[0],network_parts[1],network_parts[2],network_parts[3])
    }

    pub fn is_within_range(&self,second_ip : &IpAddress) -> bool{
        let network_address=self.get_network_address();
        let broadcast_address=self.get_broadcast_address();

        let start_parts=IpAddress::get_ip_parts(&network_address);
        let end_parts=IpAddress::get_ip_parts(&broadcast_address);
        let second_parts=IpAddress::get_ip_parts(second_ip.address.trim());

        let start=IpAddress::parts_to_decimal(start_parts);
        let end=IpAddress::parts_to_decimal(end_parts);
        let second=IpAddress::parts_to_decimal(second_parts);

        start<=second && second<=end
    }

}