#include <iostream>
#include <stack>
#include <cstdlib>
#include <ctime>
#include <conio.h>
using namespace std;

#define clearscr() system("cls")

int getCardValue(int card);
void getPassCode(char code[]);
void initDeck(stack<int>& deck);
void initPlayers(int players, stack<int>& deck);
void playGame(int players);
int playTurn(int p, int tableSum, stack<int>& deck, int moreCards, int laidCards);
void printCardName(int card);
void printInfo(void);
void printRules(void);
int random(int min, int max);
void shuffleIntoDeck(stack<int>& cards, stack<int>& deck);
bool testPassCode(int p, char code[]);

const char version[] = "1.2.4";
const int PASSLEN = 3; //spelkodslängd


struct PlayerData {
	//spelinformation
	int card[4];
	int chips;
	bool dead;
	
	//programinformation
	char passCode[PASSLEN];
};

PlayerData player[8];

int main(void) {
	int players;
	int ans;
	bool cont = true;
	
	srand(time(0));
	
	do {
		clearscr();
		cout << "LoboTomi 77 :: Meny\n====================\n"
		     << "1. Nytt Spel\n"
		     << "2. Regler\n"
		     << "3. Om\n"
		     << "4. Avsluta\n\n"
		     << "Val: ";
		cin >> ans;
		cout << "\n";
		
		switch (ans) {
			case 1:
				cout << "Hur m\x86nga spelare? (2-8): ";
				do {
					cin >> players;
				} while ((players < 2) || (players > 8));
				
				playGame(players);
				break;
			case 2:
				printRules();
				break;
			case 3:
				printInfo();
				break;
			case 4:
				cont = false;
				break;
			default:
				cout << "Felaktigt val!";
		}
		if (cont) {
			cout << "\n\nTryck p\x86 valfri tangent f\x94r att forts\x84tta...";
			getch();
		}
	} while (cont);
	
	return 0;
}

int getCardValue(int card) {
	/* Kort:
	3 av varje tal 2-9
	4 nollor
	4 "-10"
	8 "10"
	6 dubbelkort (11-66)
	1 "76"
	5 Back to you
	4 In your face
	--
	tot: 56
	*/
	
	int value;
	if (card <= 8) {
		value = card + 1; //2-9
	} else if (card <= 16) {
		value = card - 7; //2-9
	} else if (card <= 24) {
		value = card - 15; //2-9
	} else if (card <= 28) {
		value = 0; //0...
	} else if (card <= 32) {
		value = -10; //-10...
	} else if (card <= 40) {
		value = 10; //10...
	} else if (card <= 46) {
		value = (card - 40) * 11; //dubbelkort 11-66
	} else if (card == 47) {
		value = 76; //76...
	} else if (card <= 52) {
		value = -1; //Back to you
	} else if (card <= 56) {
		value = -2; //In your face
	}
	return value;
}

void getPassCode(char code[]) {
	int inChar; //inChar som int är ett krav för kontrollen nedan. (getch() returnerar en int.)
	
	for (int i = 0; i < PASSLEN; i++) {
		do {
			inChar = getch();
			if ((inChar == 0) || (inChar == 224)) { //BCC/VC++ och MinGW beter sig olika här...
				getch(); //Om användaren tryckte på en knapp som inte är en char, ignorera knappkoden.
			}
		} while ((inChar == 0) || (inChar == 224)); //Gör om till giltigt tecken matas in.
		code[i] = char(inChar);
		cout << "*";
	}
}

void initDeck(stack<int>& deck) {
	stack<int> newDeck;
	
	while (!deck.empty()) { //Töm den gamla högen, om den finns.
		deck.pop();
	}
	for (int i = 1; i <= 56; i++) {
		newDeck.push(i);
	}
	shuffleIntoDeck(newDeck, deck);
}

