extern crate tiny_http;
extern crate multipart;

use std::io::{self, Cursor, Write};
use multipart::server::{Multipart, Entries, SaveResult};
use multipart::mock::StdoutTee;
use tiny_http::{Response, StatusCode, Request};
fn main() {
    // Starting a server on `localhost:80`
    let server = tiny_http::Server::http("localhost:80").expect("Could not bind localhost:80");
    loop {
        // This blocks until the next request is received
        let mut request = server.recv().unwrap();

        // Processes a request and returns response or an occured error
        let result = process_request(&mut request);
        let resp = match result {
            Ok(resp) => resp,
            Err(e) => {
                println!("An error has occured during request proccessing: {:?}", e);
                build_response(500, "The received data was not correctly proccessed on the server")
            }
        };

        // Answers with a response to a client
        request.respond(resp).unwrap();
    }
}

type RespBody = Cursor<Vec<u8>>;

/// Processes a request and returns response or an occured error.
fn process_request(request: &mut Request) -> io::Result<Response<RespBody>> {
    // Getting a multipart reader wrapper
    match Multipart::from_request(request) {
        Ok(mut multipart) => {
            // Fetching all data and processing it.
            // save().temp() reads the request fully, parsing all fields and saving all files
            // in a new temporary directory under the OS temporary directory.
            match multipart.save().temp() {
                SaveResult::Full(entries) => process_entries(entries),
                SaveResult::Partial(entries, reason) => {
                    process_entries(entries.keep_partial())?;
                    // We don't set limits
                    Err(reason.unwrap_err())
                }
                SaveResult::Error(error) => Err(error),
            }
        }
        Err(_) => Ok(build_response(400, "The request is not multipart")),
    }
}

/// Processes saved entries from multipart request.
/// Returns an OK response or an error.
fn process_entries(entries: Entries) -> io::Result<Response<RespBody>> {
    let mut data = Vec::new();

    {
        let stdout = io::stdout();
        let tee = StdoutTee::new(&mut data, &stdout);
        entries.write_debug(tee)?;
    }

    writeln!(data, "Entries processed")?;

    Ok(build_response(200, data))
}

fn build_response<D: Into<Vec<u8>>>(status_code: u16, data: D) -> Response<RespBody> {
    let data = data.into();
    let data_len = data.len();
    Response::new(StatusCode(status_code),
                  vec![],
                  Cursor::new(data),
                  Some(data_len),
                  None)
}
