package ru.yandex.webmaster3.validator.common;

import com.ning.http.client.AsyncHandler;
import com.ning.http.client.HttpResponseBodyPart;
import com.ning.http.client.HttpResponseHeaders;
import com.ning.http.client.HttpResponseStatus;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.SynchronousQueue;

/**
 * @author leonidrom
 */
public class PipedAsyncHandler implements AsyncHandler {
    private static final Logger log = LoggerFactory.getLogger(PipedAsyncHandler.class);
    private static final long SIZE_LIMIT = 24_000_000_000L;

    private final CountDownLatch pipeCreatedLatch = new CountDownLatch(1);

    private final URL url;

    private Pipe pipe = null;
    private Throwable throwable = null;
    private long feedLength = 0;

    public PipedAsyncHandler(URL url) {
        this.url = url;
    }

    @Override
    public STATE onStatusReceived(HttpResponseStatus status) throws Exception {
        int statusCode = status.getStatusCode();
        log.debug("Status code: "  + statusCode);
        if (statusCode != 200) {
            throw new IllegalStateException("status code: "  + statusCode);
        }

        return STATE.CONTINUE;
    }

    @Override
    public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
        pipe = new Pipe(url, headers);
        pipeCreatedLatch.countDown();

        return STATE.CONTINUE;
    }

    /**
     *  Ждем пока другой поток не создаст pipe
     */
    public InputStream waitForInputStream() throws Throwable {
        pipeCreatedLatch.await();
        if (throwable != null) {
            throw throwable;
        }

        return pipe.getInputStream();
    }

    @Override
    public void onThrowable(Throwable t) {
        log.debug("Done: FAIL");

        throwable = t;
        if (pipe != null) {
            IOUtils.closeQuietly(pipe);
        }

        pipeCreatedLatch.countDown();
    }

    @Override
    public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
        int partLength = bodyPart.length();

        if (feedLength > SIZE_LIMIT) {
            log.error("Too long feed: {}, {} bytes", url.toExternalForm(), feedLength);
            throw new IllegalStateException("Too long feed: " + feedLength);
        }

        feedLength += partLength;
        pipe.write(bodyPart.getBodyPartBytes());

        return STATE.CONTINUE;
    }

    @Override
    public Object onCompleted() throws Exception {
        log.debug("Done: OK");

        if (pipe != null) {
            IOUtils.closeQuietly(pipe);
        }

        return "";
    }
}
