package com.ninjarun;

import java.util.ArrayList;

import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.andengine.entity.IEntity;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.shape.IShape;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.util.FPSLogger;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.extension.tmx.TMXLayer;
import org.andengine.extension.tmx.TMXLoader;
import org.andengine.extension.tmx.TMXLoader.ITMXTilePropertiesListener;
import org.andengine.extension.tmx.TMXObject;
import org.andengine.extension.tmx.TMXObjectGroup;
import org.andengine.extension.tmx.TMXProperties;
import org.andengine.extension.tmx.TMXTile;
import org.andengine.extension.tmx.TMXTileProperty;
import org.andengine.extension.tmx.TMXTiledMap;
import org.andengine.extension.tmx.util.exception.TMXLoadException;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.ui.activity.SimpleBaseGameActivity;
import org.andengine.util.debug.Debug;

import android.hardware.SensorManager;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;

public class MainActivity_5_2_4 extends SimpleBaseGameActivity {
	private Camera camera;
	private static final int CAMERA_WIDTH = 800;
	private static final int CAMERA_HEIGHT = 480;

	public Scene mScene;
	private TMXTiledMap mTMXTiledMap;

	private BitmapTextureAtlas mBitmapTextureAtlas;

	public TiledTextureRegion mPlayer_MPTextureRegion;
	private TiledTextureRegion mPlayerTextureRegion;

	// 적 선언
	private TiledTextureRegion mEnemyTextureRegion;
	// 총알 선언
	private TiledTextureRegion mShurikenTextureRegion;

	public PhysicsWorld mPhysicsWorld;

	private static int mapOffset = 100;

	// FixtureDefs 생성을 위한 Category bits
	private static final short CATEGORYBIT_WALL = 0x0001;
	private static final short CATEGORYBIT_PLAYER = 0x0002;
	private static final short CATEGORYBIT_ENEMY = 0x0004;
	private static final short CATEGORYBIT_SHURIKEN = 0x0008;

	// FixtureDefs 생성을 위한 Mask bits
	private static final short MASKBITS_WALL = -1;

	private static final short MASKBITS_PLAYER = CATEGORYBIT_WALL
			+ CATEGORYBIT_ENEMY;

	private static final short MASKBITS_ENEMY = CATEGORYBIT_WALL
			+ CATEGORYBIT_SHURIKEN +  CATEGORYBIT_PLAYER;

	private static final short MASKBITS_SHURIKEN = CATEGORYBIT_WALL
			+ CATEGORYBIT_ENEMY;

	public ArrayList<IEntity> mEntityList;
	private static AnimatedSprite player_sprite;
	private static Body player_body;
	private FixtureDef boxFixtureDef;
	private Rectangle rect;

	@Override
	public EngineOptions onCreateEngineOptions() {
		camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		EngineOptions engineOptions = new EngineOptions(true,
				ScreenOrientation.LANDSCAPE_FIXED, new FillResolutionPolicy(),
				camera);
		return engineOptions;
	}

	@Override
	protected void onCreateResources() {
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");

		loadImages();
	}

	private void loadImages() {

		// create atlas of 512*512
		this.mBitmapTextureAtlas = new BitmapTextureAtlas(
				this.getTextureManager(), 512, 512, TextureOptions.BILINEAR);

		// 플레이어 캐릭터
		this.mPlayerTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createTiledFromAsset(this.mBitmapTextureAtlas, this,
						"ninja.gif", 0, 0, 4, 1);

		// 적 캐릭터
		// 적캐릭터 경우는 그냥 원하는대로 만들어서 넣으면 됩니다.
		this.mEnemyTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createTiledFromAsset(this.mBitmapTextureAtlas, this,
						"enemy.gif", 0, 128, 4, 1);

		// 표창
		this.mShurikenTextureRegion = BitmapTextureAtlasTextureRegionFactory
				.createTiledFromAsset(this.mBitmapTextureAtlas, this,
						"weapon.gif", 0, 256, 1, 1);

		this.mBitmapTextureAtlas.load();
	}

