package com.scanvine.android.ui;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

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.impl.client.DefaultHttpClient;
import org.json.JSONObject;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

import com.scanvine.android.R;
import com.scanvine.android.model.Source;
import com.scanvine.android.model.Story;
import com.scanvine.android.util.Util;

/**
 * A list fragment representing a list of Stories. This fragment also supports
 * tablet devices by allowing list items to be given an 'activated' state upon
 * selection. This helps indicate which item is currently being viewed in a
 * {@link StoryDetailFragment}.
 * <p>
 * Activities containing this fragment MUST implement the {@link Callbacks} interface.
 */
public class StoryListFragment extends ListFragment {

	private static final String STATE_ACTIVATED_POSITION = "activated_position"; //only used on tables
	private int mActivatedPosition = ListView.INVALID_POSITION; //only used on tables
	private Callbacks mCallbacks = sDummyCallbacks;
	private ArrayList<Story> stories = new ArrayList<Story>();

	public interface Callbacks {
		public void onItemSelected(String json);
	    public void showProgress(Boolean progress);
	}

	// A dummy implementation for use when not attached to an activity.
	private static Callbacks sDummyCallbacks = new Callbacks() {
		@Override
		public void onItemSelected(String json) {}
	    public void showProgress(Boolean progress) {}
	};

