//	Contour - Android drawing teacher application
//	Copyright (C) 2014  Johannes Riedmann
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//	
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.

package com.j0h1.contour;

import java.util.ArrayList;
import java.util.List;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.Volley;
import com.j0h1.contour.R;
import com.j0h1.contour.database.ContourDatabase;
import com.j0h1.contour.pojo.Player;
import com.j0h1.contour.pojo.Session;
import com.j0h1.contour.pojo.SessionResponseList;
import com.j0h1.contour.pojo.Track;
import com.j0h1.contour.pojo.TrackResponseList;
import com.j0h1.contour.utils.RESTConfig;
import com.j0h1.contour.volley.SimpleXmlRequest;

import android.annotation.SuppressLint;
import android.app.Fragment;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.Toast;

/**
 * Fragment that is responsible for displaying the top 10 track sessions.
 * @author Johannes Riedmann
 *
 */
public class HighscoreTrackFragment extends Fragment {
	
	private ImageButton btnGlobal;
	private ImageButton btnLocal;
	
	private FrameLayout frmGlobalHighscore;
	private FrameLayout frmLocalHighscore;
	
	private ExpandableListView elvTrackPersonal;
	private HighscoreExpandableListAdapter listAdapter;
	
	private List<Track> listDataHeader;
    private SparseArray<List<Session>> listDataChild;
	
	private Toast toast;
	
	private String language;
	
