//upmを編集したため、ダウンロードするだけでは動作しません
//#ビルド方法
//arduinoの端末上で
//$ cd /usr/lib/node/jsupm_i2clcd
//$ mv jsupm_i2clcd.node jsupm_i2clcd.node.old
//$ wget --no-check-certificate 'https://drive.google.com/uc?export=download&id=0B4Hp2uhJD5H7dHBBMWdGcmktSzQ' -O jsupm_i2clcd.node
//#元に戻す場合
//$ cd /usr/lib/node/jsupm_i2clcd
//$ rm -f jsupm_i2clcd.node
//$ mv jsupm_i2clcd.node.old jsupm_i2clcd.node
var f3js = require('f3js')
  , c = f3js.createContainer()
  , x = 10
  , y = 10
  , width = 160
  , height = 105
  , thickness = 42
  , jw = 7 /* かみ合わせ幅 [5, 10]  */
  , jh = 2 /* 板厚[mm] [1, 5] */;
c.x = x;
c.y = y;
var f3js_contr = require('f3js')
  , c_contr1 = f3js_contr.createContainer()
  , x_contr1 = 340
  , y_contr1 = 10
  , c_contr2 = f3js_contr.createContainer()
  , x_contr2 = 395
  , y_contr2 = 10
  , width_contr = 140
  , height_contr = 50
  , thickness_contr = 23
  , jw_contr = 7
  , jh_contr = 2;
