package com.team5.aLife.Client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Random;

import org.json.JSONArray;

import android.util.Log;

import com.google.gson.Gson;

public class socketDispatch {
	
	final static String tag = "socketDispatch";

	// TCP/IP Stuff
//	private ServerSocket serverSocket;
	Socket socket = null;
	PrintWriter out = null;
	BufferedReader in = null;
	String nextLine = null;
	
	// TCP/IP I/O
//    private String line;
//	private String toSend;
//	private Gson gsonIn;
	private Gson gsonOut = new Gson();
//	private ObjectInputStream objectIn;
//	private ObjectOutputStream objectOut;
	InetAddress serverIP;
	int serverPort;
	
	// TODO simulate a tick mark on the main page
	public static boolean simulate = false;
	
	private static socketDispatch thisSocketDispatch = new socketDispatch();
	
	protected socketDispatch(){
	}
	
	public static socketDispatch instance(){
		return thisSocketDispatch;
	}
	
	
	public String login(LoginRequest l){
		Log.i(tag + ":Login", "Attempting Login");
		
		if (simulate)
			return simulateLogin();
		
        InetSocketAddress socketAddress = LoginActivity.getServerAddress();
        serverIP = socketAddress.getAddress();
        serverPort = socketAddress.getPort();
        
		if (connect()){
			try {
				// objectOut.writeObject(l);
				// Gson g = new Gson();
				String temp = l.encodeToRequestString();
				Log.i(tag + ":login", "Sending: " + temp);
				out.println(temp);
				if ((nextLine = in.readLine()) != null){
					Log.i(tag + ":login", "Received: " + nextLine);
					disconnect();
					return nextLine;
				} else {
					disconnect();
					return "Did Not Receive Response";
				}
			} catch (IOException e) {
				Log.e(tag + ":login", "objectOut/In failed", e);
				disconnect();
				return "Could Not Connect";
			} 
			
//			catch (ClassNotFoundException e) {
//				Log.e(tag + ":login", "objectIn failed", e);
//				disconnect();
//				return null;
//			}
		} // end if(connect())
		Log.i(tag + ":login", "Could not connect");
		return "Could Not Connect";
	}
			
	private String simulateLogin(){
		// Create a fake LoginReply with a dummy user
		LoginReply lr = new LoginReply(true, new User(100,
				"amos", "amos", "admin", 0, 0, 0));
		// Return the TCP/IP string
		return "101&" +  gsonOut.toJson(lr);
	}
	
	public void send(String s){
		Log.i(tag + ":send", "Attempting to send " + s);
		if (connect()){
			out.println(s);
			disconnect();
		}
		else 
			Log.i(tag + ":send", "Unable to send");
	}

	public String sendReceive(AbstractRequest ar){
		Log.i(tag + ":sendReceive", "Attempting to send " + ar.encodeToRequestString());
		
		if (simulate)
			return simulateSendReceive(ar);
		
		if (connect()){
			out.println(ar.encodeToRequestString());
			
			try {
				if((nextLine = in.readLine()) != null){
					disconnect();
					return nextLine;
				}
			} catch (IOException e) {
				Log.e(tag + ":sendReceive", "in.readLine() failed");
				disconnect();
				return "null";
			}
			
		} // end if(connect())
		return "Responce";
	} // end sendReceive();
	
	public String sendReceive(String s){
		Log.i(tag + ":sendReceive", "Attempting to send " + s);
		
		if (simulate)
			return simulateSendReceive(s);
		
		if (connect()){
			out.println(s);
			
			try {
				if((nextLine = in.readLine()) != null){
					disconnect();
					return nextLine;
				}
			} catch (IOException e) {
				Log.e(tag + ":sendReceive", "in.readLine() failed");
				disconnect();
				return "null";
			}
			
		} // end if(connect())
		return "Responce";
	} // end sendReceive();
	
	public String simulateSendReceive(AbstractRequest ar){
		switch (ar.getRequestType()){
		// request login
		case 1:
			// This case isn't used since there is a login method???
			Log.i(tag + ":simulateSendReceive", "Rcvd Type 1 Request: Login");
			return "null";
			
		// Request full device list
		case 2: 
			Log.i(tag + ":simulateSendReceive", "Rcvd Type 2 Request: All Devices");
			pause (1000);
			return createDeviceList();
		default:
			return "null";
		}
	}
	
