//	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.HashMap;
import java.util.Map;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.j0h1.contour.R;
import com.j0h1.contour.database.ContourDatabase;
import com.j0h1.contour.pojo.Image;
import com.j0h1.contour.pojo.Player;
import com.j0h1.contour.utils.RESTConfig;
import com.j0h1.contour.volley.RequestQueueSingleton;
import com.j0h1.contour.volley.SimpleXmlRequest;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnShowListener;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.text.InputFilter;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Activity that is responsible for player authentification. Furthermore
 * this activity is the entry point of the application, that requests the 
 * user to download the OpenCV Manager, that is required to play this game.
 * @author Johannes Riedmann
 *
 */
public class MainMenuActivity extends Activity {
	
	private AlertDialog usernamePrompt;	
	private Toast toast;	
	private ImageButton playButton;
	private ImageButton highscoreButton;
	
	// define BaseLoaderCallback in MainMenuActivity to prompt the OpenCV Manager download
	// on the first start of the application - this is defined in the activity, because it's
	// the launcher activity (first activity to start when app launches)
	private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
		@Override
		public void onManagerConnected(int status) {
			switch (status) {
				case LoaderCallbackInterface.SUCCESS: {
					// initialization succeeded - no feedback needed
				}
				break;
				
				default: {
					super.onManagerConnected(status);
				}
				break;
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		
		setContentView(R.layout.activity_main_menu);

		// disable buttons until user is greeted
		playButton = (ImageButton) findViewById(R.id.playButton);
		playButton.setEnabled(false);
		playButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// check if a session is currently persisted locally
				ContourDatabase db = new ContourDatabase(getApplicationContext());
				int sessionCount = db.getSessionCount();
				db.close();
				
				// if the sessionCound is > 0, ask user if he/she wants to continue
				// or discard the session
				if (sessionCount > 0) {
					new AlertDialog.Builder(MainMenuActivity.this)
				    	.setTitle(getString(R.string.continue_session_title))
				    	.setMessage(getString(R.string.continue_or_discard_session))
				    	.setPositiveButton(getString(R.string.continue_session), 
				    			new DialogInterface.OnClickListener() {
					        public void onClick(DialogInterface dialog, int which) {
					        	//TODO check if session was already finished
					        	ContourDatabase db = new ContourDatabase(getApplicationContext());
								Image lastImage = db.getLastImage();
								db.close();
								
								if (lastImage != null) {
									// jump directly into GameActivity where everything will be
						        	// handled and give the intent an extra String that tells the
						        	// GameActivity if it's a new or an existing session
						        	Intent intent = new Intent(MainMenuActivity.this, GameActivity.class);
						        	intent.putExtra("NewOrExistingSession", "existingSession");
									startActivity(intent);
								} else {
									// if the last image has already a drawing assigned to it, the
									// session was already over as the user closed the application.
									// redirect directly to SessionFinishedActivity
									Intent intent = new Intent(MainMenuActivity.this, 
											SessionFinishedActivity.class);
									startActivity(intent);
								}
					        }
				    	})
				    	.setNegativeButton(getString(R.string.discard_session), 
				    			new DialogInterface.OnClickListener() {
					        public void onClick(DialogInterface dialog, int which) {
					        	// discard session and switch to SelectGameModeActivity
					        	ContourDatabase db = new ContourDatabase(getApplicationContext());
								db.discardSession();
								db.close();
								
					        	Intent intent = new Intent(MainMenuActivity.this, 
					        			SelectGameModeActivity.class);
								startActivity(intent);
					        }
					     })
					    .show();
				} else {
					// if there is no open session, switch directly to SelectGameModeActivity
					Intent intent = new Intent(MainMenuActivity.this, SelectGameModeActivity.class);
					startActivity(intent);
				}
			}
		});
		
		highscoreButton = (ImageButton) findViewById(R.id.highscoreButton);
		highscoreButton.setEnabled(false);
		highscoreButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// redirect to HighscoreActivity
				Intent intent = new Intent(MainMenuActivity.this, HighscoreActivity.class);
				startActivity(intent);
			}
		});
		
		// request if a player is persisted (server-sided) for this device -> this is the
		// reason why a network connection is needed right from the start
		requestPlayerForAndroidId(getAndroidId());
	}
	
	@Override
	public void onResume() {
		super.onResume();
		
		// when resuming the application, OpenCV has to check for an 
		// existing OpenCV Manager again
		OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_9, 
				this, mLoaderCallback);
	}
	
	@Override
	public void onBackPressed() {
		// switch to home screen when back is pressed
		Intent intent = new Intent(Intent.ACTION_MAIN);
		intent.addCategory(Intent.CATEGORY_HOME);
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		startActivity(intent);
	}
	
	private void enableButtons() {
		playButton.setEnabled(true);
		highscoreButton.setEnabled(true);
	}
	
	/**
	 * Sends a request to the server with the current android device ID and checks if
	 * there is a player persisted (server-sided) that matches the given id. If so, a
	 * Player object is returned and passed on to the method handlePlayerRequestResult.
	 * If no player is found, an AlertDialog shows up that prompts the user to
	 * enter a username.
	 * @param androidId	Unique ID that identifies the current android device.
	 */
	private void requestPlayerForAndroidId(String androidId) {
		RequestQueue rq = RequestQueueSingleton.getInstance(getApplicationContext());
		
		SimpleXmlRequest<Player> request = new SimpleXmlRequest<Player>(
				Request.Method.GET,
				RESTConfig.BASE_URL + "players/phoneId/" + androidId,
				Player.class,
				new Response.Listener<Player>() {
					@Override
					public void onResponse(Player response) {
						handlePlayerRequestResult(response);
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						final AlertDialog errorPrompt = new AlertDialog.Builder(MainMenuActivity.this)
					    .setTitle(getString(R.string.no_internet_connection))
					    .setMessage(getString(R.string.no_internet_connection_message))
					    .setPositiveButton("Ok", null).create();
						
						errorPrompt.setCancelable(false);
						
						errorPrompt.setOnShowListener(new OnShowListener() {
							@Override
							public void onShow(DialogInterface dialog) {
								Button b = errorPrompt.getButton(AlertDialog.BUTTON_POSITIVE);
								b.setOnClickListener(new OnClickListener() {
									@Override
									public void onClick(View v) {
										finish();
									}
								});	
							}
						});
						
						errorPrompt.show();
					}
				});
		
		rq.add(request);
	}
	
	/**
	 * This method handles the response from the player request for a specific android device id.
	 * @param player Player object, given as response from the server.
	 */
	private void handlePlayerRequestResult(Player player) {
		// if playername is null, the player has either been deleted or never existed
		if (player.getPlayername() == null) {
			// delete local player and session data if existent
			ContourDatabase db = new ContourDatabase(getApplicationContext());
			db.deletePlayer();
			db.discardSession();
			db.close();
			
			// request username input
			promptUsernameInput();
		} else {
			ContourDatabase db = new ContourDatabase(getApplicationContext());
			Player localPlayer = db.findPlayer();
			db.close();
			
			// if localPlayer is not null, a player was persisted in the local database
			if (localPlayer != null) {
				if (!localPlayer.getPlayername().equals(player.getPlayername())) {
					// if local player and server-sided player differ, delete local player
					db = new ContourDatabase(getApplicationContext());
					db.deletePlayer();
					db.close();
					
					// persist player received from server
					persistPlayerLocally(new Player(player.getPlayerId(), player.getPlayername()));
				}
				
				greetPlayer(new Player(player.getPlayerId(), player.getPlayername()));
			} else {
				// server had player with this Android ID persisted (user uninstalled the game
				// or wiped the app data) -> player data gets persisted locally
				persistPlayerLocally(new Player(player.getPlayerId(), player.getPlayername()));
			}			
		}
	}
	
	/**
	 * Prompt the user to enter a username. This dialog can't be canceled, since the user has
	 * to define a valid username to use the app.
	 */
	private void promptUsernameInput() {
		// allow max 15 characters in the EditText
		final EditText input = new EditText(this);
		InputFilter[] fArray = new InputFilter[1];
		fArray[0] = new InputFilter.LengthFilter(15);
		input.setFilters(fArray);
		input.setGravity(Gravity.CENTER_HORIZONTAL);
		
		// create AlertDialog for username input
		usernamePrompt = new AlertDialog.Builder(this)
	    .setTitle(getString(R.string.choose_username))
	    .setMessage(getString(R.string.choose_username_wisely))
	    .setView(input)
	    .setPositiveButton("Ok", null).create();
		
		// apply ShowListener to verify valid user input
		usernamePrompt.setOnShowListener(new OnShowListener() {
			@Override
			public void onShow(DialogInterface dialog) {
				// when the positive button ("Ok") is clicked, request to persist
				// the player on the server
				Button b = usernamePrompt.getButton(AlertDialog.BUTTON_POSITIVE);
				b.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						String usernameInput = input.getText().toString().trim();
						if (!usernameInput.isEmpty()) {
							Player player = new Player();
							player.setPlayername(usernameInput);
							player.setPhoneId(getAndroidId());
							
							requestPersistPlayer(player);
						}
					}
				});	
			}
		});
		
		// make sure that user doesn't attempt to click the dialog away
		usernamePrompt.setCanceledOnTouchOutside(false);
		usernamePrompt.setCancelable(false);
		
		usernamePrompt.show();
	}
	
	/**
	 * Send a request to the server to persist a defined player.
	 * @param player 	Player object that was defines by the user. (username entered by the
	 * 					user and androidId extracted from the device configuration)
	 */
	private void requestPersistPlayer(final Player player) {
		RequestQueue rq = RequestQueueSingleton.getInstance(getApplicationContext());
		
		SimpleXmlRequest<Player> request = new SimpleXmlRequest<Player>(
				Request.Method.POST,
				RESTConfig.BASE_URL + "players",
				Player.class,
				new Response.Listener<Player>() {
					@Override
					public void onResponse(Player response) {
						// if the playerId equals Integer.MAX_VALUE, the username was
						// already taken
						if (response.getPlayerId() != Integer.MAX_VALUE) {
							// persist the player in the local database
							persistPlayerLocally(new Player(response.getPlayerId(), 
									response.getPlayername(), response.getPhoneId()));
							
							// close the AlertDialog
							usernamePrompt.dismiss();
							
							greetPlayer(player);
						} else {
							prepareToast(getString(R.string.username_already_taken));
							toast.show();
						}
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						// if Volley returns with an error, the connection to the server
						// was lost (server down or Internet gone)
						prepareToast(getString(R.string.connection_lost));
						toast.show();
					}
				}) {
			
			@Override
    		protected Map<String, String> getParams() {
				// add playername and phoneId as parameters to the request
    			Map<String, String> params = new HashMap<String, String>();
    			params.put("playername", "" + player.getPlayername());
    			params.put("phoneId", player.getPhoneId());
    			
    			return params;
    		}
			
		};
		
		rq.add(request);
	}
	
	/**
	 * Persists a player to the local SQLite database.
	 * @param player Player to be persisted locally.
	 */
	private void persistPlayerLocally(Player player) {
		ContourDatabase db = new ContourDatabase(getApplicationContext());
		db.persistPlayer(player);
		db.close();
		
		// greet the player after persisting him
		greetPlayer(player);
	}
	
	/**
	 * Greets the given player and enables highscoreButton and playButton for
	 * the user to continue.
	 * @param player Player that needs to be greeted.
	 */
	private void greetPlayer(Player player) {
		TextView helloUser = (TextView) findViewById(R.id.txtHelloName);
		helloUser.setText(getString(R.string.hello) + " " + player.getPlayername() + "!");
		
		enableButtons();
	}
	
	/**
	 * Gets an unique device identifier for the current device.
	 * @return Android device ID.
	 */
	private String getAndroidId() {
		return Secure.getString(getApplicationContext().getContentResolver(), 
				Secure.ANDROID_ID);
	}
	
	@SuppressLint("ShowToast") 
	private void prepareToast(String message) {
		if (toast != null) {
    		toast.cancel();
    	}
    	toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
	}

}
