content: /*
   * Copyright (C) 2011-2013 GUIGUI Simon, fyhertz@gmail.com
   * 
   * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
   * 
   * Spydroid 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 source code 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 source code; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package net.majorkernelpanic.spydroid.api;
  
  import java.lang.reflect.Field;
  
  import net.majorkernelpanic.spydroid.R;
  import net.majorkernelpanic.spydroid.SpydroidApplication;
  import net.majorkernelpanic.streaming.SessionBuilder;
  import net.majorkernelpanic.streaming.video.VideoQuality;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  import org.json.JSONTokener;
  
  import android.content.Context;
  import android.content.SharedPreferences;
  import android.content.SharedPreferences.Editor;
  import android.media.AudioManager;
  import android.media.SoundPool;
  import android.media.SoundPool.OnLoadCompleteListener;
  import android.os.Vibrator;
  import android.preference.PreferenceManager;
  import android.util.Log;
  
  /**
   * 
   * Used by the {@link CustomHttpServer}. 
   *
   */
  public class RequestHandler {
  
  	public final static String TAG = "RequestHandler";
  
  	private final static SoundPool mSoundPool = new SoundPool(4,AudioManager.STREAM_MUSIC,0);
  
  	static {
  		mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
  			public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
  				soundPool.play(sampleId, 0.99f, 0.99f, 1, 0, 1);
  			}
  		});
  	}
  
  	/**
  	 * Executes a batch of requests and returns all the results
  	 * @param request Contains a json containing one or more requests
  	 * @return A JSON to send back
  	 */
  	static public String handle(String request) {
  		StringBuilder response = new StringBuilder();
  		JSONTokener tokener = new JSONTokener(request);
  
  		try {
  			Object token = tokener.nextValue();
  
  			response.append("{");
  
  			// More than one request to execute
  			if (token instanceof JSONArray) {
  				JSONArray array = (JSONArray) token;
  				for (int i=0;i<array.length();i++) {
  					JSONObject object = (JSONObject) array.get(i);
  					response.append("\"" + object.getString("action") + "\":" );
  					exec(object, response);
  					if (i != array.length()-1) response.append(",");
  				}
  			// Only One request
  			} else if (token instanceof JSONObject) {
  				JSONObject object = (JSONObject) token;
  				response.append("\"" + object.getString("action") + "\":" );
  				exec(object, response);
  			}
  
  			response.append("}");
  
  		} catch (Exception e) {
  			// Pokemon, gotta catch'em all !
  			Log.e(TAG,"Invalid request: " + request);
  			e.printStackTrace();
  			return "INVALID REQUEST";
  		}
  
  		Log.d(TAG,"Request: " + request);
  		Log.d(TAG,"Answer: " + response.toString());
  
  		return response.toString();
  	}
  
  	/** 
  	 * The implementation of all the possible requests is here
  	 * -> "sounds": returns a list of available sounds on the phone
  	 * -> "screen": returns the screen state (whether the app. is on the foreground or not)
  	 * -> "play": plays a sound on the phone
  	 * -> "set": update Spydroid's configuration
  	 * -> "get": returns Spydroid's configuration (framerate, bitrate...)
  	 * -> "state": returns a JSON containing information about the state of the application
  	 * -> "battery": returns an approximation of the battery level on the phone
  	 * -> "buzz": makes the phone buuz 
  	 * -> "volume": sets or gets the volume 
  	 * @throws JSONException
  	 * @throws IllegalAccessException 
  	 * @throws IllegalArgumentException 
  	 **/
  	static private void exec(JSONObject object, StringBuilder response) throws JSONException, IllegalArgumentException, IllegalAccessException {
  
  		SpydroidApplication application = SpydroidApplication.getInstance();
  		Context context = application.getApplicationContext();
  
  		String action = object.getString("action");
  
  		// Returns a list of available sounds on the phone
  		if (action.equals("sounds")) {
  			Field[] raws = R.raw.class.getFields();
  			response.append("[");
  			for(int i=0; i < raws.length-1; i++) {
  				response.append("\""+raws[i].getName() + "\",");
  			}
  			response.append("\""+raws[raws.length-1].getName() + "\"]");
  		}
  
  		// Returns the screen state (whether the app. is on the foreground or not)
  		else if (action.equals("screen")) {
  			response.append(application.applicationForeground ? "\"1\"" : "\"0\"");
  		}
  
  		// Plays a sound on the phone
  		else if (action.equals("play")) {
  			Field[] raws = R.raw.class.getFields();
  			for(int i=0; i < raws.length; i++) {
  				if (raws[i].getName().equals(object.getString("name"))) {
  					mSoundPool.load(application, raws[i].getInt(null), 0);
  				}
  			}
  			response.append("[]");
  		}
  
  		// Returns Spydroid's configuration (framerate, bitrate...)
  		else if (action.equals("get")) {
  			final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
  
  			response.append("{\"streamAudio\":" + settings.getBoolean("stream_audio", false) + ",");
  			response.append("\"audioEncoder\":\"" + (application.audioEncoder==SessionBuilder.AUDIO_AMRNB?"AMR-NB":"AAC") + "\",");
  			response.append("\"streamVideo\":" + settings.getBoolean("stream_video", true) + ",");
  			response.append("\"videoEncoder\":\"" + (application.videoEncoder==SessionBuilder.VIDEO_H263?"H.263":"H.264") + "\",");
  			response.append("\"videoResolution\":\"" + application.videoQuality.resX + "x" + application.videoQuality.resY + "\",");
  			response.append("\"videoFramerate\":\"" + application.videoQuality.framerate + " fps\",");
  			response.append("\"videoBitrate\":\"" + application.videoQuality.bitrate/1000 + " kbps\"}");
  
  		}
  
  		// Update Spydroid's configuration
  		else if (action.equals("set")) {
  			final JSONObject settings = object.getJSONObject("settings");
  			final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
  			final Editor editor = prefs.edit();
  
  			editor.putBoolean("stream_video", settings.getBoolean("stream_video"));
  			application.videoQuality = VideoQuality.parseQuality(settings.getString("video_quality"));
  			editor.putInt("video_resX", application.videoQuality.resX);
  			editor.putInt("video_resY", application.videoQuality.resY);
  			editor.putString("video_framerate", String.valueOf(application.videoQuality.framerate));
  			editor.putString("video_bitrate", String.valueOf(application.videoQuality.bitrate/1000));
  			editor.putString("video_encoder", settings.getString("video_encoder").equals("H.263")?"2":"1");
  			editor.putBoolean("stream_audio", settings.getBoolean("stream_audio"));
  			editor.putString("audio_encoder", settings.getString("audio_encoder").equals("AMR-NB")?"3":"5");
  			editor.commit();
  			response.append("[]");
  
  		}
  
  		// Returns a JSON containing information about the state of the application
  		else if (action.equals("state")) {
  
  			Exception exception = application.lastCaughtException;
  
  			response.append("{");
  
  			if (exception!=null) {
  
  				// Used to display the message on the user interface
  				String lastError = exception.getMessage();
  
  				// Useful to display additional information to the user depending on the error
  				StackTraceElement[] stack = exception.getStackTrace();
  				StringBuilder builder = new StringBuilder(exception.getClass().getName()+" : "+lastError+"||");
  				for (int i=0;i<stack.length;i++) builder.append("at "+stack[i].getClassName()+"."+stack[i].getMethodName()+" ("+stack[i].getFileName()+":"+stack[i].getLineNumber()+")||");
  
  				response.append("\"lastError\":\""+(lastError!=null?lastError:"unknown error")+"\",");
  				response.append("\"lastStackTrace\":\""+builder.toString()+"\",");
  
  			}
  
  			response.append("\"activityPaused\":\""+(application.applicationForeground ? "1" : "0")+"\"");
  			response.append("}");
  
  		}
  
  		else if (action.equals("clear")) {
  			application.lastCaughtException = null;
  			response.append("[]");
  		}
  
  		// Returns an approximation of the battery level
  		else if (action.equals("battery")) {
  			response.append("\""+application.batteryLevel+"\"");
  		}
  
  		// Makes the phone vibrates for 300ms
  		else if (action.equals("buzz")) {
  			Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
  			vibrator.vibrate(300);
  			response.append("[]");
  		}
  		
  		// Sets or gets the system's volume
  		else if (action.equals("volume")) {
  			AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
  			if (object.has("set")) {
  				audio.setStreamVolume(AudioManager.STREAM_MUSIC, object.getInt("set"), AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
  				response.append("[]");
  			} else {
  				int max = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
  				int current = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
  				response.append("{\"max\":"+max+",\"current\":"+current+"}");
  			}
  		}
  
  	}
  
  }
  