	public String simulateSendReceive(String s){
		Log.i(tag + ":simulateSendReceive", "Method not implemented");
		return s;
	}
	
	private static String createDeviceList(){
		
		// Holds all the devices
		JSONArray ja = new JSONArray();
		
		// Necessary to convert GetAllDevicesReply to GSON
		Gson gson = new Gson();
		
//	case 1: return R.drawable.light;		// Light
//	case 2: return R.drawable.flash;		// Relay
//	case 3: return R.drawable.phone;		// Indoor Temperature Sensor
//	case 4: return R.drawable.settings;		// Thermostat
//	case 5: return R.drawable.exit;			// Garage Door Sensor";
//	case 6: return R.drawable.chart;		// Water sensor";
//	case 7: return R.drawable.equalizer;	// Outdoor Temperature Sensor";
		
//     public Device (int deviceID, int deviceType, String deviceName, String deviceAddress,
//		   int zigbeeStatus, int deviceStatus)
		
		Random r = new Random(System.currentTimeMillis());
		int cycle = r.nextInt(1) + 1;
		Log.i(tag + ":createDeviceList", "returning case " + cycle);
		switch (cycle){
		case 1:
			ja.put(new Device(1, 1, "Porch Light", "101", 1001, 201));
			ja.put(new Device(2, 2, "Relay A", "102", 1002, 202));
			ja.put(new Device(3, 3, "Master Bed Temp", "103", 1003, 203));
			ja.put(new Device(4, 4, "Thermostat", "104", 1004, 204));
			ja.put(new Device(5, 5, "Garage Door", "105", 1005, 205));
			ja.put(new Device(6, 6, "Rain Sensor", "106", 1005, 206));
			ja.put(new Device(7, 7, "Outdoor Temp", "107", 1007, 207));
			break;
		case 2:
			ja.put(new Device(1, 1, "Porch Light", "101", 1001, 1));
			ja.put(new Device(2, 2, "Relay A", "102", 1002, 1));
			ja.put(new Device(3, 3, "Outdoor Temp", "103", 1003, 1));
			ja.put(new Device(4, 4, "Thermostat", "104", 1004, 1));
			ja.put(new Device(5, 5, "Garage Door", "105", 1005, 1));
			ja.put(new Device(6, 6, "Rain Sensor", "106", 1005, 1));
			ja.put(new Device(7, 7, "Outdoor Temp", "107", 1007, 1));
			ja.put(new Device(8, 2, "Relay B", "108", 1008, 1));
			ja.put(new Device(9, 2, "Relay C", "109", 1009, 1));
			break;
		}
		
		
		// Create the reply
		GetAllDevicesReply reply = new GetAllDevicesReply(ja);
		
		// append the header, convert to Json and return the string
		return "102&" + gson.toJson(reply);
	}
	
	private void pause(int t){
		// Simulate a TCP/IP connection delay
		Long time = System.currentTimeMillis();
		while (System.currentTimeMillis() < time + t);
	}
	
	private Boolean connect(){
		if (openSocket() && setStreams()){
			return true;
		}
		else
			return false;
	}
	
	private boolean setStreams(){
		try{	
			out = new PrintWriter(socket.getOutputStream(), true);
			in = new BufferedReader (new InputStreamReader(socket.getInputStream()));
			//objectIn = new ObjectInputStream(socket.getInputStream());
			//objectOut = new ObjectOutputStream(socket.getOutputStream());
			return true;
		// Catches for the initial connection	
		} catch (IOException e) {
			Log.e(tag + ":setStreams", "Could not get in/out streams", e);
			disconnect();
			return false;
		}		
	}
	
	private void disconnect(){
		try {
			socket.close();
		} catch (IOException e) {
			Log.e(tag + ":disconnect", "Disconnect error", e);
		}
	}
	
    public Boolean openSocket(){
		Log.d(tag + "callServer", "Attempting connection with " + serverIP + " on port " + serverPort);
		try {
			socket = new Socket (serverIP, serverPort);
			socket.setSoTimeout(2000);
			// socket.setKeepAlive(true);
			Log.i(tag + ":openSocket", "Connected!");
			return true;
		} catch (IOException e) {
			Log.e(tag + "openSocket", "Failed to connect", e);
			return false;
		}
    } // end openSocket()
	
}
