package com.qubling.sidekick.fetch.other;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import com.qubling.sidekick.fetch.AbstractFetcher;
import com.qubling.sidekick.fetch.SerialUpdateFetcher;
import com.qubling.sidekick.fetch.UpdateFetcher;
import com.qubling.sidekick.instance.Gravatar;
import com.qubling.sidekick.model.Model;
import com.qubling.sidekick.search.ResultSet;
import com.qubling.sidekick.search.ResultsForUpdate;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

public class GravatarFetcher extends AbstractFetcher<Gravatar> implements UpdateFetcher<Gravatar> {
	
	private float gravatarDpSize;
	private int timeoutAbsolute;
	
    private static final int TIMEOUT_CONNECTION = 2000;
    private static final int TIMEOUT_SOCKET = 3000;
    
    private static final String GRAVATAR_TOOK_TOO_LONG = "Gravatar took too long";
//    private static int requestCounter = 0;
    
    public static final int DEFAULT_TIMEOUT_ABSOLUTE = 3100;
    
    public GravatarFetcher(Model<Gravatar> model, float gravatarDpSize, int timeoutAbsolute) {
    	super(model);
    	
    	this.gravatarDpSize = gravatarDpSize;
    	this.timeoutAbsolute = timeoutAbsolute;
    }
    
    public GravatarFetcher(Model<Gravatar> model, float gravatarDpSize) {
    	this(model, gravatarDpSize, DEFAULT_TIMEOUT_ABSOLUTE);
    }
    
    @Override
    public boolean needsUpdate(Gravatar gravatar) {
//    	Log.d("GravatarFetcher", "Needs update? " + (gravatar.getBitmap() == null));
    	return gravatar.getBitmap() == null;
    }
    
	public float getGravatarDpSize() {
    	return gravatarDpSize;
    }

	public int getTimeoutAbsolute() {
    	return timeoutAbsolute;
    }

	@Override
    protected void execute() {
//		Log.d("GravatarFetcher", "START execute()");

        // Calculate the pixel size of the Gravatar
        Context context = getContext();
        int gravatarPixelSize = Math.min(
                (int) (gravatarDpSize * context.getResources().getDisplayMetrics().density + 0.5f),
                512);
			
		ResultSet<Gravatar> inputResults = getResultSet();
		
		for (Gravatar gravatar : inputResults) {
			try {
				Bitmap bitmap = fetchBitmap(gravatar.getUrl(gravatarPixelSize));
	            gravatar.setBitmap(bitmap);
//	            Log.d("GravatarFetcher", "Fetched " + gravatar.getUrl(gravatarPixelSize));
			}
			catch (RuntimeException e) {
				Log.e("GravatarFetcher", "error fetching Gravatar", e);
			}
		}
		
//		Log.d("GravatarFetcher", "END execute()");
    }
	
	@Override
	public void setIncomingResultSet(ResultsForUpdate<Gravatar> inputResults) {
		setResultSet(inputResults);
	}

    private Bitmap fetchBitmap(final String gravatarURL) {
        
        try {

        	// Make sure we don't get stuck waiting for a Gravatar
            HttpParams httpParams = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_CONNECTION);
            HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_SOCKET);

            // Prepare the request
//            Log.d("AuthorByDistributionSearch", "Gravatar: " + resizedGravatarURL);
            HttpClient httpClient = getHttpClient();
            final HttpGet req = new HttpGet(gravatarURL);
            req.setParams(httpParams);
            
            // Start the absolute timer for the request
//            final int reqId = ++requestCounter;
//            final Date ts = new Date();
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
				@Override
				public void run() {
//					long elapsed = new Date().getTime() - ts.getTime();
//					Log.w("GravatarFetcher", "Gravatar request #" + reqId + " took too long (" + elapsed + " s), aborting fetch: " + resizedGravatarURL);
					Log.w("GravatarFetcher", "Gravatar request took too long, aborting fetch: " + gravatarURL);
					req.abort();
					throw new RuntimeException(GRAVATAR_TOOK_TOO_LONG);
				}
			}, timeoutAbsolute);
            
            // Do the request
            HttpResponse res = httpClient.execute(req);
            
            // Request is finished, stop the timer
//            long elapsed = new Date().getTime() - ts.getTime();
//            Log.d("GravatarFetcher", "Gravatar request #" + reqId + " finished executing request (" + elapsed + " s)");
            timer.cancel();

            // Get the response content
            HttpEntity entity = res.getEntity();
            InputStream content = entity.getContent();
            Bitmap gravatarBitmap = BitmapFactory.decodeStream(content);
            return gravatarBitmap;
        }
        
        catch (InterruptedIOException e) {
        	// No message, since this probably means Gravatar took too long
        	return null;
        }

        catch (IOException e) {
            Log.e("GravatarFetcher", "Error loading Gravatar: " + e, e);
            return null;
        }

        // During testing, I sometimes get an IllegalStateException: Connection is not open.
        // This is a stupid exception and nearly any illegal state exception we can ignore by
        // just not loading the Gravatar. The bitmap is not *that* important.
        catch (IllegalStateException e) {
        	Log.e("GravatarFetcher", "Error fetching Gravatar: " + e, e);
        	return null;
        }
        
        catch (RuntimeException e) {
        	if (GRAVATAR_TOOK_TOO_LONG.equals(e.getMessage())) {
        		return null;
        	}
        	else {
        		throw e;
        	}
        }
    }
    
    public String toString() {
    	return getModel() + ":GravatarFetch(" + gravatarDpSize + ";" + getResultSet() + ")";
    }

	@Override
	public SerialUpdateFetcher<Gravatar> thenDoFetch(UpdateFetcher<Gravatar> fetcher) {
		return super.thenDoFetch(fetcher);
	}
}

@@ -3151,32 +3151,67 @@
 URL) %7B%0A        %0A
+        Timer timer = new Timer();%0A
         try %7B%0A%0A 
@@ -3208,16 +3208,16 @@
   try %7B%0A
-
 %0A       
@@ -3931,47 +3931,8 @@
 ();%0A
-            Timer timer = new Timer();%0A
     
@@ -4528,276 +4528,8 @@
    %0A
-            // Request is finished, stop the timer%0A//            long elapsed = new Date().getTime() - ts.getTime();%0A//            Log.d(%22GravatarFetcher%22, %22Gravatar request #%22 + reqId + %22 finished executing request (%22 + elapsed + %22 s)%22);%0A            timer.cancel();%0A%0A
     
@@ -5672,16 +5672,16 @@
 hrow e;%0A
-
         
@@ -5689,24 +5689,322 @@
 %7D%0A        %7D%0A
+        %0A        finally %7B%0A            // Request is finished, stop the timer%0A//          long elapsed = new Date().getTime() - ts.getTime();%0A//          Log.d(%22GravatarFetcher%22, %22Gravatar request #%22 + reqId + %22 finished executing request (%22 + elapsed + %22 s)%22);%0A          timer.cancel();%0A        %7D%0A
     %7D%0A    %0A 

Fix a bug where the timer does not get shut down on exception