package org.auie.ui;

import java.util.ArrayList;
import java.util.List;

import org.auie.image.UEImage;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.GridLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

@SuppressLint("NewApi")
public class UI2048GameView extends GridLayout {
	
	private static final int MOVE_DISTANCE = 5;
	private static final int DEFAULT_BACKGROUND = 0xFFbbada0;
	
	private List<Point> emptyPoints = new ArrayList<>();
	private Card[][] cardMaps = new Card[4][4];
	private OnUIGame2048Listener onGameListener;
	private int cardSize = 0;
	private int totalScore = 0;
	private int maxCardValue = 0;
	private int cardDistance = 10;
	private int textSize = 20;
	private boolean merge = false;
	private boolean gameover = false;
	
	public UI2048GameView(Context context) {
		super(context);
		init();
	}

	public UI2048GameView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public UI2048GameView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	public UI2048GameView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
		super(context, attrs, defStyleAttr, defStyleRes);
		init();
	}
	
	@SuppressLint("ClickableViewAccessibility")
	private void init(){
		setColumnCount(4);
		try{
			setBackgroundColor(((ColorDrawable) getBackground()).getColor());			
		}catch(Exception e){			
			setBackgroundColor(DEFAULT_BACKGROUND);
		}
		setOnTouchListener(onTouchListener);
		getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
			
			@Override
			public boolean onPreDraw() {
				int w = getWidth();
				int h = getHeight();
				cardSize = (Math.min(w, h) - cardDistance)/4;
				initCards();
				startGame();
				getViewTreeObserver().removeOnPreDrawListener(this);
				return false;
			}
		});
	}
	
	public void restartGame(){
		if (cardMaps[2][2] != null) {
			startGame();
		}else {
			UIToast.show(getContext(), "游戏界面尚未初始化!");
		}
	}

	private void startGame(){
		totalScore = 0;
		maxCardValue = 0;
		for (int y = 0; y < 4; y++) {
			for (int x = 0; x < 4; x++) {
				cardMaps[x][y].setNum(0);
			}
		}
		if (onGameListener != null) {
			onGameListener.onGameStart();
		}
		addRandomCard();
		addRandomCard();
	}
	
	private void addRandomCard(){
		emptyPoints.clear();
		for (int y = 0; y < 4; y++) {
			for (int x = 0; x < 4; x++) {
				if (cardMaps[x][y].getNum() == 0) {
					emptyPoints.add(new Point(x, y));
				}
			}
		}
		Point point = emptyPoints.remove((int)(Math.random() * emptyPoints.size()));
		AlphaAnimation animation = new AlphaAnimation(0, 1f);
		animation.setDuration(500);
		cardMaps[point.x][point.y].startAnimation(animation);
		cardMaps[point.x][point.y].setNum(Math.random() > 0.1 ? 2 : 4);
	}
	
	private void checkGameOver(){
		gameover = true;
		ALL:
		for (int y = 0; y < 4; y++) {
			for (int x = 0; x < 4; x++) {
				if (cardMaps[x][y].getNum() <= 0 
						|| (x > 0 && cardMaps[x][y].equals(cardMaps[x - 1][y]))
						|| (x < 3 && cardMaps[x][y].equals(cardMaps[x + 1][y]))
						|| (y > 0 && cardMaps[x][y].equals(cardMaps[x][y - 1]))
						|| (y < 3 && cardMaps[x][y].equals(cardMaps[x][y + 1]))) {
					gameover = false;
					break ALL;
				}
			}
		}
		if (gameover && onGameListener != null) {
			onGameListener.onGameOver();
		}
	}
	
	private void initCards(){
		for (int y = 0; y < 4; y++) {
			for (int x = 0; x < 4; x++) {
				Card card = new Card(getContext());
				card.setNum(0);
				cardMaps[x][y] = card;
				addView(card, cardSize, cardSize);
			}
		}
	}
	
	private void moveLeft(){
		merge = false;
		for (int y = 0; y < 4; y++) {
			for (int x = 0; x < 4; x++) {
				for (int x1 = x+1; x1 < 4; x1++) {
					if (cardMaps[x1][y].getNum() > 0) {
						if (cardMaps[x][y].getNum() <= 0) {
							cardMaps[x][y].startAnimation(moveAnimationX(x1 - x));
							cardMaps[x][y].setNum(cardMaps[x1][y].getNum());
							cardMaps[x1][y].setNum(0);
							x--;
							merge = true;
						}else if (cardMaps[x][y].equals(cardMaps[x1][y])) {
							cardMaps[x][y].startAnimation(moveAnimationX(x1 - x));
							cardMaps[x][y].setNum(cardMaps[x][y].getNum() * 2);
							cardMaps[x1][y].setNum(0);
							totalScore += cardMaps[x][y].getNum();
							maxCardValue = Math.max(maxCardValue, cardMaps[x][y].getNum());
							if (onGameListener != null) {
								onGameListener.onGameScoreChanged(totalScore, maxCardValue);
							}
							merge = true;
						}
						break;
					}
				}
			}
		}
		if (merge) {
			addRandomCard();
			checkGameOver();
		}
	}
	
	private void moveRight(){
		merge = false;
		for (int y = 0; y < 4; y++) {
			for (int x = 3; x >= 0; x--) {
				for (int x1 = x-1; x1 >=0; x1--) {
					if (cardMaps[x1][y].getNum() > 0) {
						if (cardMaps[x][y].getNum() <= 0) {
							cardMaps[x][y].startAnimation(moveAnimationX(x1 - x));
							cardMaps[x][y].setNum(cardMaps[x1][y].getNum());
							cardMaps[x1][y].setNum(0);
							x++;
							merge = true;
						}else if (cardMaps[x][y].equals(cardMaps[x1][y])) {
							cardMaps[x][y].startAnimation(moveAnimationX(x1 - x));
							cardMaps[x][y].setNum(cardMaps[x][y].getNum() * 2);
							cardMaps[x1][y].setNum(0);
							totalScore += cardMaps[x][y].getNum();
							maxCardValue = Math.max(maxCardValue, cardMaps[x][y].getNum());
							if (onGameListener != null) {
								onGameListener.onGameScoreChanged(totalScore, maxCardValue);
							}
							merge = true;
						}
						break;
					}
				}
			}
		}
		if (merge) {
			addRandomCard();
			checkGameOver();
		}
	}
	
	private void moveUp(){
		merge = false;
		for (int x = 0; x < 4; x++) {
			for (int y = 0; y < 4; y++) {
				for (int y1 = y+1; y1 < 4; y1++) {
					if (cardMaps[x][y1].getNum() > 0) {
						if (cardMaps[x][y].getNum() <= 0) {
							cardMaps[x][y].startAnimation(moveAnimationY(y1 - y));
							cardMaps[x][y].setNum(cardMaps[x][y1].getNum());
							cardMaps[x][y1].setNum(0);
							y--;
							merge = true;
						}else if (cardMaps[x][y].equals(cardMaps[x][y1])) {;
							cardMaps[x][y].startAnimation(moveAnimationY(y1 - y));
							cardMaps[x][y].setNum(cardMaps[x][y].getNum() * 2);
							cardMaps[x][y1].setNum(0);
							totalScore += cardMaps[x][y].getNum();
							maxCardValue = Math.max(maxCardValue, cardMaps[x][y].getNum());
							if (onGameListener != null) {
								onGameListener.onGameScoreChanged(totalScore, maxCardValue);
							}
							merge = true;
						}
						break;
					}
				}
			}
		}
		if (merge) {
			addRandomCard();
			checkGameOver();
		}
	}

	private void moveDown(){
		merge = false;
		for (int x = 0; x < 4; x++) {
			for (int y = 3; y >= 0; y--) {
				for (int y1 = y-1; y1 >= 0; y1--) {
					if (cardMaps[x][y1].getNum() > 0) {
						if (cardMaps[x][y].getNum() <= 0) {
							cardMaps[x][y].startAnimation(moveAnimationY(y1 - y));
							cardMaps[x][y].setNum(cardMaps[x][y1].getNum());
							cardMaps[x][y1].setNum(0);
							y++;
							merge = true;
						}else if (cardMaps[x][y].equals(cardMaps[x][y1])) {
							cardMaps[x][y].startAnimation(moveAnimationY(y1 - y));
							cardMaps[x][y].setNum(cardMaps[x][y].getNum() * 2);
							cardMaps[x][y1].setNum(0);
							totalScore += cardMaps[x][y].getNum();
							maxCardValue = Math.max(maxCardValue, cardMaps[x][y].getNum());
							if (onGameListener != null) {
								onGameListener.onGameScoreChanged(totalScore, maxCardValue);
							}
							merge = true;
						}
						break;
					}
				}
			}
		}
		if (merge) {
			addRandomCard();
			checkGameOver();
		}
	}
	
	private TranslateAnimation moveAnimationY(int overY){
		TranslateAnimation animation = new TranslateAnimation(0, 0, cardSize * overY, 0);
		animation.setDuration(120 * Math.abs(overY));
		return animation;
	}
	
	private TranslateAnimation moveAnimationX(int overX){
		TranslateAnimation animation = new TranslateAnimation(cardSize * overX, 0, 0, 0);
		animation.setDuration(120 * Math.abs(overX));
		return animation;
	}
	
	private OnTouchListener onTouchListener = new OnTouchListener() {
		
		private float startX;
		private float startY;
		private float offsetX;
		private float offsetY;
		
		@SuppressLint("ClickableViewAccessibility")
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				startX = event.getX();
				startY = event.getY();
				break;
			case MotionEvent.ACTION_UP:
				offsetX = event.getX() - startX;
				offsetY = event.getY() - startY;
				if (Math.abs(offsetX) > Math.abs(offsetY)) {
					if (offsetX < -MOVE_DISTANCE) {
						moveLeft();
					}else if (offsetX > MOVE_DISTANCE) {
						moveRight();
					}
				}else {
					if (offsetY < -MOVE_DISTANCE) {
						moveUp();
					}else if (offsetY > MOVE_DISTANCE) {
						moveDown();
					}
				}
				break;
			}
			return true;
		}
	};
	
	public void setOnGameListener(OnUIGame2048Listener onGameListener) {
		this.onGameListener = onGameListener;
	}

	public void setCardDistance(int cardDistance) {
		this.cardDistance = cardDistance;
	}

	public void setTextSize(int textSize) {
		this.textSize = textSize;
	}

	public interface OnUIGame2048Listener{
		public void onGameStart();
		public void onGameOver();
		public void onGameScoreChanged(int totalScore, int maxCardValue);
	}
	
	class Card extends RelativeLayout{

		private int num = 0;
		private TextView mTextView;
		
		public Card(Context context) {
			super(context);
			LayoutParams params = new LayoutParams(-1, -1);
			params.setMargins(cardDistance, cardDistance, 0, 0);
			mTextView = new TextView(getContext());
			mTextView.setLayoutParams(params);
			mTextView.setGravity(Gravity.CENTER);
			mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
			addView(mTextView);
		}

		public int getNum() {
			return num;
		}

		public void setNum(int num) {
			this.num = num;
			if (num <= 0) {
				mTextView.setText("");
			}else {
				mTextView.setText(String.valueOf(num));
			}
			mTextView.setTextColor(0xCCffffff);
			switch (num) {
			case 0:
				mTextView.setBackground(UEImage.createBackground(0xFFFFFF, 80, 8));
				break;
			case 2:
				mTextView.setTextColor(0xFF888888);
				mTextView.setBackground(UEImage.createBackground(0xe5dcd2, 8));
				break;
			case 4:
				mTextView.setTextColor(0xFF888888);
				mTextView.setBackground(UEImage.createBackground(0xede0c8, 8));
				break;
			case 8:
				mTextView.setBackground(UEImage.createBackground(0xf2b179, 8));
				break;	
			case 16:
				mTextView.setBackground(UEImage.createBackground(0xf59563, 8));
				break;
			case 32:
				mTextView.setBackground(UEImage.createBackground(0xf67c5f, 8));
				break;
			case 64:
				mTextView.setBackground(UEImage.createBackground(0xf65e3b, 8));
				break;
			case 128:
				mTextView.setBackground(UEImage.createBackground(0xedcf72, 8));
				break;
			case 256:
				mTextView.setBackground(UEImage.createBackground(0xedcc61, 8));
				break;
			case 512:
				mTextView.setBackground(UEImage.createBackground(0xedc850, 8));
				break;
			case 1024:
				mTextView.setBackground(UEImage.createBackground(0xedc53f, 8));
				break;
			case 2048:
				mTextView.setBackground(UEImage.createBackground(0xedc22e, 8));
				break;
			case 4096:
				mTextView.setBackground(UEImage.createBackground(0x3c3a32, 8));
				break;
			case 8192:
				mTextView.setBackground(UEImage.createBackground(0xff0000, 8));
				break;
			case 16384:
				mTextView.setBackground(UEImage.createBackground(0x009efc, 8));
				break;
			case 32768:
				mTextView.setBackground(UEImage.createBackground(0x000000, 8));
				break;
			default:
				mTextView.setTextColor(0x000000);
				mTextView.setBackground(UEImage.createBackground(0xFFFFFF, 8));
				break;
			}
		}
		
		public boolean equals(Card card){
			return card.getNum() == num;
		}
	}
}