<?php namespace App\Controllers;
/******************************************************************************************************************
 * RPSLS - Rock, Paper, Scissors, Lizard, Spock Game
 *
 * @file      app/Controllers/GameController.php
 * @package   App\Controllers
 * @description
 *      Main Controller for Game.
 * @author    Rick Hays
 * @date      11/15/2019
 * @license   MIT
 * @copyright Copyright © 2019 - Rick Hays, All Rights Reserved.
 *
 * Revisions:
 * YYYY-MM-DD  XX  Description
 ******************************************************************************************************************/
	use App\Libraries\Location;
	use App\Models\UserModel;
	use App\Models\PlayModel;
	use App\Models\LogModel;

    class GameController extends BaseController
	{
		private $session;

	    /***************************************************************************************************************
	     * GameController constructor.
	     */
	    public function __construct()
	    {
		    $this->session = session();
		    if (empty($this->session->get('ip')) && empty($this->session->get('UserID')))
		    {
		    	$this->CreateSession();
		    	$this->CreateLogRecord();
			    return redirect()->route('/');
		    }
		    else
		    {
			    return redirect()->route('play');
		    }
	    }

	    /***************************************************************************************************************
	     * @method index
	     * @description
	     *       Main page for game, Start/Home Page.
	     */
	    public function index()
		{
		    $HeaderData = $this->getHeaderData();
			echo view('inc/head');
			echo view('inc/header', $HeaderData);
			echo view('game');
			echo view('inc/footer');
		}

	    /***************************************************************************************************************
	     * @method createUser
	     * @description
	     *       Creates user for game play.
	     * @param $UserName
	     * @param $UserID
	     *
	     * @return \CodeIgniter\HTTP\RedirectResponse
	     * @throws \ReflectionException
	     */
		public function createUser($UserName, $UserID)
		{
			$UserName = esc($UserName);
			$UserID   = esc($UserID);
			$dbUsers     = new UserModel();

			// Validate User Name
			if (!$this->validateUserName($UserName)) return redirect()->to(base_url() . '/getUser');
			$UserID = $this->saveUserData($UserID, $UserName);
			$UserData = $dbUsers->find($UserID);
			// Add user to the Session
			$data =
			[
				'UserID'	=> $UserID,
				'UserName'  => $UserName,
				'Wins'      => $UserData['Wins'],
				'Loses'     => $UserData['Loses'],
				'Ties'      => $UserData['Ties']
			];
			$this->session->set($data);
			// Pass off to PLAY()
			return redirect()->route('play');
		}

	    /***************************************************************************************************************
	     * @method endGame
	     * @description
	     *       Destroys Session and sends them back to Home Page.
	     * @return \CodeIgniter\HTTP\RedirectResponse
	     */
		public function endGame()
		{
			// Close Session
			$this->session->destroy();
			// Goto Index page
			return redirect()->route('/');
		}

	    /***************************************************************************************************************
	     * @method getUser
	     * @description
	     *       Prompt Screen to get user name for game play.
	     */
		public function getUser()
		{
			$getUserData = ['UserName' => $this->session->getFlashdata('UserName'), 'ErrorCode' => $this->session->getFlashdata('ErrorCode')];
			$HeaderData  = $this->getHeaderData();
			echo view('inc/head');
			echo view('inc/header', $HeaderData);
			echo view('getUser', $getUserData);
			echo view('inc/footer');
		}

	    /***************************************************************************************************************
	     * @method play
	     * @description
	     *       Main Play Page/Function Logic.
	     *
	     * @param  null $PlayerPick
		 * @todo   REFACTOR GAME PLAY!!
	     *
	     * @return \CodeIgniter\HTTP\RedirectResponse|false|string
	     * @throws \ReflectionException
	     */
		public function play($PlayerPick = null)
		{
			$UserID = $this->session->UserID;

			if ($PlayerPick)
			{
				$W             = 0;  // Win
				$L             = 0;  // Lose
				$T             = 0;  // Tie
				$ComputerPick  = $this->ComputerPick();
				$textResult    = '';
				$TextResults   =
				[
					'RL'   => 'Rock Crushes Lizard!',
					'RS'   => 'Rock Crushes Scissors!',
					'PR'   => 'Paper Covers Rock!',
					'PS'   => 'Paper Disproves Spock!',
					'SP'   => 'Scissors Cuts Paper!',
					'SL'   => 'Scissors Decapitates Lizard!',
					'LS'   => 'Lizard Poisons Spock!',
					'LP'   => 'Lizard Eats Paper!',
					'SS'   => 'Spock Smashes Scissors!',
					'SR'   => 'Spock Vaporizes Rock!',
					'TIE'  => 'No One Wins This Time.',
					'DRAW' => 'Danger, Danger Will Robinson, that does not compute!',
				];

				/** GAME LOGIC */
				switch(strtoupper($PlayerPick))
				{
					case 'ROCK':
						switch(strtoupper($ComputerPick))
						{
							case 'ROCK':     $textResult = 'TIE';  $W = 0; $L = 0; $T = 1; break;
							case 'PAPER':    $textResult = 'PR';   $W = 0; $L = 1; $T = 0; break;
							case 'SCISSORS': $textResult = 'RS';   $W = 1; $L = 0; $T = 0; break;
							case 'LIZARD':   $textResult = 'RL';   $W = 1; $L = 0; $T = 0; break;
							case 'SPOCK':    $textResult = 'SR';   $W = 0; $L = 1; $T = 0; break;
							default:         $textResult = 'DRAW'; $W = 0; $L = 0; $T = 0;
						}
						break;
					case 'PAPER':
						switch(strtoupper($ComputerPick))
						{
							case 'ROCK':     $textResult = 'PR';   $W = 1; $L = 0; $T = 0; break;
							case 'PAPER':    $textResult = 'TIE';  $W = 0; $L = 0; $T = 1; break;
							case 'SCISSORS': $textResult = 'SP';   $W = 0; $L = 1; $T = 0; break;
							case 'LIZARD':   $textResult = 'LP';   $W = 0; $L = 1; $T = 0; break;
							case 'SPOCK': 	 $textResult = 'PS';   $W = 1; $L = 0; $T = 0; break;
							default:         $textResult = 'DRAW'; $W = 0; $L = 0; $T = 0;
						}
						break;
					case 'SCISSORS':
						switch(strtoupper($ComputerPick))
						{
							case 'ROCK':     $textResult = 'RS';   $W = 0; $L = 1; $T = 0; break;
							case 'PAPER':    $textResult = 'SP';   $W = 1; $L = 0; $T = 0; break;
							case 'SCISSORS': $textResult = 'TIE';  $W = 0; $L = 0; $T = 1; break;
							case 'LIZARD':   $textResult = 'SL';   $W = 1; $L = 0; $T = 0; break;
							case 'SPOCK':    $textResult = 'SS';   $W = 0; $L = 1; $T = 0; break;
							default:         $textResult = 'DRAW'; $W = 0; $L = 0; $T = 0;
						}
						break;
					case 'LIZARD':
						switch(strtoupper($ComputerPick))
						{
							case 'ROCK':     $textResult = 'RL';   $W = 0; $L = 1; $T = 0; break;
							case 'PAPER':    $textResult = 'LP';   $W = 1; $L = 0; $T = 0; break;
							case 'SCISSORS': $textResult = 'SL';   $W = 0; $L = 1; $T = 0; break;
							case 'LIZARD':   $textResult = 'TIE';  $W = 0; $L = 0; $T = 1; break;
							case 'SPOCK':    $textResult = 'LS';   $W = 1; $L = 0; $T = 0; break;
							default:         $textResult = 'DRAW'; $W = 0; $L = 0; $T = 0;
						}
						break;
					case 'SPOCK':
						switch(strtoupper($ComputerPick))
						{
							case 'ROCK':     $textResult = 'SR';   $W = 1; $L = 0; $T = 0; break;
							case 'PAPER':    $textResult = 'PS';   $W = 0; $L = 1; $T = 0; break;
							case 'SCISSORS': $textResult = 'SS';   $W = 1; $L = 0; $T = 0; break;
							case 'LIZARD':   $textResult = 'LS';   $W = 0; $L = 1; $T = 0; break;
							case 'SPOCK':    $textResult = 'TIE';  $W = 0; $L = 0; $T = 1; break;
							default:         $textResult = 'DRAW'; $W = 0; $L = 0; $T = 0;
						}
						break;
					default:
						// TODO: SOME RETURN ERROR GOES HERE
						//       AS WE SHOULD NEVER GET HERE...
				}

				// Text for Play Result of who Won, Lost or Tied.
				if     ($W === 1 && $L === 0 && $T === 0) $PlayResult = 'WINNER';
				elseif ($W === 0 && $L === 1 && $T === 0) $PlayResult = 'LOSER';
				elseif ($W === 0 && $L === 0 && $T === 1) $PlayResult = 'TIE';
				else                                      $PlayResult = 'DRAW';

				$this->addPlay( $this->session->UserID, $this->session->UserName, $this->session->ip, $PlayerPick, $ComputerPick, $PlayResult);

				$data =
				[
					'Wins'  => $this->session->Wins  + $W,
					'Loses' => $this->session->Loses + $L,
					'Ties'  => $this->session->Ties  + $T,
				];
				$this->session->set($data);
				$this->updateDBCounts($this->session->UserID, $this->session->Wins, $this->session->Loses, $this->session->Ties);

				////////////////////////////////////////////////////////////////////////////////////////////////////////

				$data =
				[
					'PlayerPick'    => $PlayerPick,
					'ComputerPick'  => $ComputerPick,
					'Result'        => $PlayResult,
					'ResultText'    => $TextResults[$textResult],
					'Wins'          => $this->session->Wins,
					'Loses'         => $this->session->Loses,
					'Ties'          => $this->session->Ties,
				];
				return  json_encode($data);
			}
			else
			{
				$playData =
				[
					'UserID'    => $this->session->UserID,
					'UserName'  => $this->session->UserName,
					'Wins'      => $this->session->Wins,
					'Loses'     => $this->session->Loses,
					'Ties'      => $this->session->Ties,
				];

				if (empty($UserID))
				{
					return redirect()->to(base_url() . '/getUser');
				}
				else
				{
					$HeaderData = $this->getHeaderData();
					echo view('inc/head');
					echo view('inc/header', $HeaderData);
					echo view('play', $playData);
					echo view('inc/footer');
				}
			}
		}

	    /**
	     *************************************************************************************************************
	     * @method testUser
	     * @description
	     *       Returns UserID from User Name if one exists else it will return a 0.
	     *
	     * @param $UserName
	     *
	     * @return string
	     */
		public function testUser($UserName)
		{
			// TODO: Have it compare more then name to equal the same
			//       if not the same country and city then its a new user
			//       and they should pick a different name, as this one
			//       belongs to someone else.  This will keep stats correct!
			return $this->getIDfromUserName($UserName);
		}

	    /***************************************************************************************************************
	     * @method top10
	     * @description
	     *       Logic for the Top10 page.
	     */
		public function top10()
		{
			//Table Models
			$dbUsers            = new UserModel();
			$dbPlay             = new PlayModel();
			// Queries
			$TotalPlays         = $dbPlay->query("SELECT COUNT(Result) AS Total FROM plays")->getRow();
			$TotalWinsPlayer    = $dbPlay->query("SELECT COUNT(Result) AS Total FROM plays WHERE Result = 'WINNER'")->getRow();
			$TotalLosesPlayer   = $dbPlay->query("SELECT COUNT(Result) AS Total FROM plays WHERE Result = 'LOSER'")->getRow();
			$TotalTiesPlayer    = $dbPlay->query("SELECT COUNT(Result) AS Total FROM plays WHERE Result = 'TIE'")->getRow();
//			echo '##### RESULT: ' . $TotalsWinsPlayer->Total . ' #####';
			$Win10              = $dbUsers->select('UserName, Wins')->OrderBy('Wins', 'DESC')->findAll(10);
			$Lose10             = $dbUsers->select('UserName, Loses')->OrderBy('Loses', 'DESC')->findAll(10);
			$Tie10              = $dbUsers->select('UserName, Ties')->OrderBy('Ties', 'DESC')->findAll(10);
			$PlayerPicks        = $dbPlay->select('PlayerPick, COUNT(PlayerPick) AS Total')->groupBy('PlayerPick')->OrderBy('Total', 'DESC')->findAll(5);
			$ComputerPicks      = $dbPlay->select('ComputerPick, COUNT(PlayerPick) AS Total')->groupBy('ComputerPick')->OrderBy('Total', 'DESC')->findAll(5);
			// Combine Results for View
			$PageData           =
			[
				'Win10'             => $Win10,
				'Lose10'            => $Lose10,
				'Tie10'             => $Tie10,
				'PlayerPicks'       => $PlayerPicks,
				'ComputerPicks'     => $ComputerPicks,
				'TotalWinsPlayer'   => $TotalWinsPlayer,
				'TotalLosesPlayer'  => $TotalLosesPlayer,
				'TotalTiesPlayer'   => $TotalTiesPlayer,
				'TotalPlays'        => $TotalPlays
			];

			$HeaderData = $this->getHeaderData();
			echo view('inc/head');
			echo view('inc/header', $HeaderData);
			echo view('top10', $PageData);
			echo view('inc/footer');
		}

		// #############################################################################################################
		// ### START OF PRIVATE FUNCTIONS
		// #############################################################################################################

	    /***************************************************************************************************************
	     * @method addPlay
	     * @description
	     *       Adds (Inserts) Game Play Results to the DB.
	     *
	     * @param $UserID
	     * @param $UserName
	     * @param $IP
	     * @param $PlayerPick
	     * @param $ComputerPick
	     * @param $Result
	     *
	     * @throws \ReflectionException
	     */
		private function addPlay($UserID, $UserName, $IP, $PlayerPick, $ComputerPick, $Result)
		{
			$dbPlay = new PlayModel();
			$data  =
				[
					'UserID'        => $UserID,
					'UserName'      => $UserName,
					'IP'            => $IP,
					'PlayerPick'    => $PlayerPick,
					'ComputerPick'  => $ComputerPick,
					'Result'        => $Result
				];
			$dbPlay->insert($data);
		}

	    /***************************************************************************************************************
	     * @method ComputerPick
	     * @description
	     *       Returns the Computers Pick/Choice.
	     * @return string
	     */
		private function ComputerPick()
		{
			mt_srand(crc32(microtime()));
			$pick          = mt_rand(0,4);
			$ComputerPicks = array('Rock', 'Paper', 'Scissors', 'Lizard', 'Spock');
			return $ComputerPicks[$pick];
		}

	    /***************************************************************************************************************
	     * @method CreateLogRecord
	     * @description
	     *       Inserts a Log Record at start of New Session.
	     * @throws \ReflectionException
	     */
		private function CreateLogRecord()
		{
			$dbLog = new LogModel();
			$data  =
			[
				'IP' => $this->session->get('ip'),
				'City' => $this->session->get('city'),
				'State' => $this->session->get('region_name'),
				'Country' => $this->session->get('country_name'),
			];
			$dbLog->insert($data);
		}

	    /***************************************************************************************************************
	     * @method CreateSession
	     * @description
	     *       Creates a new session for start of Game Play.
	     */
		private function CreateSession()
		{
			$Location = new Location();
			$this->session->set($Location->Data);
		}

	    /***************************************************************************************************************
	     * @method getPageName
	     * @description
	     *       Returns the current page name, used for menu to show active page.
	     * @return string
	     */
		private function getPageName()
		{
			return $this->request->uri->getSegment(1);
		}

	    /***************************************************************************************************************
	     * @method getHeaderData
	     * @description
	     *       Creates array of header page data to pass into view.
	     * @return array
	     */
		private function getHeaderData()
		{
			$page = $this->getPageName();
			$data =
			[
				'page' => $page,
			];
			return $data;
		}

	    /***************************************************************************************************************
	     * @method getIDfromUserName
	     * @description
	     *       Returns UserID from User Name if one exists else it will return a 0.
	     *
	     * @param $UserName
	     *
	     * @return string
	     */
		private function getIDfromUserName($UserName)
		{
			$UserName = esc($UserName);
			$dbUsers     = new UserModel();
			$row = $dbUsers->select('id')->where('UserName', $UserName)->findAll(1);
			return (empty($row['0']['id'])) ? '0' : strval($row['0']['id']);
		}

	    /***************************************************************************************************************
	     * @method saveUserData
	     * @description
	     *       Either saves (Inserts) the new user, or it will save (Update) a current user in the DB.
	     *       This will return the UserID once Updated/Inserted into the DB.
	     *
	     * @param $UserID
	     * @param $UserName
	     *
	     * @return string
	     * @throws \ReflectionException
	     */
		private function saveUserData($UserID, $UserName)
		{
			$dbUsers     = new UserModel();
			if ($UserID !== '0')
			{
				// UPDATE
				$data =
					[
						'id' => $UserID,
						'IP' => $this->session->get('ip'),
					];
			}
			else
			{
				// ADD (Create)
				$data =
					[
						'UserName'    => $UserName,
						'Wins'        => 0,
						'Loses'       => 0,
						'Ties'        => 0,
						'IP'          => $this->session->get('ip'),
						'City'        => $this->session->get('city'),
						'State'       => $this->session->get('region_name'),
						'Country'     => $this->session->get('country_name'),
						'CountryCode' => $this->session->get('country_code'),
						'FlagURL'     => $this->session->get('flagUrl'),
					];
			}
			$dbUsers->save($data);
			return $this->getIDfromUserName($UserName);
		}

	    /***************************************************************************************************************
	     * @method updateDBCounts
	     * @description
	     *       This will update user scores from game play.
	     *
	     * @param $UserID
	     * @param $Wins
	     * @param $Loses
	     * @param $Ties
	     *
	     * @throws \ReflectionException
	     */
		private function updateDBCounts($UserID, $Wins, $Loses, $Ties)
		{
			$dbUsers     = new UserModel();
			$UserID = esc($UserID);
			$Wins   = esc($Wins);
			$Loses  = esc($Loses);
			$Ties   = esc($Ties);

			$data =
			[
				'id'    => $UserID,
				'Wins'  => $Wins,
				'Loses' => $Loses,
				'Ties'  => $Ties,
			];
			$dbUsers->save($data);
		}

	    /**
	     *************************************************************************************************************
	     * @method validateUserName
	     * @description
	     *       Backend user name validation.
	     *
	     * @param $UserName
	     *
	     * @return bool
	     */
		private function validateUserName($UserName)
		{
			$Valid = TRUE;
			$validation =  \Config\Services::validation();
			if (!$validation->check($UserName, 'required|regex_match[/^([0-9a-zA-Z-_@\.]*)$/]'))
			{
				$data =
					[
						'UserName'  => $UserName,
						'ErrorCode' => 'User Name can only contain characters: (a-z, A-Z, 0-9, - _ @ .) Only.'
					];
				$this->session->setFlashdata($data);
				$Valid = FALSE;
			}
			return $Valid;
		}
	}