	@Override
	public void onStart() {
		super.onStart();
		
		// language is needed to get the track names in the correct language
		language = getResources().getConfiguration().locale.getLanguage();
		
		frmGlobalHighscore = (FrameLayout) getView().findViewById(R.id.frmGlobalHighscore);
		frmLocalHighscore = (FrameLayout) getView().findViewById(R.id.frmLocalHighscore);
		
		listDataChild = new SparseArray<List<Session>>();
		listDataHeader = new ArrayList<Track>();
		
		// when global button is clicked, populate the child data (header data not necessary,
		// because it does not change)
		btnGlobal = (ImageButton) getView().findViewById(R.id.imbGlobalBest);
		btnGlobal.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {				
				frmGlobalHighscore.setBackground(getResources().getDrawable(R.drawable.highscore_local_global_border));
				frmLocalHighscore.setBackground(null);
				btnGlobal.setTag("selected");
				btnLocal.setTag("");
				
				if (listDataHeader != null) {
					populateChildren();
				}
			}			
		});
		
		// set global highscores as initially selected
		btnGlobal.setTag("selected");
		frmGlobalHighscore.setBackground(getResources().getDrawable(R.drawable.highscore_local_global_border));
		
		// when local button is clicked, populate the child data (header data not necessary,
		// because it does not change)
		btnLocal = (ImageButton) getView().findViewById(R.id.imbPersonalBest);
		btnLocal.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {				
				frmLocalHighscore.setBackground(getResources().getDrawable(R.drawable.highscore_local_global_border));
				frmGlobalHighscore.setBackground(null);
				btnLocal.setTag("selected");
				btnGlobal.setTag("");
				
				if (listDataHeader != null) {
					populateChildren();
				}
			}	
		});
		
		elvTrackPersonal = (ExpandableListView) getView().findViewById(R.id.elvTrackPersonal);
		
		// get track list for local/global highscore lists		
		requestListHeaders();
		
		// when a specific session is selected by the user, open a SessionDetailsDialog
		// to show detailed information about the selected session
		elvTrackPersonal.setOnChildClickListener(new OnChildClickListener() {
			@Override
			public boolean onChildClick(ExpandableListView parent, View v,
					int groupPosition, int childPosition, long id) {	
				// extract current session from ExpandableListView's adapter
				final Session currentSession = (Session) parent
						.getExpandableListAdapter().getChild(groupPosition, childPosition);
				
				// open a SessionDetailsDialog that request and presents the session details
				SessionDetailsDialog dialog = new SessionDetailsDialog(getActivity(), currentSession);				
				dialog.show();
				
				return true;
			}
		});
	}
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		return inflater.inflate(R.layout.fragment_highscore_track, container, false);
	}
	
	/**
	 * Request track names as header data for the ExpandableListView in the default 
	 * language the user has defined on his device. Populate the header data if the
	 * request was successful, provide feedback why if failed otherwise.
	 */
	private void requestListHeaders() {
		RequestQueue rq = Volley.newRequestQueue(getActivity()
				.getApplicationContext());
		
		SimpleXmlRequest<TrackResponseList> request = new SimpleXmlRequest<TrackResponseList>(
				Request.Method.GET,
				RESTConfig.BASE_URL + "tracks/language/" + language,
				TrackResponseList.class,
				new Response.Listener<TrackResponseList>() {
					@Override
					public void onResponse(TrackResponseList response) {
						if (response.getList() != null) {
							// populate header data
							for (Track t : response.getList()) {
								listDataHeader.add(t);
							}
							
							populateChildren();
							
							// if no sessions are persisted, refreshListData right here to at least
							// show the track names as header data
							refreshListData();
						} else {
							prepareToast(getString(R.string.no_tracks_found));
							toast.show();
						}
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						prepareToast(getString(R.string.connection_lost));
						toast.show();
					}
				});
		
		rq.add(request);
	}
	
	/**
	 * For each track that serves as header data, request the top 10
	 * track sessions. If the user selected the global button, the global
	 * high scores are shown. If the local button is selected, the users
	 * high scores are shown.
	 */
	private void populateChildren() {
		for (Track t : listDataHeader) {
			// request top10 for the current track
			if (btnGlobal.getTag().equals("selected")) {
				requestTop10GlobalForTrack(t);
			} else if (btnLocal.getTag().equals("selected")) {
				requestTop10LocalForTrack(t);
			}
		}
	}
	
	/**
	 * The top10 sessions for a specific track are requested. If the request returns a
	 * valid response (SessionResponseList with List that does not equal null), the child
	 * items for the header data is set.
	 * @param track	Track to get session highscore data from.
	 */
	private void requestTop10GlobalForTrack(final Track track) {
		RequestQueue rq = Volley.newRequestQueue(getActivity()
				.getApplicationContext());
		
		// Integer.MAX_VALUE indicates no selected player -> global highscores
		SimpleXmlRequest<SessionResponseList> request = new SimpleXmlRequest<SessionResponseList>(
				Request.Method.GET,
				RESTConfig.BASE_URL + "session/player/" + Integer.MAX_VALUE +"/track/" + track.getTrackId(),
				SessionResponseList.class,
				new Response.Listener<SessionResponseList>() {
					@Override
					public void onResponse(SessionResponseList response) {
						if (response.getList() != null) {
							// populate child view
							listDataChild.put(track.getTrackId(), response.getList());						
						} else {
							listDataChild.remove(track.getTrackId());
						}
						
						refreshListData();
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						prepareToast(getString(R.string.connection_lost));
						toast.show();
					}
				});
		
		rq.add(request);
	}
	
	/**
	 * The top10 sessions for a specific track and player are requested. If the request returns a
	 * valid response (SessionResponseList with List that does not equal null), the child
	 * items for the header data is set.
	 * @param track	Track to get session highscore data from.
	 */
	private void requestTop10LocalForTrack(final Track track) {
		Player player = getLocalPlayer();
		
		// request top10 session list for local player
		RequestQueue rq = Volley.newRequestQueue(getActivity()
				.getApplicationContext());
		
		SimpleXmlRequest<SessionResponseList> request = new SimpleXmlRequest<SessionResponseList>(
				Request.Method.GET,
				RESTConfig.BASE_URL + "session/player/" + player.getPlayerId() +"/track/" + track.getTrackId(),
				SessionResponseList.class,
				new Response.Listener<SessionResponseList>() {
					@Override
					public void onResponse(SessionResponseList response) {
						if (response.getList() != null) {
							// populate child view
							listDataChild.put(track.getTrackId(), response.getList());
						} else {
							listDataChild.remove(track.getTrackId());
						}
						
						refreshListData();
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						prepareToast(getString(R.string.connection_lost));
						toast.show();
					}
				});
		
		rq.add(request);
	}
	
	/**
	 * Gets the locally persisted player from the database.
	 * @return	Locally persisted player.
	 */
	private Player getLocalPlayer() {
		ContourDatabase db = new ContourDatabase(getActivity().getApplicationContext());
		Player player = db.findPlayer();
		db.close();
		
		return player;
	}
	
	/**
	 * Refreshes the data held by the ExpandableListView and expands it afterwards.
	 */
	private void refreshListData() {
		listAdapter = new HighscoreExpandableListAdapter(getActivity(), listDataHeader, listDataChild);
		elvTrackPersonal.setAdapter(listAdapter);
		
		expandList();
	}
	
	/**
	 * Expands the headers of the ExpandableListView to show the child data.
	 */
	private void expandList() {
		for (int i = 0; i < listDataHeader.size(); i++) {
			elvTrackPersonal.expandGroup(i);
		}
	}
	
	@SuppressLint("ShowToast") 
	private void prepareToast(String message) {
		if (toast != null) {
    		toast.cancel();
    	}
    	toast = Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT);
	}

}
