use bytecodec::bytes::BytesEncoder;
use bytecodec::io::IoEncodeExt;
use bytecodec::json_codec::JsonEncoder;
use bytecodec::{ByteCount, Encode, Eos, ErrorKind, Result};
use fibers_tasque::{AsyncCall, DefaultCpuTaskQueue, TaskQueueExt};
use futures::{Async, Future};
use std::marker::PhantomData;

use http::HttpResult;
use Error;

#[derive(Debug, Default)]
pub struct AsyncEncoder<E> {
    rx: Option<AsyncCall<Result<Vec<u8>>>>,
    bytes: BytesEncoder<Vec<u8>>,
    _inner: PhantomData<E>,
}
impl<E: Encode + Default> Encode for AsyncEncoder<E>
where
    E::Item: Send + 'static,
{
    type Item = E::Item;

    fn encode(&mut self, buf: &mut [u8], eos: Eos) -> Result<usize> {
        if let Some(mut rx) = self.rx.take() {
            match rx.poll() {
                Err(e) => {
                    track_panic!(ErrorKind::Other, "{}", e);
                }
                Ok(Async::NotReady) => {
                    self.rx = Some(rx);
                    return Ok(0);
                }
                Ok(Async::Ready(result)) => {
                    let bytes = track!(result)?;
                    track!(self.bytes.start_encoding(bytes))?;
                }
            }
        }
        track!(self.bytes.encode(buf, eos))
    }

    fn start_encoding(&mut self, item: Self::Item) -> Result<()> {
        track_assert!(self.is_idle(), ErrorKind::EncoderFull);
        self.rx = Some(DefaultCpuTaskQueue.async_call(move || {
            let mut buf = Vec::new();
            let mut encoder = E::default();
            track!(encoder.start_encoding(item))?;
            track!(encoder.encode_all(&mut buf))?;
            Ok(buf)
        }));
        Ok(())
    }

    fn is_idle(&self) -> bool {
        self.rx.is_none() && self.bytes.is_idle()
    }

    fn requiring_bytes(&self) -> ByteCount {
        if self.rx.is_some() {
            ByteCount::Unknown
        } else {
            self.bytes.requiring_bytes()
        }
    }
}

#[derive(Debug)]
pub enum ObjectResultEncoder {
    Ok(BytesEncoder<Vec<u8>>),
    Err(JsonEncoder<Error>),
}
impl Encode for ObjectResultEncoder {
    type Item = HttpResult<Vec<u8>>;

    fn encode(&mut self, buf: &mut [u8], eos: Eos) -> Result<usize> {
        match *self {
            ObjectResultEncoder::Ok(ref mut e) => track!(e.encode(buf, eos)),
            ObjectResultEncoder::Err(ref mut e) => track!(e.encode(buf, eos)),
        }
    }

    fn start_encoding(&mut self, item: Self::Item) -> Result<()> {
        track_assert!(self.is_idle(), ErrorKind::EncoderFull);
        match item {
            HttpResult::Ok(v) => {
                let mut e = BytesEncoder::default();
                track!(e.start_encoding(v))?;
                *self = ObjectResultEncoder::Ok(e);
            }
            HttpResult::Err(v) => {
                let mut e = JsonEncoder::default();
                track!(e.start_encoding(v))?;
                *self = ObjectResultEncoder::Err(e);
            }
        }
        Ok(())
    }

    fn is_idle(&self) -> bool {
        match *self {
            ObjectResultEncoder::Ok(ref e) => e.is_idle(),
            ObjectResultEncoder::Err(ref e) => e.is_idle(),
        }
    }

    fn requiring_bytes(&self) -> ByteCount {
        match *self {
            ObjectResultEncoder::Ok(ref e) => e.requiring_bytes(),
            ObjectResultEncoder::Err(ref e) => e.requiring_bytes(),
        }
    }
}
impl Default for ObjectResultEncoder {
    fn default() -> Self {
        ObjectResultEncoder::Ok(Default::default())
    }
}
