Documentation
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use serde::Serialize;
use serde_json;

use hyper;
//use hyper::header::ContentLength;

use super::status_code::StatusCode;
use crate::error::Result;

#[derive(Debug)]
pub struct Response {
    status_code: StatusCode,
    headers: HashMap<String, String>,
    body: Vec<u8>,
}

impl Response {
    pub fn new(
        status_code: StatusCode,
        headers: HashMap<String, String>,
        data: Vec<u8>,
    ) -> Response {
        Response {
            status_code: status_code,
            headers: headers,
            body: data,
        }
    }

    pub fn empty<S>(status_code: S) -> Response
    where
        S: Into<StatusCode>,
    {
        Response::new(status_code.into(), HashMap::new(), Vec::new())
    }

    pub fn from_data<C, D>(&mut self, content_type: C, data: D) -> Result<&mut Response>
    where
        C: Into<String>,
        D: Into<Vec<u8>>,
    {
        let data = data.into();

        self.headers
            .insert("Content-Type".to_owned(), content_type.into());
        self.body = data;

        Ok(self)
    }

    pub fn from_file<C>(&mut self, content_type: C, mut file: File) -> Result<&mut Response>
    where
        C: Into<String>,
    {
        //let file_size = file.metadata().ok().map(|v| v.len() as usize);
        let mut data: Vec<u8> = Vec::new();
        file.read_to_end(&mut data)?;

        self.headers
            .insert("Content-Type".to_owned(), content_type.into());
        self.body = data;

        Ok(self)
    }

    pub fn from_text<S>(&mut self, string: S) -> Result<&mut Response>
    where
        S: Into<String>,
    {
        let string = string.into();

        self.headers.insert(
            "Content-Type".to_owned(),
            "text/plain; charset=UTF-8".to_owned(),
        );
        self.body = string.into();

        Ok(self)
    }

    pub fn from_html<S>(&mut self, string: S) -> Result<&mut Response>
    where
        S: Into<String>,
    {
        let string = string.into();

        self.headers.insert(
            "Content-Type".to_owned(),
            "text/html; charset=UTF-8".to_owned(),
        );
        self.body = string.into();

        Ok(self)
    }

    pub fn from_json<S: Serialize>(&mut self, value: S) -> Result<&mut Response> {
        let data = serde_json::to_vec(&value)?;

        self.headers.insert(
            "Content-Type".to_owned(),
            "application/json; charset=UTF-8".to_owned(),
        );
        self.body = data;

        Ok(self)
    }

    #[inline]
    pub fn status_code(&mut self, code: u16) -> &mut Response {
        self.status_code = code.into();
        self
    }

    #[inline]
    pub fn get_status_code(&self) -> u16 {
        self.status_code.0
    }

    #[inline]
    pub fn header<S>(&mut self, header: (S, S)) -> &mut Response
    where
        S: Into<String>,
    {
        self.headers.insert(header.0.into(), header.1.into());
        self
    }

    #[inline]
    pub fn get_header(&self, name: &str) -> Option<&String> {
        self.headers.get(name)
    }

    #[inline]
    pub fn get_headers(&self) -> &HashMap<String, String> {
        &self.headers
    }

    #[inline]
    pub(crate) fn raw_response(self) -> hyper::Response<hyper::Body> {
        /*
        let mut response = hyper::Response::new();

        response.set_status(hyper::StatusCode::try_from(self.get_status_code()).unwrap());

        let mut headers = hyper::Headers::with_capacity(16);

        let data_len = self.body.len();

        if data_len > 0 {
            headers.set(ContentLength(data_len as u64));

            response.set_body(self.body);
        }

        for (key, value) in self.headers.iter() {
            headers.set_raw(key.to_string(), value.to_string());
        }

        response = response.with_headers(headers);

        response
        */

        // let mut response = hyper::Response::new(hyper::Body::empty());

        // if self.body.len() > 0 {
        //     *response.body_mut() = hyper::Body::from(self.body);
        // }
        let mut header_builder = hyper::Response::builder();

        header_builder.status(self.get_status_code());

        for (key, value) in self.headers.iter() {
            header_builder.header(&**key, &**value);
        }

        header_builder.body(hyper::Body::from(self.body)).unwrap()
    }
}