/**
* Copyright (C) 2013 Gundog Studios LLC.
*
* 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.godsandtowers.graphics.menu.layouts;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import org.restlet.resource.ClientResource;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.PopupWindow;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.godsandtowers.R;
import com.godsandtowers.core.GameEngine;
import com.godsandtowers.core.GameInfo;
import com.godsandtowers.core.HostGameEngine;
import com.godsandtowers.core.PlayerStats;
import com.godsandtowers.core.RemoteGameEngine;
import com.godsandtowers.core.grid.Board;
import com.godsandtowers.core.grid.Boards;
import com.godsandtowers.core.grid.Grid;
import com.godsandtowers.core.networking.ClientInfo;
import com.godsandtowers.core.networking.DeviceInfo;
import com.godsandtowers.core.networking.GetGameResource;
import com.godsandtowers.core.networking.HostNetworkManager;
import com.godsandtowers.core.networking.NetworkGameInfo;
import com.godsandtowers.core.networking.PrepareGameResource;
import com.godsandtowers.graphics.game.BitmapCache;
import com.godsandtowers.messaging.ApplicationMessageProcessor;
import com.godsandtowers.sprites.Player;
import com.godsandtowers.sprites.Races;
import com.godsandtowers.util.ADS;
import com.godsandtowers.util.Constants;
import com.godsandtowers.util.TDWPreferences;
import com.gundogstudios.gl.ModelUtils;
import com.gundogstudios.modules.Modules;
public class OnlineGameLayout implements ILayout {
private static final String TAG = "OnlineGameLayout";
private LinearLayout newGameLayout;
private int speed;
private PlayerStats player;
private String boardName;
private PopupWindow window;
public OnlineGameLayout(final PlayerStats player, String boardName) {
this.player = player;
this.boardName = boardName;
}
private void setGameSpeed(int position) {
switch (position) {
case 0:
speed = GameInfo.SLOW;
break;
case 1:
speed = GameInfo.NORMAL;
break;
case 2:
default:
speed = GameInfo.FAST;
break;
}
}
private Spinner generateSpinner(Context context, OnItemSelectedListener listener, int description, int[] choices) {
String[] mStrings = new String[choices.length];
Resources resources = context.getResources();
for (int i = 0; i < mStrings.length; i++) {
mStrings[i] = resources.getString(description) + ": " + resources.getString(choices[i]).toUpperCase();
}
Spinner speedSpinner = new Spinner(context);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, mStrings);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
speedSpinner.setAdapter(adapter);
speedSpinner.setOnItemSelectedListener(listener);
return speedSpinner;
}
public LinearLayout getLayout(final Context context) {
if (newGameLayout == null) {
Typeface font = (Typeface) Modules.PREFERENCES.get(TDWPreferences.TEXT_FONT, Typeface.DEFAULT);
newGameLayout = new LinearLayout(context);
newGameLayout.setOrientation(LinearLayout.VERTICAL);
ADS.placeObtrusiveADMobAD(newGameLayout);
TextView racesTextView = new TextView(context);
racesTextView.setText(R.string.newgame_yourRace);
racesTextView.setTypeface(font);
newGameLayout.addView(racesTextView);
final RaceSelectionGallery humanRaces = new RaceSelectionGallery(context, Races.ALL_RACES,
new RaceSelectionGallery.BasicSelector(context, player));
int races = Modules.PREFERENCES.get(TDWPreferences.PLAYER_RACE, 0);
if (Races.asArray(races).length <= 1 || player.areMultipleRacesUnlocked())
humanRaces.setSelections(races);
newGameLayout.addView(humanRaces);
Spinner speedSpinner = generateSpinner(context, new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
setGameSpeed(arg2);
Modules.PREFERENCES.put(TDWPreferences.GAME_SPEED, arg2);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
}, R.string.newgame_speed, new int[] { R.string.newgame_slow, R.string.newgame_normal,
R.string.newgame_fast, });
int s = Modules.PREFERENCES.get(TDWPreferences.GAME_SPEED, GameInfo.NORMAL);
setGameSpeed(s);
speedSpinner.setSelection(s >= 3 ? 0 : s);
newGameLayout.addView(speedSpinner);
final Button startButton = new Button(context);
BitmapDrawable drawable = new BitmapDrawable(context.getResources(),
BitmapCache.getBitmap(R.drawable.menu_options_button));
startButton.setBackgroundDrawable(drawable);
startButton.setText(R.string.newgame_findGame);
startButton.setTypeface((Typeface) Modules.PREFERENCES.get(TDWPreferences.BUTTON_FONT, Typeface.DEFAULT));
startButton.setTextColor(Color.WHITE);
startButton.setTextSize(20);
startButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
createPopup(context);
new Thread(new Runnable() {
@Override
public void run() {
try {
Modules.NETWORKING.disconnect();
int races = Races.getRaces(humanRaces.getSelections());
Modules.PREFERENCES.put(TDWPreferences.PLAYER_RACE, races);
if (races == 0) {
Toast toast = Toast.makeText(context, R.string.newgame_minHumanRaces,
Toast.LENGTH_LONG);
toast.show();
return;
}
NetworkGameInfo networkGameInfo = null;
int id = 0;
id = getID(races);
networkGameInfo = getNetworkGameInfo(id);
if (networkGameInfo == null) {
Modules.LOG.error(TAG, "network info was null");
return;
}
Modules.LOG.info(TAG, "NetworkGameInfo: " + networkGameInfo);
boolean isHost = id == networkGameInfo.getHostID();
Modules.LOG.info(TAG, "IsHost: " + isHost);
Board board = Boards.getBoard(networkGameInfo.getBoardName());
ClientInfo hostInfo = networkGameInfo.getHost();
Grid gridOne = new Grid(board);
Player playerOne = new Player(0, hostInfo.getPlayerStats(), hostInfo.getRaces(),
gridOne);
ClientInfo clientInfo = networkGameInfo.getClient();
Grid gridTwo = new Grid(board);
Player playerTwo = new Player(1, clientInfo.getPlayerStats(), clientInfo.getRaces(),
gridTwo);
Player[] players = new Player[] { playerOne, playerTwo };
GameInfo gameInfo = new GameInfo(isHost ? 0 : 1, players, networkGameInfo
.getGameSpeed(), GameInfo.BATTLE, GameInfo.BATTLE_WAVES, board);
GameEngine engine;
if (isHost) {
HostNetworkManager hostManager = new HostNetworkManager(gameInfo);
engine = new HostGameEngine(gameInfo, hostManager);
} else {
engine = new RemoteGameEngine(gameInfo);
}
Modules.LOG.info(TAG, "Attaching GameEngine");
Modules.MESSENGER.submit(ApplicationMessageProcessor.ID,
ApplicationMessageProcessor.ATTACH_GAME, engine);
} catch (Exception e) {
Modules.NETWORKING.disconnect();
e.printStackTrace();
Modules.LOG.error(TAG, e.getMessage());
return;
} finally {
if (window != null) {
try {
window.dismiss();
} catch (Exception e) {
Modules.LOG.error(TAG, e.getMessage());
}
window = null;
}
}
}
}).start();
}
});
newGameLayout.addView(startButton);
ADS.placeADMobAd(newGameLayout);
}
return newGameLayout;
}
private void createPopup(Context context) {
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
layout.setOrientation(LinearLayout.VERTICAL);
layout.setGravity(Gravity.CENTER);
layout.setBackgroundColor(Color.argb(196, 0, 0, 0));
TextView view = new TextView(context);
view.setText(R.string.newgame_searching);
view.setGravity(Gravity.CENTER);
view.setTextSize(30);
layout.addView(view);
window = new PopupWindow(layout, context.getResources().getDisplayMetrics().widthPixels, context.getResources()
.getDisplayMetrics().heightPixels);
window.showAtLocation(newGameLayout, Gravity.CENTER, 0, 0);
}
@Override
public void attach(LinearLayout baseLayout) {
baseLayout.addView(getLayout(baseLayout.getContext()));
}
private DeviceInfo getDeviceInfo() {
return new DeviceInfo(android.os.Build.MODEL, Runtime.getRuntime().maxMemory(), Runtime.getRuntime()
.availableProcessors(), android.os.Build.VERSION.SDK_INT, Modules.PREFERENCES.get(
TDWPreferences.GAME_ENGINE_SPEED, GameEngine.FAST), ModelUtils.getMeshQuality(),
ModelUtils.getTextureQuality());
}
private int getID(int races) {
DeviceInfo deviceInfo = getDeviceInfo();
ClientInfo clientInfo = new ClientInfo(deviceInfo, player, races, speed, boardName);
ClientResource prepareGameClient = new ClientResource("http://" + Constants.RESTLET_HOSTNAME + ":"
+ Constants.RESTLET_PORT + Constants.RESTLET_PREPAREGAME);
prepareGameClient.setRequestEntityBuffering(true);
PrepareGameResource prepareGameResource = prepareGameClient.wrap(PrepareGameResource.class);
Modules.LOG.info(TAG, "sending preparegame requests");
return prepareGameResource.prepareGame(clientInfo);
}
private NetworkGameInfo getNetworkGameInfo(int id) throws IOException, InterruptedException {
InetAddress address = getAddress();
DatagramSocket socket = new DatagramSocket(ClientInfo.UDP_PORT, address);
ClientResource getGameClient = new ClientResource("http://" + Constants.RESTLET_HOSTNAME + ":"
+ Constants.RESTLET_PORT + Constants.RESTLET_GETGAME);
getGameClient.setRequestEntityBuffering(true);
GetGameResource getGameResource = getGameClient.wrap(GetGameResource.class);
NetworkGameInfo networkGameInfo = null;
long start = System.currentTimeMillis();
do {
Modules.LOG.info(TAG, "sending update packet on ID: " + id);
String data = "" + id;
DatagramPacket sentPacket = new DatagramPacket(data.getBytes(), data.length(),
Inet4Address.getByName(Constants.RESTLET_HOSTNAME), ClientInfo.UDP_PORT);
socket.send(sentPacket);
Modules.LOG.info(TAG, "sent packet, trying to get game now");
networkGameInfo = getGameResource.getGame(id);
if (networkGameInfo == null)
Thread.sleep(1000);
Modules.LOG.info(TAG, networkGameInfo == null ? "no network game info found" : networkGameInfo.toString());
} while (networkGameInfo == null && (System.currentTimeMillis() - start) < 30000);
if (networkGameInfo == null)
return null;
ClientInfo remoteClient;
if (networkGameInfo.getHostID() == id) {
remoteClient = networkGameInfo.getClient();
} else {
remoteClient = networkGameInfo.getHost();
}
// ModuleManager.LOG.info(TAG, "****** IDS: " + id + " " + networkGameInfo.getHostID());
Modules.LOG.info(TAG,
"IsHost: " + (networkGameInfo.getHostID() == id) + " remoteaddress: " + remoteClient.getAddress() + ":"
+ remoteClient.getPort());
Modules.NETWORKING.connect(socket, remoteClient.getAddress(), remoteClient.getPort());
return networkGameInfo;
}
private Inet4Address getAddress() throws SocketException {
Inet4Address address = null;
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) {
address = (Inet4Address) inetAddress;
}
}
}
if (address != null)
Modules.LOG.info(TAG, "Local Address Is: " + address.getHostAddress());
else
Modules.LOG.error(TAG, "Unable to determine local address");
return address;
}
}