void initPlayers(int players, stack<int>& deck) {
	char testCode[PASSLEN];
	bool equal;
	
	clearscr();
	cout << "LoboTomi 77 :: Spelkod\n=======================\n"
	     << "Varje spelare beh\x94ver en spelkod.\n"
	     << "Denna ser till s\x86 att bara den enskilde spelaren kan se sina kort.\n"
	     << "Koden ska vara " << PASSLEN << " tecken l\x86ng (exakt), och du m\x86ste komma ih\x86g den under spelets g\x86ng.\n"
	     << "Vad du skriver kommer INTE att synas, och du ska INTE trycka Enter efter att du\nhar matat in " << PASSLEN << " tecken.\n"
		 << "Du kan inte heller anv\x84nda backsteg f\x94r att ta bort tecken du skrivit.\n\n";
	for (int i = 0; i < players; i++) {
		cout << "------- ------ ----- ---- --- -- -\n\n";
		do {
			cout << "Spelare " << i + 1 << ", mata in en valfri spelkod: ";
			getPassCode(player[i].passCode);
			cout << "\nEn g\x86ng till (f\x94r kontroll): ";
			getPassCode(testCode);
			cout << "\n\n";
			equal = testPassCode(i,testCode);
			if (!equal) {
				cout << "Du angav tv\x86 olika spelkoder! F\x94rs\x94k igen.\n\n";
			}
		} while (!equal);
		
		player[i].chips = 3;
		player[i].dead = false;
		for (int j = 0; j < 4; j++) {
			player[i].card[j] = deck.top();
			deck.pop();
		}
	}
}

void playGame(int players) {
	int p = 0, pVal, pCard, numCards = 1, nextNumCards, pLaid, alivePlayers = players, i, dir, tableSum;
	stack<int> deck, table, playerCards; //playerCards används när en spelare dör.
	bool found = false;
		
	tableSum = 0;
	dir = 1;
	
	initDeck(deck);
	initPlayers(players, deck);
	
	do {
		if (!player[p].dead) {
			nextNumCards = 1;
			pLaid = 0; //Hur många kort spelaren har lagt, inget annat... pervo...
			do {
				pCard = playTurn(p, tableSum, deck, numCards, pLaid++);
				
				table.push(pCard);
				pVal = getCardValue(pCard);
				numCards--;
				switch (pVal) {
					case -1:
						dir -= 2 * dir; //1 <-> -1
						break;
					case -2:
						nextNumCards *= 2;
						break;
					default:
						tableSum += pVal;
						if (tableSum >= 77) {
							if (player[p].chips > 0) {
								player[p].chips = 0;
								numCards = 1; //Spelaren ska lägga ett kort i detta läge, oavsett tidigare kort.
							} else {
								player[p].dead = true;
							}
							tableSum = 0;
							shuffleIntoDeck(table, deck);
						} else if ((tableSum % 11 == 0) && (tableSum != 0)) { // 0/11 = 0, rest: 0...
							if (player[p].chips > 0) {
								player[p].chips--;
							} else {
								player[p].dead = true;
							}
						}
						if (player[p].dead) {
							alivePlayers--;
							cout << "\n\n------- ------ ----- ---- --- -- -\n\n"
							     << "Tyv\x84rr!\nSpelare " << p + 1 << ", du \x84r ute ur spelet.\n\n"
							     << "------- ------ ----- ---- --- -- -\n\n";
							if (alivePlayers > 1) {
								cout << "Tryck p\x86 valfri tangent f\x94r att forts\x84tta...";
								getch();
							}
							for (i = 0; i < 4; i++) {
								playerCards.push(player[p].card[i]);
							}
							shuffleIntoDeck(playerCards, deck);
							numCards = 0; // så inte spelaren blir tillfrågad igen denna rundan.
						}
						break;
				}
			} while (numCards > 0);
			numCards = nextNumCards;
		}
		p += dir;
		if (p < 0) {
			p = players - 1;
		} else if (p >= players) {
			p = 0;
		}
	} while (alivePlayers > 1);
	
	for (i = 0; (i < players) && (!found); i++) {
		if (!player[i].dead) {
			cout << "Spelare " << i + 1 << " vann.\n\n"
			     << "------- ------ ----- ---- --- -- -";
			found = true;
		}
	}
}

