Documentation
use crate::response::ResponseBodyByte;
use crate::{Config, Request, Response};
use async_std::fs::File;
use async_std::{net::TcpStream, prelude::*};
use std::sync::Arc;

use self::status_code::status_code_to_text;
mod status_code;

pub const CRLF: &str = "\r\n";
const BUFFER_SIZE: usize = 1024 * 2;

fn generate_response_header_bytes(
    request: Option<&Request>,
    response: &Response,
    config: &Arc<Config>,
) -> Vec<u8> {
    let status = format!("HTTP/1.1 {} {}\r\n", response.status_code, status_code_to_text(response.status_code));
    let server = "Server: Rust\r\n";
    let mut headers_str = format!("{}{}", status, server);

    for (key, value) in response.headers.iter() {
        headers_str.push_str(format!("{}: {}\r\n", key, value).as_str());
    }

    if let Some(request) = request {
        if request.is_keep_alive() {
            headers_str.push_str("Connection: keep-alive\r\n");
            headers_str.push_str(
                format!("Keep-Alive: timeout={}\r\n", config.keep_alive_timeout).as_str(),
            );
        } else {
            headers_str.push_str("Connection: close\r\n");
        }
    }

    if let Some(content_length) = response.get_body_bytes_len() {
        headers_str.push_str(format!("Content-Length: {}\r\n", content_length,).as_str());
    }

    headers_str.push_str(CRLF);

    headers_str.as_bytes().to_vec()
}

pub async fn send_stream(
    stream: &mut TcpStream,
    config: &Arc<Config>,
    request: Option<&Request>,
    response: &Response,
) {
    let mut bytes = generate_response_header_bytes(request, response, config);

    match response.get_body_bytes() {
        ResponseBodyByte::All(mut body_bytes) => {
            bytes.append(&mut body_bytes);
            stream.write(&bytes).await.unwrap();
        }
        ResponseBodyByte::File(path) => {
            let mut file = File::open(path).await.unwrap();
            let mut buffer = vec![0u8; BUFFER_SIZE];

            stream.write(&bytes).await.unwrap();
            loop {
                let bytes_count = file.read(&mut buffer).await.unwrap();
                if bytes_count == 0 {
                    break;
                }
                
                stream.write(&buffer).await.unwrap();

                if bytes_count < BUFFER_SIZE {
                    break;
                }
            }
        }
        ResponseBodyByte::None => {}
    }
    stream.flush().await.unwrap();
}