c_contr1.x = x_contr1;
c_contr1.y = y_contr1;
c_contr2.x = x_contr2;
c_contr2.y = y_contr2;
// 本体側
// 表の面を作る
var p = c.jrc(0, 0, width, height, jw, jh);
// 表面にいろいろ配置する
c.add(lcd, { x: 80 + 0, y: 30 + 0 });
c.add(button, { x: 140, y: 40 });
c.add(buzzer, { x: 40, y: 70 });
// 配線用の穴を開ける
c.drawRectangle(136, 33, 8, 2);
c.drawRectangle(137, 38, 12, 10);
c.drawRectangle(135, 89, 10, 10);
c.drawRectangle(15, 89, 10, 10);
c.drawRectangle(88.5, 61.5, 32, 17);
c.drawCircle(113, 60, 1);
c.drawCircle(113, 80, 1);
c.drawCircle(83, 70, 1);
c.drawCircle(70, 155.5, 1.5);
c.drawCircle(70, 183, 1.5);
// 側面と裏面を作る
var ps = p.extrude(45);
ps[0].x = width + 5;
ps[0].y = jh;
ps[1].x = width + 5;
ps[1].y = (thickness + 5) * 2 + jh;
ps[2].x = width + 5;
ps[2].y = thickness + 5 + jh;
ps[3].x = width + 5;
ps[3].y = (thickness + 5) * 3 + jh;
ps[4].x = 0; // 最後の要素が裏面
ps[4].y = height + 5; // 表の面の真下に配置する
for (var i = 0; i < ps.length; i ++) {
    c.add(ps[i]);
}
// 側面の一つに電源用の穴を開ける
ps[1].drawRectangle(jh + 5, thickness - jh - 5, height - jh*2 - 10, -13);
// コントローラ側
// 表面
var p_contr1 = c_contr1.jrc(0, 0, height_contr, width_contr, jw_contr, jh_contr);
var p_contr2 = c_contr2.jrc(0, 0, height_contr, width_contr, jw_contr, jh_contr);
// 側面と裏面
var ps_contr1 = p_contr1.extrude(23);
ps_contr1[0].x = -height_contr - 5;
ps_contr1[0].y = (thickness + 5) * 2 + jh_contr;
ps_contr1[1].x = -width_contr - 5;
ps_contr1[1].y = width_contr * 2 - thickness_contr * 2 + 5;
ps_contr1[2].x = -height_contr - 5;
ps_contr1[2].y = (thickness + 5) * 2 + thickness_contr + jh_contr * 2;
ps_contr1[3].x = -width_contr - 5;
ps_contr1[3].y = width_contr * 2 - thickness_contr + jh_contr + 5;
ps_contr1[4].x = 0;
ps_contr1[4].y = width_contr + 5;
for (i = 0; i < ps_contr1.length; i ++) {
    c_contr1.add(ps_contr1[i]);
}
var ps_contr2 = p_contr2.extrude(23);
ps_contr2[0].x = (-height_contr - 5) * 2;
ps_contr2[0].y = (thickness + 5) * 2 + thickness_contr * 2 + jh_contr * 3;
ps_contr2[1].x = -width_contr * 2 - height_contr - 5 * 3;
ps_contr2[1].y = width_contr * 2 - thickness_contr * 2 + 5;
ps_contr2[2].x = (-height_contr - 5) * 2;
ps_contr2[2].y = (thickness + 5) * 2 + thickness_contr * 3 + jh_contr * 4;
ps_contr2[3].x = -width_contr * 2 - height_contr - 5 * 3;
ps_contr2[3].y = width_contr * 2 - thickness_contr + jh_contr + 5;
ps_contr2[4].x = 0;
ps_contr2[4].y = width_contr + 5;
for (i = 0; i < ps_contr2.length; i ++) {
    c_contr2.add(ps_contr2[i]);
}
//ボタン類
var b1 = c_contr1.add(button, { x: 29, y: width_contr + 40 });
var b2 = c_contr2.add(button, { x: 29, y: width_contr + 40 });
b1.rotate(-90);
b2.rotate(-90);
var st1 = c_contr1.add(joyStick, { x: 25, y: 110 });
var st2 = c_contr2.add(joyStick, { x: 25, y: 110 });
st1.rotate(-90);
st2.rotate(-90);
//ボタン類用の穴
c.drawRectangle(351, 175, 8, 10);
c.drawRectangle(362, 175, 2, 10);
c.drawRectangle(406, 175, 8, 10);
c.drawRectangle(417, 175, 2, 10);
c.drawCircle(355, 255, 12);
c.drawCircle(410, 255, 12);
//配線用の穴
c.drawRectangle(105, 244, 10, 10);
c.drawRectangle(105, 268, 10, 10);
// 以降はゲームのソースコード
var upmBuzzer = require("jsupm_buzzer");
var groveSensor = require('jsupm_grove');
var LCD = require("jsupm_i2clcd");
var joystick = require('jsupm_joystick12');
var tm1637 = require('jsupm_tm1637');
var myBuzzer = new upmBuzzer.Buzzer(6);
var display = new tm1637.TM1637(0,1);
var sw = new groveSensor.GroveButton(4);
var lcd = new LCD.Jhd1313m1 (6, 0x3E, 0x62);
var js = 
	[new joystick.Joystick12(0,1),
	 new joystick.Joystick12(2,3)];
var button =
	[new groveSensor.GroveButton(2),
	 new groveSensor.GroveButton(3)];
