package com.app.downloadstream1;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Process;
import android.util.Log;


public class DownloadManager {
	public static final String TAG = "DownloadManager";
	protected  static ConnectivityManager mConnMan = null;
	private static HttpClient mClient;
	
	private Download curDownload;
	private String curUrl;
	
	private Context ctx;
	private boolean isDownloading = false;
	
	private static final int CONNECT_TIMEOUT = 20 * 1000;
    private static final int READ_TIMEOUT = 15 * 1000;
    /**
    public static final int STATE_UNKNOWN = 0;
    public static final int STATE_CONNECTING = 7;
    public static final int STATE_CONNECTED = 8;
    public static final int STATE_PAUSED_LOCAL_FAILURE = 1;
    public static final int STATE_HTTP_ERROR = 2;
    public static final int STATE_PAUSED_REMOTE_FAILURE = 3;
    public static final int STATE_FILE_ERROR = 4;
    public static final int STATE_ABORTED = 5;
    public static final int STATE_TOO_MANY_RETRIES = 6;
    */
	
	public DownloadManager(Context context) {
		ctx = context;
		mClient = this.newClient(null);
	}
	
	public Download startDownload(String url, String path)
	  throws IOException
	{
		Download d = new Download(this, url, path);
		setCurrentDownload(d, curUrl);
		d.start();
		isDownloading = true;
		return d;
	}
	
	public void stopDownload() {
		if(curDownload != null) {
			curDownload.interrupt();
			resetCurrentDownload();
			isDownloading = false;
		}
	}
	
	public boolean isDownloading(String url) {
		return isDownloading;
	}
	
	public Download getCurrentDownload() {
		return curDownload;
	}
	
	private void resetCurrentDownload() {
		this.curDownload = null;
		this.curUrl = null;
	}
	
	private void setCurrentDownload(Download d, String url) {
		this.curDownload = d;
		this.curUrl = url;
	}
	
	private static HttpClient newClient(String userAgent) {
		HttpParams params = new BasicHttpParams();
		
        // Turn off stale checking.  Our connections break all the time anyway,
        // and it's not worth it to pay the penalty of checking every time.
        HttpConnectionParams.setStaleCheckingEnabled(params, false);

        // Default connection and socket timeout of 10 seconds.  Tweak to taste.
        HttpConnectionParams.setConnectionTimeout(params, CONNECT_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, READ_TIMEOUT);
        HttpConnectionParams.setSocketBufferSize(params, 8192);

        // Don't handle redirects -- return them to the caller.  Our code
        // often wants to re-POST after a redirect, which we must do ourselves.
        HttpClientParams.setRedirecting(params, false);

        // Set the specified user agent and register standard protocols.
        if (userAgent != null)
        	HttpProtocolParams.setUserAgent(params, userAgent);
        
        mClient = new DefaultHttpClient(params);
        return mClient;
    }
	
	public boolean isNetworkAvailable() {
    	mConnMan = (ConnectivityManager)ctx.getSystemService
		  (Context.CONNECTIVITY_SERVICE);
    	NetworkInfo info = mConnMan.getActiveNetworkInfo();
    	if (info == null)
			return false;
    	return info.isConnected();
    }
	
	public void onStateChange(String url, int state, String message) {
		
	}
	
	public void onFinished(String mUrl) {
		Log.i(TAG, "Download of " + mUrl + " has finished.");
	}
	
	public void onProgressUpdate(String mUrl, int progress) {
		Log.i(TAG, "Download progress of " + mUrl + ": " + progress + "%");
	}
	
	public static class Download extends Thread {
		private static final int BUFFER_SIZE = 2048;
		
		private final String mUrl;
		private final File mDest;
		
		private final FileOutputStream mOut;
		private HttpGet mMethod;
		
		private int mAttempts = 0;
		private long mResumeFrom;
		private long mLength = -1;
		private long mBytes = 0;
		private int mLastProgress = 0;
		private boolean mPostResponse;
		private final Object mResponseLock = new Object();
	    private final DownloadManager mManager;
	    
	    private Download(DownloadManager mgr, String url, String path,
	  		  long resumeFrom)
	  		  throws IOException
	  		{
	  			mManager = mgr;
	  			mUrl = url;
	  			mDest = new File(path);

	  			mResumeFrom = resumeFrom;
	  			mOut = new FileOutputStream(path, (resumeFrom > 0));
	  		}
	    
	    private Download(DownloadManager mgr, String url, String path)
		  throws IOException
		{
			this(mgr, url, path, 0);
		}
	    
	    private void tryDownload() throws Exception {
	    	HttpGet method = new HttpGet(mUrl);
	    	synchronized(this) {
				mMethod = method;
			}
	    	InputStream in = null;
	    	
	    	try {
	    		HttpEntity ent = null;
	    		try {
	    			HttpClient client;
	    			synchronized(this) {
						client = mClient;
					}
	    			
	    			HttpResponse resp = client.execute(mMethod);
	    			
	    			StatusLine status = resp.getStatusLine();
					int statusCode = status.getStatusCode();
					
	    			if (statusCode != HttpStatus.SC_OK)
						throw new IOException("HTTP GET failed: " + status);
	    			
	    			if ((ent = resp.getEntity()) == null)
						throw new IOException("No entity?");
	    			
	    			if (mResumeFrom == 0)
						mLength = ent.getContentLength();
	    		}finally {
	    			synchronized(mResponseLock) {
	    				mPostResponse = true;
		    			mResponseLock.notify();
	    			}
	    			
	    		}
	    		
	    		in = ent.getContent();
	    		
	    		byte[] b = new byte[BUFFER_SIZE];
				mBytes = mResumeFrom;
				int n;
				while ((n = in.read(b)) >= 0)
				{
					
					try {
						mOut.write(b, 0, n);
					} catch (IOException e) {
						System.out.println(e.toString());
					}

					mBytes += n;

					
					int progress = (int)
					  (((float)mBytes / (float)mLength) * 100f);

					if (progress > mLastProgress)
					{
						mManager.onProgressUpdate(mUrl, progress);
						mLastProgress = progress;
					}
					
				}
				if (mBytes < mLength)
					throw new HttpException("Server didn't send as much as it said it would.");
			} catch (HttpException e) {
				System.out.println("STATE_HTTP_ERROR: " + e.toString());
				throw e;
			}catch (IOException e) {
				if (mManager.isNetworkAvailable() == false)
					System.out.println("Network not available");
				else
					System.out.println("STATE_PAUSED_REMOTE_FAILURE");
				throw e;
			} finally {
				synchronized(this) {
					mMethod = null;
				}

				if (in != null)
					try { in.close(); } catch (IOException e) {}
			}
		}
	    
	    public void waitForResponse() {
	    	synchronized(mResponseLock) {
	    		while (isAlive() == true && mPostResponse == false)
				{
					try {
						mResponseLock.wait(); 
					} catch (InterruptedException e) {
						Log.e(TAG, "error: " + e.toString());
					}
				}
	    	}
	    	
	    }
	    
        /** All get methods */
	    
	    public String getUrl()
		{
			return mUrl;
		}

		public File getDestination()
		{
			return mDest;
		}
		
		public long getContentLength()
		{
			return mLength;
		}
		
		public void run() {
			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

			for (int i = 0; i < 2; i++) {
				try {
					tryDownload();
					mManager.onFinished(mUrl);
					break;
			    }catch(Exception e) {
				    System.out.println("Download of " + mUrl + " failed." +
						"Info: " + e.toString());
			    }
	        }
		}
	}
}