use super::TeeWriter;

#[cfg(feature = "futures")]
impl<This, Other> futures::io::AsyncWrite for TeeWriter<This, Other>
where
    This: futures::io::AsyncWrite + Unpin,
    Other: futures::io::AsyncWrite + Unpin,
{
    fn poll_write(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
        buf: &[u8],
    ) -> core::task::Poll<futures::io::Result<usize>> {
        use core::task::Poll::*;
        if self.offset > 0 {
            let offset = self.offset as usize;
            let offset = core::cmp::min(offset, buf.len());
            let buf = &buf[offset..];
            let written = core::pin::Pin::new(&mut self.this).poll_write(cx, buf)?;
            if let Ready(written) = written {
                if written == 0 {
                    return Pending;
                }
                self.offset -= written as isize;
            }
            return written.map(Ok);
        } else if self.offset < 0 {
            let offset = (-self.offset) as usize;
            let offset = core::cmp::min(offset, buf.len());
            let buf = &buf[offset..];
            let written = core::pin::Pin::new(&mut self.other).poll_write(cx, buf)?;
            if let Ready(written) = written {
                if written == 0 {
                    return Pending;
                }
                self.offset += written as isize;
            }
            return written.map(Ok);
        }
        let this_written = core::pin::Pin::new(&mut self.this).poll_write(cx, buf)?;
        let other_written = core::pin::Pin::new(&mut self.other).poll_write(cx, buf)?;

        match (this_written, other_written) {
            (Ready(this), Ready(other)) => {
                if this == other {
                    Ready(Ok(this))
                } else {
                    let offset = this as isize - other as isize;
                    self.offset += offset;
                    let min = core::cmp::min(this, other);
                    // next call should write to the one that wrote less
                    Ready(Ok(min))
                }
            }
            (Ready(this), Pending) => {
                // next call should write to other
                self.offset = this as isize;
                Pending
            }
            (Pending, Ready(other)) => {
                // next call should write to this
                self.offset = -(other as isize);
                Pending
            }
            (Pending, Pending) => Pending,
        }
    }

    fn poll_flush(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
    ) -> core::task::Poll<futures::io::Result<()>> {
        let this_flushed = core::pin::Pin::new(&mut self.this).poll_flush(cx)?;
        let other_flushed = core::pin::Pin::new(&mut self.other).poll_flush(cx)?;

        if this_flushed.is_ready() && other_flushed.is_ready() {
            core::task::Poll::Ready(Ok(()))
        } else {
            core::task::Poll::Pending
        }
    }

    fn poll_close(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
    ) -> core::task::Poll<futures::io::Result<()>> {
        let this_close = core::pin::Pin::new(&mut self.this).poll_close(cx)?;
        let other_close = core::pin::Pin::new(&mut self.other).poll_close(cx)?;

        if this_close.is_ready() && other_close.is_ready() {
            core::task::Poll::Ready(Ok(()))
        } else {
            core::task::Poll::Pending
        }
    }
}

// #[cfg(feature = "futures")]
// #[cfg(test)]
// #[futures::test]
// async fn test_poll_uneven() {
//     let sink = futures::io::sink();
//     use futures::io::AsyncWriteExt;
//     let mut stream = futures::io::SimplexStream::new_unsplit(5).tee(sink);
//     stream.write_all(b"Hello, world!").await.unwrap();
// }