var picture1 = new LCD.uint8Array(8);
picture1.setitem(0, 31);
picture1.setitem(1, 27);
picture1.setitem(2, 21);
picture1.setitem(3, 27);
picture1.setitem(4, 21);
picture1.setitem(5, 27);
picture1.setitem(6, 21);
picture1.setitem(7, 31);
var picture2 = new LCD.uint8Array(8);
picture2.setitem(0, 31);
picture2.setitem(1, 27);
picture2.setitem(2, 21);
picture2.setitem(3, 21);
picture2.setitem(4, 21);
picture2.setitem(5, 21);
picture2.setitem(6, 27);
picture2.setitem(7, 31);
var ten = new LCD.uint8Array(8);
ten.setitem(0, 0);
ten.setitem(1, 18);
ten.setitem(2, 21);
ten.setitem(3, 21);
ten.setitem(4, 21);
ten.setitem(5, 21);
ten.setitem(6, 18);
ten.setitem(7, 0);
var downArrow = new LCD.uint8Array(8);
downArrow.setitem(0, 4);
downArrow.setitem(1, 4);
downArrow.setitem(2, 4);
downArrow.setitem(3, 4);
downArrow.setitem(4, 4);
downArrow.setitem(5, 21);
downArrow.setitem(6, 14);
downArrow.setitem(7, 4);
var upArrow = new LCD.uint8Array(8);
upArrow.setitem(0, 4);
upArrow.setitem(1, 14);
upArrow.setitem(2, 21);
upArrow.setitem(3, 4);
upArrow.setitem(4, 4);
upArrow.setitem(5, 4);
upArrow.setitem(6, 4);
upArrow.setitem(7, 4);
lcd.createChar(0,picture1);
lcd.createChar(1,picture2);
lcd.createChar(2,ten);
lcd.createChar(3,downArrow);
lcd.createChar(4,upArrow);
var cards = [];
var back = ["[0]", "[1]"];
var flip = "*";
var isAnimating = false;
var flipCount = 0;
var pair = [0, 0];
var height = 15;
var neut = [{x:0,y:0}, {x:0,y:0}];
var page = 1;
var scene = "title";
var changingScene = false;
var maxTryLimit = 10;
var tryLimit = maxTryLimit;
var mode = 0;
var turn = 0;
var input = [{x:0, y:0, button:false}, {x:0, y:0, button:false}];
var waitingButton = false;
var cPos = {x:0,y:0}
var level = 1;
var score = [0, 0];
var lastPage = 1;
var gameLength = "full";
init();
initTitle();
setInterval(function()
{
	getInput();
	if (sw.value() == 0 && scene != "off") {
		lcd.displayOff();
		lcd.backlightOff();
		lcd.setColor(0,0,0);
		scene = "off";
	}
	switch (scene) {
	case "title":
		if (changingScene) {
			changingScene = !changingScene;
			initTitle();
		}
		startScreen();
		break;
	case "levelSelect":
		if (changingScene) {
			changingScene = !changingScene;
			level = 1;
			lcd.clear();
			lcd.setCursor(0,0);
			lcd.write("Select Level");
			lcd.setCursor(1,3);
			lcd.write("NORMAL");
			lcd.setCursor(0,15);
			lcd.write("[4]");
			lcd.setCursor(1,15);
			lcd.write("[3]");
		}
		if (input[0].x < 0) {
			if (level < 2) {
				level++;
			}
		} else if (input[0].x > 0) {
			if (level > 0) {
				level--;
			}
		}
		var c = ["  EASY", "NORMAL", "  HARD"];
		lcd.setCursor(1,3);
		lcd.write(c[level]);
		if (input[0].button) {
			changeScene("lengthSelect");
		}
		break;
	case "lengthSelect":
		if (changingScene) {
			changingScene = !changingScene;
			gameLength = "full";
			lcd.clear();
			lcd.setCursor(0,0);
			lcd.write("Select Mode");
			lcd.setCursor(1,3);
			lcd.write(" FULL GAME");
			lcd.setCursor(0,15);
			lcd.write("[4]");
			lcd.setCursor(1,15);
			lcd.write("[3]");
		}
		if (input[0].x < 0) {
			if (gameLength == "half") {
				gameLength = "full";
				lcd.setCursor(1,3);
				lcd.write(" FULL");
			}
		} else if (input[0].x > 0) {
			if (gameLength == "full") {
				gameLength = "half";
				lcd.setCursor(1,3);
				lcd.write(" HALF");
			}
		}
		if (input[0].button) {
			if (mode == 0) {
				changeScene("singlePlay");
			} else {
				changeScene("multiPlay");
			}
		}
		break;		
	case "singlePlay":
		gamePlay();
		break;
	case "multiPlay":
		gamePlay();
		break;
	case "victory":
		if (changingScene) {
			changingScene = !changingScene;
			lcd.clear();
		}
		lcd.setCursor(0,0);
		lcd.write("Success!!!");
		if (input[0].button) {
			changeScene("title");
		}
		break;
	case "defeat":
		if (changingScene) {
			changingScene = !changingScene;
			lcd.clear();
			melody_mro();
		}
		lcd.setCursor(0,0);
		lcd.write("Failure");
		lcd.setCursor(1,0);
		lcd.write("Score: " + score[0]);
		if (input[0].button) {
			changeScene("title");
		}
		break;
	case "off":
		if (sw.value() > 0) {
			lcd.displayOn();
			lcd.backlightOn();
			init();
			scene = "title";
			changingScene = true;
		}
		break;
	case "result":
		var s1 = score[0];
		var s2 = score[1];
		lcd.clear();
		lcd.setCursor(0,0);
		if (s1 > s2) {
			lcd.write("Winner: 1P");
		} else if (s2 > s1) {
			lcd.write("Winner: 2P");
		} else {
			lcd.write("Draw Game");
		}
		if (input[0].button) {
			changeScene("title");
		}
		break;
	case "interval":
		break;
	}
}, 100);
function changeScene(s)
{
	changingScene = true;
	scene = "interval";
	setTimeout(function() {
		scene = s;
	}, 500);
}
function gamePlay()
{
	if (changingScene) {
		changingScene = !changingScene;
		turn = 0;
		initGamePlay();
	}
	if (waitingButton) {
		if (input[0].button || input[0].x != 0 || input[0].y != 0 || input[1].button || input[1].x != 0 || input[1].y != 0) {
			waitingButton = !waitingButton;
			lcd.setColor(100,255,100);
			faceDown(pair[0], pair[1]);
		}
	} else {
		mainLoop();
	}
	showScore();
	showPageInfo();
	showCards();
	showCursor();
	endGamePlay();
}
function endGamePlay()
{
	if (mode == 0) {
		if (score[0] >= cards.length) {
			melody_ff();
			changeScene("victory");
		}
		if (tryLimit <= 0) {
			changeScene("defeat");
		}
	} else {
		if (score[0] + score[1] >= cards.length) {
			melody_ff();
			changeScene("result");
		}
	}
}
function startScreen()
{	
	if (input[0].y == -1) {
		writePair("[", "]", 2);
		writePair(" ", " ", 6);
		mode = 0;
	} else if (input[0].y == 1) {
		writePair(" ", " ", 2);
		writePair("[", "]", 6);
		mode = 1;
	}
	if (input[0].button) {
		if (mode == 0) {
			changeScene("levelSelect");
		} else {
			changeScene("lengthSelect");
		}
	}
}
function writePair(c1, c2, pos)
{
	lcd.setCursor(1,pos);
	lcd.write(c1);
	lcd.setCursor(1,pos+3);
	lcd.write(c2);
}	
function init()
{
	neut[0].x = js[0].getXInput();
	neut[0].y = js[0].getYInput();
	neut[1].x = js[1].getXInput();
	neut[1].y = js[1].getYInput();
	myBuzzer.setVolume(0.05);
  lcd.setColor(100,255,100);
}
function initTitle()
{
	lcd.clear();
	lcd.setColor(100,255,100);
	mode = 0;
	tryLimit = maxTryLimit;
	lcd.setCursor(0,2);
	lcd.write("[0]");
	lcd.write("Select Mode");
	lcd.write("[0]");
	lcd.setCursor(1,2);
	lcd.write("[1P]")
	lcd.setCursor(1,7);
	lcd.write("2P");
}
function initGamePlay()
{
	lcd.clear();
	var type = ["A","2","3","4","5","6","7","8","9","[2]","J","Q","K"];
	var num;
	if (gameLength == "half") {
		num = 2;
	} else {
		num = 4;
	}
	var deck = new Array(num*13);
	for (var i = 0; i < 13; i++) {
		for (var j = 0; j < num; j++) {
			deck[i*num+j] = type[i];
		}
	}
	deck.sort(function() {
		return Math.random() - 0.5;
	});
  for (var i = 0; i < deck.length; i++) {
	  cards[i] = {name:deck[i], valid:true, state:"back", pic:back[i%2]};
	}
	flipCount == 0;
	turn = 0;
	var tls = [40, 30, 20];
	tryLimit = tls[level];
	if (gameLength == "half") {
		tryLimit /= 2;
	}
	score[0] = 0; score[1] = 0;
	page = 1;
	lastPage = 1;
	cPos.x = 0; cPos.y = 0;
}
function mainLoop()
{
	moveCursor();
	var index = (cPos.x+(page-1)*2)*height + cPos.y;
	if (index >= cards.length || cPos.y >= height) {
		return;
	}
  if (input[turn].button && flipCount < 2 && cards[index].state == "back") {
		if (flipCount == 0 && isAnimating) {
			return;
		}
		faceUp(index);
		return;
	}
  
	if (flipCount >= 2) {
		checkPair();
		return;
	}
}
function getInput()
{
	for (i = 0; i < 2; i++) {
		var y = js[i].getXInput() - neut[i].x;
		var x = -js[i].getYInput() + neut[i].y;
		if (x > 0.1) {
			input[i].x = 1;
		} else if (x < -0.1) {
			input[i].x = -1;
		} else {
			input[i].x = 0;
		}
		if (y > 0.05) {
			input[i].y = 1;
		} else if (y < -0.05) {
			input[i].y = -1;
		} else {
			input[i].y = 0;
		}
		if (button[i].value() > 0) {
			input[i].button = true;
		} else {
			input[i].button = false;
		}
	}
}
function moveCursor()
{
  if (input[turn].x == 1) {
		if (cPos.x == 0) {
			cPos.x++;
			playMove();
		} else if (page == 1) {
			cPos.x--;
			page++;
			playMove();
		}
  } else if (input[turn].x == -1) {
		if (cPos.x == 1) {
			cPos.x--;
			playMove();
		} else if (page == 2) {
			cPos.x++;
			page--;
			playMove();
		}
	}
  if (input[turn].y == 1 && cPos.y < height) {
		cPos.y++;
		playMove();
  } else if (input[turn].y == -1 && cPos.y > 0) {
		cPos.y--;
		playMove();
  }
}	
function faceUp(i)
{
	cards[i].state = "front";
	pair[flipCount] = i;
	flipCount++;
}
function checkPair()
{
	var p0 = pair[0];
	var p1 = pair[1];
	flipCount = 0;
	var success = cards[pair[0]].name == cards[pair[1]].name;
	if (!success) {
		lcd.setColor(255,100,100);
		incorrect();
		tryLimit--;
	} else {
		correct();
	}
  if (success) {
		pickCard(p0);
		pickCard(p1);
	} else {
		waitingButton = true;
	}
}
function faceDown(i1, i2)
{
	cards[i1].state = "back";
	cards[i2].state = "back";
	if (mode == 1) {
		turn ^= 1;
	}
}
function pickCard(i)
{
	cards[i].state = "invalid";	
	score[turn]++;
}
function showScore()
{
	if (mode == 0) {
		display.writeString(("000"+tryLimit).slice(-4));
		display.setColon(false);
	} else {
		display.writeString(("0"+score[0]).slice(-2)+("0"+score[1]).slice(-2));
		display.setColon(true);
	}
}
function showCursor()
{
	lcd.cursorBlinkOn();
	lcd.setCursor(cPos.x, cPos.y);
}
function showPageInfo()
{
	if (page == 1) {
		lcd.setCursor(1,15);
		lcd.write("[3]");
		lcd.setCursor(0,15);
		lcd.write(" ");
	} else {
		if (lastPage != page) {
			lcd.clear();
		}
		lcd.setCursor(0,15);
		lcd.write("[4]");
		lcd.setCursor(1,15);
		lcd.write(" ");
	}
	lastPage = page;
}
function showCards()
{
	cards.forEach(function(card, num) {
		var y = num%height;
		var x = (num-y)/height;
		if (page == 1) {
			if (x < 2) {
				lcd.setCursor(x, y);
				switch (card.state) {
				case "front":
					lcd.write(card.name);
					break;
				case "flip":
					lcd.write(flip);
					break;
				case "back":
					lcd.write(card.pic);
					break;
				case "invalid":
					lcd.write(" ");
					break;
				}
			}
		} else {
			if (x >= 2) {
				lcd.setCursor(x-2, y);
				switch (card.state) {
				case "front":
					lcd.write(card.name);
					break;
				case "flip":
					lcd.write(flip);
					break;
				case "back":
					lcd.write(card.pic);
					break;
				case "invalid":
					lcd.write(" ");
					break;
				}
			}
		}
	});
}
var defVolume = 0.05;
function stopSound()
{
	myBuzzer.stopSound();
}
function playMove()
{
	myBuzzer.setVolume(defVolume*2);
	myBuzzer.playSound(500,10000);
	stopSound();
}
function correct(){
	myBuzzer.setVolume(defVolume);
	myBuzzer.playSound(1900, 100000);
	myBuzzer.playSound(upmBuzzer.SOL, 200000);
	stopSound();
}
function incorrect(){
	myBuzzer.setVolume(defVolume);
	myBuzzer.playSound(10000, 100000);
	myBuzzer.playSound(10000, 200000);
	stopSound();
}
//宣言文でconstを用いるとプレビューが表示されなくなるようです
function melody_ff()
{
	myBuzzer.setVolume(defVolume);
	var F = upmBuzzer.FA;
	var G = upmBuzzer.SOL;
	var A = upmBuzzer.LA;
	var FOUR = 300000;
	var THREE = 100000;
	var chords = [A,A,A,A,F,G,A,A,G,A];
	var length = [THREE,THREE,THREE,FOUR,FOUR,FOUR,THREE,THREE,THREE,FOUR*3];
	var chordIndex = 0;
	if (chords.length != 0)
	{
		for(chordIndex = 0; chordIndex < chords.length; chordIndex++){
			//Play sound for one second
			myBuzzer.playSound(chords[chordIndex], length[chordIndex]);
			myBuzzer.stopSound();
		}
		//Reset the sound to start from the beginning. 
		if (chordIndex > chords.length - 1)
			chordIndex = 0;
	}
	stopSound();
}
function melody_mro(){
	myBuzzer.setVolume(defVolume);
	var G = upmBuzzer.SOL;
	var HIF = (upmBuzzer.FA / 2);
	var HIE = (upmBuzzer.MI / 2);
	var HID = (upmBuzzer.RE / 2);
	var HIC = (upmBuzzer.DO / 2);
	var B = upmBuzzer.SI;
	var E = upmBuzzer.MI;
	var C = upmBuzzer.DO;
	var EIGHT = 150000;
	var THREE = 200000;
	
	var chords = [B,HIF,HIF,HIF,HIE,HID,HIC,E,E,C];
	var length = [EIGHT,EIGHT*2,EIGHT,THREE,THREE,THREE,EIGHT,EIGHT*2,EIGHT,EIGHT*2];
	var chordIndex = 0;
	if (chords.length != 0)
	{
		for(chordIndex = 0; chordIndex < chords.length; chordIndex++){
			//Play sound for one second
			myBuzzer.playSound(chords[chordIndex], length[chordIndex]);
			myBuzzer.stopSound();
		}
		//Reset the sound to start from the beginning. 
		if (chordIndex > chords.length - 1)
			chordIndex = 0;
	}
	stopSound();
}