<?php

namespace Mine\SeekerBundle\Rpc;

use Doctrine\DBAL\Driver\PDOException;
use Doctrine\ORM\EntityManager;
use Mine\SeekerBundle\Entity\Grid;
use Mine\SeekerBundle\Entity\GridRow;
use Mine\SeekerBundle\Entity\PlayedGame;
use Psr\Log\LoggerInterface;
use Ratchet\ConnectionInterface;
use Gos\Bundle\WebSocketBundle\RPC\RpcInterface;
use Gos\Bundle\WebSocketBundle\Router\WampRequest;

class MineseekerRpc implements RpcInterface
{
    /** @var EntityManager */
    protected $em;

    protected $grid;

    private $logger;

    /**
     * MineseekerRpc constructor.
     * @param EntityManager $entityManager
     */
    public function __construct(EntityManager $entityManager, LoggerInterface $logger)
    {
        $this->em = $entityManager;
        $this->logger = $logger;
    }

    /**
     * Name of RPC, use for pubsub router (see step3)
     *
     * @return string
     */
    public function getName()
    {
        return 'mineseeker.rpc';
    }

    /**
     * @param ConnectionInterface $connection
     * @param WampRequest $request
     * @param array $params
     * @return boolean
     */
    public function startGame(ConnectionInterface $connection, WampRequest $request, array $params)
    {
        $this->reConnect($params);
        return $this->saveGrid($params);
    }

    /**
     * @param ConnectionInterface $connection
     * @param WampRequest $request
     * @param array $params
     * @return string Json string for frontend w/ numbering consideration. (=> a number is not string)
     */
    public function connectGame(ConnectionInterface $connection, WampRequest $request, array $params)
    {
        $grid = $this->getGrid($params);
        $users = null !== $grid ? $this->getUsers($params) : null;

        return base64_encode(json_encode(
            array(
                'grid' => $grid,
                'users' => $users
            )
        ));
    }

    /**
     * @param $gameAssoc
     * @return array
     */
    private function getGrid($gameAssoc)
    {
        $this->reConnect();
        $getsee = array();

        $this->em->clear();

        $grid = $this->em
            ->getRepository('MineSeekerBundle:PlayedGame')
            ->findOneByGameAssoc($gameAssoc);

        if (null !== $grid) {
            foreach ($grid->getGrid()->getGridRow()->toArray() as $row) {
                $getsee[] = $row->getGridCol();
            }

            return $getsee;
        }

        return null;
    }

    /**
     * @param $gameAssoc
     * @return array
     */
    private function getUsers($gameAssoc)
    {
        $this->reConnect();
        return $this->getUserCollection(
            $this->em
                ->getRepository('MineSeekerBundle:PlayedGame')
                ->findOneByGameAssoc($gameAssoc)
        );
    }

    /**
     * Get user collection from PlayedGame entity
     *
     * @param $playedGame
     * @return array
     */
    private function getUserCollection($playedGame)
    {
        return array(
            'red' => null !== $playedGame->getRed() ? $playedGame->getRed()->getUsername() : '',
            'blue' => null !== $playedGame->getBlue() ? $playedGame->getBlue()->getUsername() : '',
            'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '',
            'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : ''
        );
    }

    /**
     * @param $data
     * @return boolean
     */
    private function saveGrid($data)
    {
        $this->reConnect();
        $playedGame = new PlayedGame();
        $grid = new Grid();

        foreach (json_decode(base64_decode($data[0])) as $row) {
            $gridRow = new GridRow();

            $gridRow->setGridCol($row);

            /** Save Row */
            $gridRow->setGrid($grid);
            $this->em->persist($gridRow);
        }

        /** Save Grid */
        $grid->setPlayedGame($playedGame);
        $this->em->persist($grid);

        /** Save PlayedGame */
        $playedGame->setGameAssoc($data[1]);
        $playedGame->setGrid($grid);
        $playedGame->setCreated(new \DateTime());
        $playedGame->setUpdated(new \DateTime());
        $this->em->persist($playedGame);

        $this->em->flush();

        return true;
    }

    /** Handle prod MySQL timeout */
    private function reConnect()
    {
        try {
            $connection = $this->em->getConnection();

            if (false === $connection->ping()) {
                $connection->close();
                $connection->connect();
            }
        } catch(PDOException $ex) {
            $this->logger->error($ex->getMessage());
        }
    }
}