int playTurn(int p, int tableSum, stack<int>& deck, int moreCards, int laidCards) {
	int card, cardVal, cardNum, newSum;
	char code[PASSLEN];
	bool equal;
	
	if (laidCards == 0) {
		clearscr();
		cout << "LoboTomi 77 :: Spelare " << p + 1 << "\n=========================\n\n"
		     << "Mata in din spelkod (vad du skriver syns INTE): ";
		do {
			getPassCode(code);
			equal = testPassCode(p, code);
			if (!equal) {
				cout << "\nFel Kod! F\x94rs\x94k igen: ";
			}
		} while (!equal);
	}
	
	clearscr();
	cout << "LoboTomi 77 :: Spelare " << p + 1 << "\n=========================\n\n";
	cout << "Du ska l\x84gga " << moreCards << " kort";
	if (laidCards > 0) {
		cout << " till";
	}
	cout << ".\n\n";
	cout << "Bordssumma: " << tableSum << "\n"
	     << "Antal spelmarker: " << player[p].chips << "\n\n"
	     << "Dina kort\n----------\n";
	
	for (int i = 0; i < 4; i++) {
		 cout << i + 1 << ": ";
		 printCardName(player[p].card[i]);
		 cout << "\n";
	}
	
	cout << "\nL\x84gg kort nummer: ";
	do {
		cin >> cardNum;
	} while ((cardNum < 1) || (cardNum > 4));
	card = player[p].card[cardNum - 1];
	player[p].card[cardNum - 1] = deck.top();
	deck.pop();
	
	cardVal = getCardValue(card);
	cout << "\nDu lade ";
	printCardName(card);
	cout << " p\x86 bordet, ";
	if (cardVal == -1) {
		cout << "spelordningen v\x84nds";
	} else if (cardVal == -2) {
		cout << "n\x84sta person m\x86ste l\x84gga dubbelt antal kort";
	} else {
		newSum = tableSum + cardVal;
		cout << "summan blir nu " << newSum;
		if (player[p].chips > 0) {
			if (newSum >= 77) {
				cout << ".\nDu f\x94rlorar alla dina spelmarker och nollst\x84ller bordssumman.\nDet \x84r din tur igen";
			} else if ((newSum % 11 == 0) && (newSum != 0)) {  // 0/11 = 0, rest: 0...
				cout << ".\nDu f\x94rlorar en spelmarker";
			}
			cout << ".\nDitt nya kort blev ";
			printCardName(player[p].card[cardNum - 1]);
		} else if ((newSum >= 77) || ((newSum % 11 == 0) && (newSum != 0))) {
			cout << ".\nDu har utnyttjat din sista chans";
		}
	}
	cout <<".\n\nTryck p\x86 valfri tangent f\x94r att forts\x84tta...";
	getch();
	return card;
}

void printCardName(int card) {
	switch (getCardValue(card)) {
		case -1:
			cout << "\x1D";
			break;
		case -2:
			cout << "2x";
			break;
		default:
			cout << getCardValue(card);
			break;
	}
}

void printInfo(void) {
	clearscr();
	cout << "LoboTomi 77 :: Om\n==================\n\n"
	     << "Version " << version << "\n\n"
	     << "------- ------ ----- ---- --- -- -\n\n"
	     << "Detta spel \x84r baserat p\x86 kortspelet LoBo 77, som i Sverige distribueras av\n"
	     << "Midgaard Games.\n"
	     << "Om du gillar detta spel b\x94r du definitivt k\x94pa den fysiska varianten, den \x84r\n"
	     << "mycket roligare!\n"
		 << "(Finns att k\x94pa i diverse spelbutiker, och kostar runt 60 kr.)\n\n"
	     << "LoboTomi 77 skapades i december 2005 av Jocke \"Firetech\" Andersson.\n\n"
	     << "------- ------ ----- ---- --- -- -";
}

