package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;

import jp.gr.java_conf.shiseissi.commonlib.FileUtil;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicLineFormatter;
import org.apache.http.protocol.HttpContext;

import android.os.Handler;

public abstract class HttpXmlLoader extends XmlLoader {
//	private static final boolean DEBUG_LOGV = Release.IS_DEBUG & true;
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

//	private HttpUriRequest mHttpRequest = null;
    private AtomicReference<InputStream> mInDownload =
        new AtomicReference<InputStream>();
    private DefaultHttpClient mHttpClient;

	protected HttpXmlLoader() {
	    super();
	}
	protected HttpXmlLoader(Handler handler) {
	    super(handler);
	}

	protected abstract HttpUriRequest createRequest();

	@Override
	public void run() {
		DefaultHttpClient httpClient = createHttpClient();
		mHttpClient = httpClient;
		httpClient.getCookieStore().clear();

		mNetworkIORetryCount = NETWORK_IO_RETRY_COUNT;

        while (!mIsFinish) {
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, Log.buf()
                        .append("mNetworkIORetryCount=").append(mNetworkIORetryCount)
                        .toString());
            }
            if (!loadMain()) {
                break;
            }
            if (mIsFinish) {
                break;
            }
            // いったん破棄されるので再生成
            mHttpClient = createHttpClient();
        }
	}

	/**
	 * ロード処理本体
	 * @return trueならリトライの可能性、falseなら完全に終了
	 */
	private boolean loadMain() {
        DefaultHttpClient httpClient = mHttpClient;
		InputStream inDownload = null;
		HttpEntity entityInput = null;

		try {
			HttpUriRequest httpRequest = createRequest();
//			mHttpRequest = httpRequest;
			HttpResponse httpResponse = HttpManager.executeWrapper(httpClient,
			        httpRequest);
            StatusLine statusLine = httpResponse.getStatusLine();
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, "XmlLoader HTTP response>");
                Log.d(LOG_TAG, BasicLineFormatter.formatStatusLine(statusLine, null));
				Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			}

            // ネットワーク接続成功したらカウントリセット
            mNetworkIORetryCount = NETWORK_IO_RETRY_COUNT;

			int httpStatusCode = statusLine.getStatusCode();
			if (httpStatusCode == HttpStatus.SC_OK) {
				entityInput = httpResponse.getEntity();
				inDownload = entityInput.getContent();
				mInDownload.set(inDownload);

				boolean result = readAndCreateData(inDownload);

				if (result) {
					dispatchOnFinished();
				} else {
					dispatchOnOccurredError(getXmlParseErrorString());
				}
			} else {
				// エラー
				String errorMessage = "HTTP Status Code: " + httpStatusCode;
				dispatchOnOccurredError(errorMessage);
			}

		} catch (UnsupportedEncodingException e) {
			String errorMessage = e.toString();
            Log.d(LOG_TAG, errorMessage, e);
			dispatchOnOccurredError(errorMessage);
		} catch (ClientProtocolException e) {
			String errorMessage = e.toString();
            Log.d(LOG_TAG, errorMessage, e);
            --mNetworkIORetryCount;
            if (mNetworkIORetryCount >= 0) {
                // ネットワークエラーはひとまずリトライ
                waitForNetworkError();
                return true;
            } else {
                dispatchOnOccurredError(errorMessage);
            }
		} catch (IOException e) {
			String errorMessage = e.toString();
            Log.d(LOG_TAG, errorMessage, e);
            --mNetworkIORetryCount;
            if (mNetworkIORetryCount >= 0) {
                // ネットワークエラーはひとまずリトライ
                waitForNetworkError();
                return true;
            } else {
                dispatchOnOccurredError(errorMessage);
            }
        } catch (IllegalStateException e) {
            // HttpClientを強制シャットダウンしたときに飛んでくる可能性
            String errorMessage = e.toString();
            Log.d(LOG_TAG, errorMessage, e);
            --mNetworkIORetryCount;
            if (mNetworkIORetryCount >= 0) {
                // ネットワークエラーはひとまずリトライ
                waitForNetworkError();
                return true;
            } else {
                dispatchOnOccurredError(errorMessage);
            }
		} finally {
//            mHttpRequest = null;
            if (entityInput != null) {
                try {
                    entityInput.consumeContent();
                } catch (IOException e) {
                    Log.e(LOG_TAG, e.toString(), e);
                }
            }
            mHttpClient.getConnectionManager().shutdown();
            // 同時にcloseすると不具合出る可能性があるので一回のみに
			if (inDownload != null && mInDownload.getAndSet(null) != null) {
			    FileUtil.closeIgnoreException(inDownload);
			}
		}
		return false;
	}

	@Override
	protected void shutdownNetwork() {
	    if (mHttpClient != null) {
	        mHttpClient.getConnectionManager().shutdown();
	    }
//        Util.abortHttpUriRequest(mHttpRequest);
//	    InputStream inDownload = mInDownload.getAndSet(null);
//	    if (inDownload != null) {
//	        FileUtil.closeIgnoreException(inDownload);
//	    }
	}

	protected abstract String getXmlParseErrorString();

    private DefaultHttpClient createHttpClient() {
        DefaultHttpClient httpClient = Util.createHttpClient();

        httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
            @Override
            public void process(HttpRequest httprequest, HttpContext httpcontext) throws HttpException,
                    IOException {
                if (!httprequest.containsHeader("Accept-Encoding")) {
                    httprequest.addHeader("Accept-Encoding", "gzip");
                }
            }
        });
        httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
            @Override
            public void process(HttpResponse httpresponse, HttpContext httpcontext) throws HttpException,
                    IOException {
                HttpEntity entity = httpresponse.getEntity();
                if (entity != null) {
                    Header contentEncoding = entity.getContentEncoding();
                    if (contentEncoding != null) {
                        HeaderElement[] codecs = contentEncoding.getElements();
                        for (HeaderElement he : codecs) {
                            if ("gzip".equalsIgnoreCase(he.getName())) {
                                httpresponse.setEntity(new GzipDecompressingEntity(httpresponse.getEntity()));
                            }
                        }
                    }
                }
            }
        });

        return httpClient;
    }

    private static class GzipDecompressingEntity extends HttpEntityWrapper {
        public GzipDecompressingEntity(HttpEntity entity) {
            super(entity);
        }

        @Override
        public long getContentLength() {
            return -1;
        }

        @Override
        public InputStream getContent() throws IOException {
            InputStream in = wrappedEntity.getContent();
            return new GZIPInputStream(in);
        }
    }
}
