use futures::{Async, Future, Poll};
use std::{fmt, io, mem, path::Path};
use tokio_io;
use {file, File};

/// Creates a future that will open a file for writing and write the entire
/// contents of `contents` to it.
///
/// This is the async equivalent of `std::fs::write`.
///
/// # Examples
///
/// ```no_run
/// # extern crate tokio;
/// use tokio::prelude::Future;
/// fn main() {
///     let buffer = b"Hello world!";
///     let task = tokio::fs::write("foo.txt", buffer).map(|data| {
///         // `data` has now been written to foo.txt. The buffer is being
///         // returned so it can be used for other things.
///         println!("foo.txt now had {} bytes written to it", data.len());
///     }).map_err(|e| {
///         // handle errors
///         eprintln!("IO error: {:?}", e);
///     });
///     tokio::run(task);
/// }
/// ```
pub fn write<P, C: AsRef<[u8]>>(path: P, contents: C) -> WriteFile<P, C>
where
    P: AsRef<Path> + Send + 'static,
{
    WriteFile {
        state: State::Create(File::create(path), Some(contents)),
    }
}

/// A future used to open a file for writing and write the entire contents
/// of some data to it.
#[derive(Debug)]
pub struct WriteFile<P: AsRef<Path> + Send + 'static, C: AsRef<[u8]>> {
    state: State<P, C>,
}

#[derive(Debug)]
enum State<P: AsRef<Path> + Send + 'static, C: AsRef<[u8]>> {
    Create(file::CreateFuture<P>, Option<C>),
    Write(tokio_io::io::WriteAll<File, C>),
}

impl<P: AsRef<Path> + Send + 'static, C: AsRef<[u8]> + fmt::Debug> Future for WriteFile<P, C> {
    type Item = C;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let new_state = match &mut self.state {
            State::Create(ref mut create_file, contents) => {
                let file = try_ready!(create_file.poll());
                let write = tokio_io::io::write_all(file, contents.take().unwrap());
                State::Write(write)
            }
            State::Write(ref mut write) => {
                let (_, contents) = try_ready!(write.poll());
                return Ok(Async::Ready(contents));
            }
        };

        mem::replace(&mut self.state, new_state);
        // We just entered the Write state, need to poll it before returning.
        self.poll()
    }
}