	// Mandatory empty constructor for the fragment manager to instantiate the fragment.
	public StoryListFragment() { }

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		if (!(activity instanceof Callbacks))
			throw new IllegalStateException("Activity must implement fragment's callbacks.");
		mCallbacks = (Callbacks) activity;
	}

	@Override
	public void onDetach() {
		super.onDetach();
		mCallbacks = sDummyCallbacks;
	}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ListView lv = (ListView) inflater.inflate(R.layout.fragment_story_list, container, false);
        lv.setDivider(null);
        lv.setDividerHeight(0);
		return lv;

    }
    
    @Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		String slug = null;
		if (getActivity().getIntent().getExtras()!=null) {  
			String json = getActivity().getIntent().getExtras().getString(StoryListActivity.SOURCE);
			Source source = Source.GetSourceFor(json);
			slug = source.getSlug();
		}
		refreshList(null, null, slug);
	}
	
    @Override
	public void onViewCreated(View view, Bundle savedInstanceState) {
		super.onViewCreated(view, savedInstanceState);
		if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION))
			setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
	}

	@Override
	public void onListItemClick(ListView listView, View view, int position,
			long id) {
		super.onListItemClick(listView, view, position, id);
		mCallbacks.onItemSelected(stories.get(position).getJSON());
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		if (mActivatedPosition != ListView.INVALID_POSITION) {
			// Serialize and persist the activated item position.
			outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
		}
	}

	private void renderStories() {
		Activity activity = getActivity();
		if (activity!=null)
			setListAdapter(new StoryArrayAdapter(activity, this, R.id.story_list, stories));
	}
	
	public void showDownloads() {
		new ListDownloadsTask().execute();
	}

	public void selectItem(int n) {
		String json = stories.get(n).getJSON();
		mCallbacks.onItemSelected(json);
	}
	
	public void refreshList(String time, String section, String source) {
		refreshList(time, section, source, false);
	}

	public void refreshListFromCache(String time, String section, String source) {
		refreshList(time, section, source, true);
	}

	private void refreshList(String time, String section, String source, boolean cached) {
		if (source!=null && "Firehose".equalsIgnoreCase(time))
			time = null;
		if (source==null && "Latest".equalsIgnoreCase(time))
			time = null;
		if (time==null && section==null && source==null)
			time = "Latest";
		new StoryFetchTask().execute(time, section, source, cached ? "cached" : null);
	}

	public void setActivateOnItemClick(boolean activateOnItemClick) {
		getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE);
	}

	private void setActivatedPosition(int position) {
		if (position == ListView.INVALID_POSITION)
			getListView().setItemChecked(mActivatedPosition, false);
		else
			getListView().setItemChecked(position, true);
		mActivatedPosition = position;
	}

	public final class StoryFetchTask extends AsyncTask<String, Boolean, String> {
		@Override
		protected String doInBackground(String... params) {
	        Log.i(""+this, "Fetching stories, params: "+Arrays.toString(params)+", online: "+Util.AreWeOnline(getActivity()));
		    publishProgress(true);
		    String result = null;
		    String time = (params==null || params.length<1 ? null : params[0]);
		    String section = (params==null || params.length<2 ? null : params[1]);
		    String source = (params==null || params.length<3 ? null : params[2]);
		    String url = "http://www.scanvine.com/api/1";

		    if (source!=null && source.length()>0)
		    	url += "/source/"+source;
		    else if (section!=null && section.length()>0)
				url += "/"+section;
		    if (time!=null && time.length()>0)
		    	url += "/"+time;

		    String fileName = Util.ConvertUrlToFilename(url);
            File storiesFile = new File(getActivity().getCacheDir(), fileName);

            if (storiesFile.exists()) {
            	boolean useCache = !Util.AreWeOnline(getActivity());
            	useCache = useCache || params.length>3 && "cached".equalsIgnoreCase(params[3]);
            	long age = System.currentTimeMillis() - storiesFile.lastModified();
            	useCache = useCache || (age>0 && age < 600*1000);
                if (useCache) {
                	Log.i(""+this, "Using cached stories file");
                	result = Util.ConvertFileToString(storiesFile);
                	return result;
                }
            }

		    HttpClient httpclient = new DefaultHttpClient();
	        Log.i(""+this, "About to fetch "+url);
		    try {
			    HttpGet httpget = new HttpGet(url); 
		    	HttpResponse response = httpclient.execute(httpget);
		        Log.i(""+this, "Fetch result: "+response);
		        HttpEntity entity = response.getEntity();
		        if (entity != null) {
		            InputStream instream = entity.getContent();
		            result = Util.ConvertStreamToString(instream);
		            instream.close();
		        }
		    }
		      catch (org.apache.http.NoHttpResponseException ex) {
                if (storiesFile.exists())
                	result = Util.ConvertFileToString(storiesFile);
                return result;
		    } catch (Exception ex) {
		    	Log.e(""+this, "Fetch error: "+ex);
		    	ex.printStackTrace();
		    }
		    
		    if (result!=null && result.length()>0) {
			    //write to cache
		        try {
		            FileWriter out = new FileWriter(storiesFile);
		            out.write(result);
		            out.close();
		            Log.i(""+this, "Cached results to "+storiesFile);
		        } catch (IOException ex) {
		            Log.e(""+this, "Write error: "+ex);
		            ex.printStackTrace();
		        }
		    }
		    return result;
		}
		
	    @Override
		protected void onProgressUpdate(Boolean... progress) {
		    mCallbacks.showProgress(progress[0]);
		}

		@Override
		protected void onPostExecute(String result) {
		    publishProgress(false);
		    if (result!=null && result.length()>0) {
		    	Log.i(""+this, "Got response of length "+result.length());
			    stories = Story.GetStoriesFrom(result);
		    }
		    else
		    	stories = new ArrayList<Story>();
		    renderStories();
		}
	}

	public final class ListDownloadsTask extends AsyncTask<String, Boolean, String> {
		@Override
		protected String doInBackground(String... params) {
		    publishProgress(true);
			File[] files = getActivity().getFilesDir().listFiles();
			Arrays.sort(files, new Comparator<File>() {
			    public int compare(File f1, File f2) {
			    	return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
			    }
			});

			ArrayList<Story> downloads = new ArrayList<Story>();
			for (File child : files) {
				if (child.getName().startsWith(Story.FILE_PREFIX)) {
					Log.i(""+this, "Found "+child);
					String contents = Util.ConvertFileToString(child);
					try {
						JSONObject json = new JSONObject(contents);
						Story story = Story.GetStoryFor(json.getString("story"));
						downloads.add(story);
					}
					catch(Exception ex) {
						Log.e(""+this, "Couldn't get story from "+child);
					}
				}
			}

			stories = downloads;
		    return "done";
		}
		
	    @Override
		protected void onProgressUpdate(Boolean... progress) {
		    mCallbacks.showProgress(progress[0]);
		}

		@Override
		protected void onPostExecute(String result) {
		    publishProgress(false);
		    renderStories();
		}
	}
}