use std::{io::{Write, Result as IoResult}, net::IpAddr };

pub struct Uri {
    scheme: String,
    authority: Authority,
    path: String,
    query: String,
}

impl Uri {
    pub fn write_to<W: Write>(&self, w: &mut W) -> IoResult<()> {
        w.write_all(self.scheme.as_bytes())?;
        w.write_all(b"://")?;
        self.authority.write_to(w)?;
        w.write_all(b"/")?;
        w.write_all(self.path.as_bytes())?;
        w.write_all(b"?")?;
        w.write_all(self.path.as_bytes())
    }
}

pub struct Authority {
    user_info: Option<String>,
    host: Host,
    port: Option<u16>,
}

impl Authority {
    pub fn write_to<W: Write>(&self, w: &mut W) -> IoResult<()> {
        w.write_all(self.user_info.clone().map(|ui| ui + "@").unwrap_or("".to_owned()).as_bytes())?;
        self.host.write_to(w)?;
        w.write_all(self.port.map(|p| ":".to_owned() + p.to_string().as_str()).unwrap_or("".to_owned()).as_bytes())
    }
}

#[derive(Clone)]
pub enum Host {
    Ip(IpAddr),
    Name(HostName),
}

impl Host {
    pub fn write_to<W: Write>(&self, w: &mut W) -> IoResult<()> {
        match self {
            Host::Ip(ip_addr) => w.write_all(ip_addr.to_string().as_bytes()),
            Host::Name(name) => w.write_all(name.0.as_bytes()),
        }
    }
}

#[derive(Clone)]
pub struct HostName(String);

impl HostName {
    pub fn labels_iter(&self) -> impl Iterator<Item = &str> {
        //TODO: This is probably too simplistic, even though it's meant to be the point.
        self.0.split_terminator('.')
    }
}