void printRules(void) {
	clearscr();
	cout << "LoboTomi 77 :: Regler (Sida 1/2)\n=================================\n\n"
	     << "I LoboTomi 77 g\x84ller det att h\x86lla sig undan talet 77 och andra dubbeltal.\n\n"
	     << "Kortleken inneh\x86ller f\x94ljande kort:\n"
	     << "* 24 st vanliga sifferkort (3 av varje tal 2-9)\n"
	     << "*  4 st 0\n"
	     << "*  4 st -10\n"
	     << "*  8 st 10\n"
	     << "*  6 st dubbelkort (11-66)\n"
	     << "*  1 st 76\n"
	     << "*  5 st \x1D\n"
	     << "*  4 st 2x\n\n"
	     << "Som du ser s\x86 st\x86r det en siffra, \x1D eller 2x p\x86 varje kort.\n\n"
	     << "\x1D v\x84nder spelordningen, s\x86 att det blir den f\x94rre spelarens tur.\n"
	     << "2x tvingar n\x84sta spelare att l\x84gga dubbelt antal kort.\n"
	     << "Det \x84r m\x94jligt att besvara ett 2x-kort med tv\x86 nya 2x-kort, och p\x86 s\x86 s\x84tt\n"
	     << "tvinga n\x84sta spelare att l\x84gga 4 kort.\n\n"
	     << "------- ------ ----- ---- --- -- -\n\n"
	     << "Tryck p\x86 valfri tangent f\x94r att l\x84sa vidare...";
	getch();
	
	clearscr();
	cout << "LoboTomi 77 :: Regler (Sida 2/2)\n=================================\n\n"
	     << "Varje spelare f\x86r 4 kort och 3 spelmarker, d\x84refter b\x94rjar Spelare 1 med att\n"
		 << "l\x84gga ett av sina kort p\x86 bordet.\n"
	     << "Sedan ska l\x84gga Spelare 2 ett av sina kort. Bordssumman blir nu det sammanlagda\n"
		 << "v\x84rdet av dessa kort.\n\n"
		 << "N\x84r en spelare l\x84gger ett sifferkort (alla kort utom \x1D och 2x) g\x84ller f\x94ljande:\n"
	     << "* Om bordssumman blir j\x84mnt delbar med 11 (11, 22, 33 etc.), blir spelaren av\n"
	     << "  med EN av sina spelmarker.\n"
		 << "* Om bordssumman blir 77 eller h\x94gre, blir spelaren av med ALLA de\n"
		 << "  spelmarker spelaren har kvar. H\x94gen (bordssumman) t\x94ms d\x86, och spelaren f\x86r\n"
		 << "  b\x94rja p\x86 en ny h\x94g.\n\n"
	     << "N\x84r en spelare har f\x94rlorat alla sina spelmarker har spelaren en sista chans,\n"
		 << "d\x86 denne m\x86ste undvika att f\x86 summan till n\x86got av specialfallen ovan.\n"
	     << "G\x94r man ett misstag och inte har n\x86gra spelmarker kvar \x86ker man ut ur spelet,\n"
		 << "och det blir n\x84sta spelares tur.\n\n"
	     << "Den spelare som till sist \x84r ensam kvar i spelet vinner.\n\n"
	     << "------- ------ ----- ---- --- -- -";
}

int random(int min, int max) {
	int range = max - min + 1;
	return min + int(range * rand()/(RAND_MAX + 1.0));
}

void shuffleIntoDeck(stack<int>& cards, stack<int>& deck) {
	int shuffDeck[56], newPos, c = 0, i;
	
	while (!deck.empty()) {
		shuffDeck[c] = deck.top();
		deck.pop();
		c++;
	}
	while (!cards.empty()) {
		shuffDeck[c] = cards.top();
		cards.pop();
		c++;
	}
	for (i = 0; i < c; i++) {
		do {
			newPos = random(0,c - 1);
		} while (newPos == i);
		swap(shuffDeck[i], shuffDeck[newPos]);
	}
	for (i = 0; i < c; i++) {
		deck.push(shuffDeck[i]);
	}
}

bool testPassCode(int p, char code[]) {
	bool equal = true;
	
	for (int j = 0; (j < PASSLEN) && (equal); j++) {
		if (player[p].passCode[j] != code[j]) {
			equal = false;
		}
	}
	return equal;
}