	@Override
	protected Scene onCreateScene() {
		this.mEngine.registerUpdateHandler(new FPSLogger());

		mScene = new Scene();

		try {
			final TMXLoader tmxLoader = new TMXLoader(this.getAssets(),
					this.mEngine.getTextureManager(),
					TextureOptions.BILINEAR_PREMULTIPLYALPHA,
					this.getVertexBufferObjectManager(),
					new ITMXTilePropertiesListener() {
						@Override
						public void onTMXTileWithPropertiesCreated(
								final TMXTiledMap pTMXTiledMap,
								final TMXLayer pTMXLayer,
								final TMXTile pTMXTile,
								final TMXProperties<TMXTileProperty> pTMXTileProperties) {
						}
					});

			// 타일맵 로드
			this.mTMXTiledMap = tmxLoader.loadFromAsset("tmx/back.tmx");

		} catch (final TMXLoadException e) {
			Debug.e(e);
		}

		final TMXLayer tmxLayer = this.mTMXTiledMap.getTMXLayers().get(0);
		mScene.attachChild(tmxLayer);

		this.mPhysicsWorld = new PhysicsWorld(new Vector2(0,
				SensorManager.GRAVITY_EARTH), false);
		mScene.registerUpdateHandler(this.mPhysicsWorld);

		// 벽과 적군에 대한 타일맵 로드
		loadObject(mTMXTiledMap);

		mEntityList = new ArrayList<IEntity>(mScene.getChildCount());

		for (int i = 0; i < mScene.getChildCount(); i++)
			mEntityList.add(mScene.getChildByIndex(i));

		// 플레이어 캐릭터를 "player"라는 이름으로 등장시킨다.
		showPlayer("player", mPlayerTextureRegion);

		player_sprite = (AnimatedSprite) findShape("player");
		player_body = mPhysicsWorld.getPhysicsConnectorManager()
				.findBodyByShape(player_sprite);

		return mScene;
	}

	/*****************************************************
	 * 
	 * 플레이어 캐릭터 등장
	 * 
	 *****************************************************/

	public void showPlayer(String playerName, TiledTextureRegion playerTexture) {

		AnimatedSprite mPlayerSprite = new AnimatedSprite(mapOffset, 0,
				playerTexture, this.getVertexBufferObjectManager());

		final FixtureDef mPlayerFixtureDef = PhysicsFactory.createFixtureDef(0,
				0f, 0f, false, CATEGORYBIT_PLAYER, MASKBITS_PLAYER, (short) 0);

		Body mPlayerBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld,
				mPlayerSprite, BodyType.DynamicBody, mPlayerFixtureDef);
		mPlayerSprite.setUserData(playerName);
		mPlayerBody.setUserData(playerName);
		this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(
				mPlayerSprite, mPlayerBody, true, false));

		mScene.attachChild(mPlayerSprite);
		mEntityList.add(mPlayerSprite);
	}

	/**********************************************************************************
	 * 
	 * UserData string (name)에 해당하는 모양을 찾는 함수 입니다.
	 * 
	 **********************************************************************************/

	public IShape findShape(String shapeName) {
		IShape pShape = null;

		// 존재하는 모든 entities들을 체크하면서
		// UserData 문자열에 해당하는 값이 있는 entity를 리턴합니다.
		for (IEntity pEntity : mEntityList) {
			if (pEntity.getUserData() != null) {
				if (pEntity.getUserData().toString()
						.equalsIgnoreCase(shapeName)) {
					pShape = (IShape) pEntity;
				}
			}
		}
		return pShape;
	}

	private void loadObject(TMXTiledMap map) {

		// tmx map에서 모든 object groups을 가져와서 속성 이름을 체크합니다.

		for (final TMXObjectGroup group : map.getTMXObjectGroups()) {

			if (group.getName().equals("wall")) {
				loadWallFromObjects(group, "wall");
			}

			// 타일맵상에 속성을 enemies라고 표기하면 게임 시작시 해당 위치에 자동으로 enemies가 나타납니다.
			if (group.getName().equals("enemies")) {

			}

		}

	}

	private void loadWallFromObjects(TMXObjectGroup _group,
			String _userData) {

		for (final TMXObject object : _group.getTMXObjects()) {

			// tmx map에 사각형을 생성합니다.
			rect = new Rectangle(object.getX(), object.getY(),
					object.getWidth(), object.getHeight(),
					this.getVertexBufferObjectManager());

			// 벽에 해당하는 FixtureDef를 생성합니다.
			boxFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f, false,
					CATEGORYBIT_WALL, MASKBITS_WALL, (short) 0);

			// 벽을 StaticBody로 선언하고 userData에 셋팅합니다.
			PhysicsFactory.createBoxBody(this.mPhysicsWorld, rect,
					BodyType.StaticBody, boxFixtureDef).setUserData(_userData);

			// 사각형 영역을 보이지 않게 만듭니다.
			rect.setVisible(false);

			mScene.attachChild(rect);
		}
	}

}
