<?php
namespace EmotionHero\Api;

use EmotionHero\Application as EH;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Silex\Api\ControllerProviderInterface;
use EmotionHero\Models;

class ScoreControllerProvider implements ControllerProviderInterface
{
    /** @var EH */
    protected $_eh;

    public function __construct()
    {
        $this->_eh = EH::getInstance();
    }

    public function connect(Application $app)
    {
        // creates a new controller based on the default route
        $controllers = $app['controllers_factory'];

        $controllers->get('/', function (Application $app) {
            return "OK";
        });

        $controllers->get('/levels', function (Application $app) {
            $levels = $this->_eh->getEm()->getRepository(Models\Level::class)->findAll();
            return $app['serializer']->serialize($levels, 'json');
        });

        $controllers->get('/emotions', function (Application $app) {
            $levels = $this->_eh->getEm()->getRepository(Models\Emotion::class)->findAll();
            return $app['serializer']->serialize($levels, 'json');
        });

        $controllers->get('/me', function (Application $app) {
            $token = $app['security.token_storage']->getToken();
            $user = $token->getUser();
            return $app['serializer']->serialize($user, 'json');
        });

        $controllers->get('/games/{gameId}', function(Request $request, Application $app) {

            $game = $request->attributes->get('game');

            if(empty($game)) {
                return new CustomJsonResponse(['message' => 'Game not found'], function($data) use ($app){return $app['serializer']->serialize($data, 'json');}, 404);
            }

            return new CustomJsonResponse($game, $app['serializer.json'], 200);
        })
        ->bind('game')
        ->convert('game', function($game, Request $request) use ($app){ return $app['entity.manager']->getRepository(Models\Game::class)->find($request->attributes->get('gameId'));});

        /**
         * Expects a full game + hits in the request
         */
        $controllers->post('/games', function (Request $request, Application $app) {

            $data = json_decode($request->getContent(), true);
            $token = $app['security.token_storage']->getToken();
            $user = $token->getUser();

            if($data == null || !isSet($data['lvl_id'])) {
                return new JsonResponse(['success' => false, 'message' => "Invalid JSON body"], 400);
            }


            $level = $this->_eh->getEm()->getRepository(Models\Level::class)->find($data['lvl_id']);
            if(empty($level)) {
                return new JsonResponse(['success' => false, 'message' => "Invalid level" , "lvl_id" => (int) $data['lvl_id']], 400);
            }

            $game = new Models\Game();
            $game->setUser($user);
            $game->setLevel($level);
            $game->setLostFaceTime((float) $data['lost_face_time']);
            $game->setOriginalGameAt(new \DateTime($data['time']));

            $map_hits = [];

            foreach($data['hits'] as $data_hit) {
                $hit = new Models\Hit();
                $target = $this->_eh->getEm()->getRepository(Models\Target::class)->findOneBy(['position' => $data_hit['target_index'], 'level' => $level]);
                if(empty($target)){
                    return new JsonResponse(['success' => false, 'message' => "Invalid target for hit"], 400);
                }

                $hit->setTarget($target);

                $hit->setScore($data_hit['score']);

                // attributes
                $hit->getAttributes()->setGlasses($data_hit['glasses']);
                foreach($hit->getAttributes()::$ATTRIBUTES as $attribute) {
                    $hit->getAttributes()->setAttribute($attribute, $data_hit[$attribute]);
                }
                // emotions
                foreach($hit->getEmotions()::$EMOTIONS as $emotion) {
                    $hit->getEmotions()->setEmotion($emotion, $data_hit['emotions'][$emotion]);
                }

                //expressions
                foreach($hit->getExpressions()::$EXPRESSIONS as $expression) {
                    $hit->getExpressions()->setExpression($expression, $data_hit['expressions'][$expression]);
                }

                //points
                foreach(range(0, 33) as $i) {
                    $hit->getPoints()->setPoint($i, $data_hit['points']["$i"]['x'], $data_hit['points']["$i"]['y']);
                }


                // set game appends hit to game
                $hit->setGame($game);

                $map_hits[$data_hit['id']] = $hit;
            }

            $this->_eh->getEm()->persist($game);
            $this->_eh->getEm()->flush();

            $hits_array = [];
            foreach($map_hits as $hit_player_id => $hit) {
                $hits_array[$hit_player_id] = $hit->getId();
            }

            $achievement_ids = [];
            foreach($game->getAchievements() as $achievement) {
                $achievement_ids[] = $achievement->getId();
            }

            $achievement_ids = [2]; // override for test purpose
            
            /* @var $userRepo Models\UserRepository */
            $userRepo = $this->_eh->getEm()->getRepository(Models\User::class);
            $userRepo->updateTotalForUser($user);

            return new JsonResponse(['success' => true, 'id' => $game->getId(), 'hits' => $hits_array, 'achievements' => $achievement_ids], 200);
        });
        
        
        /**
         * Expects a full game + hits in the request
         * add images as post multipart data.
         * filename: hit_id-imagetype.jpg (ie. 12341-brows.jpg)
         */
        $controllers->post('/images', function (Request $request, Application $app) {
            $token = $app['security.token_storage']->getToken();
            /* @var $user Models\User */
            $user = $token->getUser();
            if($request->files->count() < 1) {
                return new JsonResponse(['success' => false, 'message' => "no files given"], 400);
            }
            
            $valid_parts = Models\Hit::$FEATURES;
            foreach($valid_parts as $part) {
                $path = $app['file.path'] . "/hits/$part";
                if(!is_dir($path)) {
                    mkdir($path, 0777, true);
                }
            }
            
            foreach($request->files->all() as $id => $file) {
                /* @var $file \Symfony\Component\HttpFoundation\File\UploadedFile */
                $filename = $file->getClientOriginalName();
                $ext = $file->getClientOriginalExtension();
                if($ext != 'jpg' || $file->getMimeType() != 'image/jpeg') {
                    return new JsonResponse(['success' => false, 'message' => "Image not image/jpeg but " . $file->getMimeType() . " (".$ext.")" ], 400);
                }
                
               
                // name without .jpg
                $id_string = substr($filename, 0, -4);
                
                list($hit_id, $part) = \explode('-', $id_string);
                
                if(!in_array($part, $valid_parts)) {
                    return new JsonResponse(['success' => false, 'message' => "unknown face part '$part'"], 400);
                }
                
                /* @var $em \Doctrine\ORM\EntityManager */
                $em = $app['entity.manager'];
                /* @var $hitRepo Models\HitRepository */
                $hitRepo = $em->getRepository(Models\Hit::class);
                
                /* @var $hit Models\Hit */
                $hit = $hitRepo->find($hit_id);
                if(empty($hit) || $hit->getGame()->getUser()->getId() != $user->getId()) {
                    return new JsonResponse(['success' => false, 'message' => "Hit not found."], 400);
                }
                $targetDirname = $hit->getFeatureDirname($part);

                if($targetDirname == null) {
                    return new JsonResponse(['success' => false, 'message' => "Something went wrong. Invalid part?", "part" => (string) $part], 400);
                }
                
                $file->move($targetDirname, $hit->getId().'.jpg');
                $hit->setHasImage(true);
                $em->persist($hit);
            }
            
            $em->flush();
            
            return new JsonResponse(['success' => true, 'message' => "images saved."], 200);
        });

        $controllers->get('/hits/{hitId}', function(Request $request, Application $app) {

            $hit = $request->attributes->get('hit');

            if(empty($hit)) {
                return new JsonResponse(['message' => 'Hit not found'], 404);
            }

            return new CustomJsonResponse($hit, $app['serializer.json'], 200);
        })
        ->bind('hit')
        ->convert('hit', function($hit, Request $request) use ($app){ return $app['entity.manager']->getRepository(Models\Hit::class)->find($request->attributes->get('hitId'));});

        /**
         * Convert the hit to a mhpose file for use with MakeHuman
         */
        $controllers->get('/hits/{hitId}.mhpose', function(Request $request, Application $app) {

            $hit = $request->attributes->get('hit');

            if(empty($hit)) {
                return new JsonResponse(['message' => 'Hit not found'], 404);
            }

            $conversions = [
                'roll' => [],
                'pitch' => [],
                'yaw' => [],
                'inter_ocular_distance' => [],
                // 'mouth_open' => ["JawDropStretched"],
                'mouth_open' => ["UpperLipUp","lowerLipDown"], 
                'lip_press' => ["lowerLipUp"],
                'brow_raise' => ["RightOuterBrowUp","LeftOuterBrowUp"],
                'nose_wrinkler' => ["NasolabialDeepener"],
                'lip_depressor' => ["MouthRightPullDown","MouthLeftPullDown"],
                'brow_furrow' => ["NoseWrinkler"],
                'attention' => [],
                // 'smile' => ["MouthRightPullUp","MouthLeftPullUp", "UpperLipUp","lowerLipDown"],
                'smile' => ["UpperLipStretched"],
                'inner_brow_raiser' => ["RightInnerBrowUp","LeftInnerBrowUp"],
                'chin_raiser' => ["ChinForward"],
                'smirk' => ["MouthLeftPullUp"],
                'lip_suck' => ["UpperLipBackward","lowerLipBackward"],
                'upper_lip_raiser' => ["UpperLipUp"],
                // 'lip_pucker' => ["LipsKiss"],
                'lip_pucker' => ["UpperLipForward","lowerLipForward"],
                'eye_closure' => ["RightUpperLidClosed","LeftUpperLidClosed"],
                'engagement' => [],
                'valence' => [],
                'dimpler' => ["MouthRightPullSide","MouthLeftPullSide"],
                'cheek_raise' => ["LeftCheekUp","RightCheekUp"],
                'eye_widen' => ["RightUpperLidOpen","LeftUpperLidOpen"],
                'jaw_drop' => ["JawDrop"],
                'lid_tighten' => ["LeftLowerLidUp","RightLowerLidUp"],
                'lip_stretch' => ["MouthRightPullSide","MouthLeftPullSide"],
                // <!-- platysma?? -->
            ];

            $exprs = "";

            foreach($hit->getExpressions()::$EXPRESSIONS as $expr) {
                $exprs .= " $expr {$hit->getExpressions()->getExpressionScore($expr)}%.";
            }

            $data = [
                "license" => "AGPL3", 
                "name" => "hit_{$hit->getId()}", 
                "copyright" => "None", 
                "tags" => [
                    "no tag", 
                    "expression"
                ], 
                "author" => "Ruben van de Ven & player {$hit->getGame()->getUser()->getId()}", 
                "unit_poses" => [],
                "homepage" => "http://emotionhero.com", 
                "description" => "Hit {$hit->getId()} in the Emotion Hero game by player {$hit->getGame()->getUser()->getId()}. Points awarded for hit: {$hit->getScore()}. $exprs"
            ];

            foreach($conversions as $from => $to) {
                $value = $hit->getExpressions()->getExpressionScore($from) / 100;
                foreach($to as $t) {
                    $data['unit_poses'][$t] = isSet($data['unit_poses'][$t]) ? min(1,$data['unit_poses'][$t]+$value) : $value;
                    // $data['description'] .= " $from => $t";
                }
            }

            return new CustomJsonResponse($data, $app['serializer.json'], 200);
        })
        ->bind('hit')
        ->convert('hit', function($hit, Request $request) use ($app){ return $app['entity.manager']->getRepository(Models\Hit::class)->find($request->attributes->get('hitId'));});


        return $controllers;
    }
}