<?php class ImageScoring extends Device {

    const SCORING_SYSTEM_NOT_FOUND = 'Scoring system not found';
    const BAD_COLOR_CODE = 'Bad color code in ImageScoring::createScoringSystem';
    const NO_TWO_SCORES_CAN_HAVE_THE_SAME_NAME = 'No two scores can have the same name';
    const NO_TWO_SCORES_CAN_HAVE_THE_SAME_INDEX = 'No two scores can have the same index';
    const NO_TWO_SCORES_CAN_HAVE_THE_SAME_HOTKEY = 'No two scores can have the same hotkey';

    /**
     * @throws NotFoundException
     * @throws BadRequestException
     * @throws ServerException
     */
    public static function getScoringSystem(array|int|string $params): array {
        Log::debug('In ImageScoring::getScoringSystem');
        if(!is_array($params) && 0!==(int)$params){ $params=['id'=>$params]; }
        if(is_string($params)){ $params=['name'=>$params]; }
        foreach($params as $k=>$v){
            Log::debug('- '.$k.'='.$v);
        }
        self::requireOneOf(['id','name'], $params);
        if(isset($params['id'])){
            Log::debug('Finding scoring system by ID: '.$params['id']);
            $scoringSystem=crystalscoringsystem::getById($params['id']);
        } else {
            Log::debug('Finding scoring system by name: '.$params['name']);
            $scoringSystem=crystalscoringsystem::getByName($params['name']);
        }
        if(!$scoringSystem){ throw new NotFoundException(self::SCORING_SYSTEM_NOT_FOUND); }
        $scores=crystalscoringsystem::getscores($scoringSystem['id']);
        if(!isset($scores['rows'])){
            Log::warning('No scores in this scoring system');
            $scoringSystem['scores']=[];
        } else {
            $scoringSystem['scores']=$scores['rows'];
        }
        $scoringSystem['receiversId']=$scoringSystem['id'];
        Log::debug('Returning from ImageScoring::getScoringSystem');
        return $scoringSystem;
    }

    /**
     * @throws BadRequestException
	 * @throws ServerException
     */
    public static function getScoresWithScoringSystemName(array|string $params): array {
        Log::debug('In ImageScoring::getScoresWithScoringSystemName');
        if(is_string($params)){ $params=['name'=>$params]; }
        static::require('name',$params);
        $system=crystalscoringsystem::getByName($params['name']);
        if(!$system){
            Log::info('No scoring system called '.$params['name']);
            return array();
        }
        $scores=crystalscoringsystem::getscores($system['id']);
        if(isset($scores['rows'])){ $scores=$scores['rows']; }
        Log::debug('Returning from ImageScoring::getScoresWithScoringSystemName');
        return $scores;
    }

    /**
     * @throws NotFoundException
     * @throws ServerException
     * @throws BadRequestException
     * @throws ForbiddenException
     */
    public static function createScoringSystem(array $params): array{
        Log::debug('In ImageScoring::createScoringSystem');
        foreach($params as $k=>$v){
            if(is_array($v)){
                Log::debug('- '.$k.'=array('.count($v).')');
                continue;
            }
            Log::debug('- '.$k.'='.$v);
        }
        static::require(['name','scores'],$params);
        $existing=crystalscoringsystem::getByName($params['name']);
        if($existing){
            Log::info('Found existing scoring system '.$params['name']);
            $scores=crystalscoringsystem::getscores($existing['id']);
            if(empty($scores['rows'])){
                Log::warning('No scores in this scoring system');
                $existing['scores']=[];
            } else {
                $existing['scores']=$scores['rows'];
            }
            return $existing;
        }
        session::becomeAdmin();
        Log::info('No existing scoring system called '.$params['name'].', creating...');
        $scoringSystem=crystalscoringsystem::create(array('name'=>$params['name'], 'iscurrent'=>1))['created'];
        $scores=$params['scores'];
        $names=array_column($scores, 'name');
        $indices=array_column($scores, 'scoreindex');
        $hotkeys=array_column($scores, 'hotkey');
        if(count($names)!==count(array_unique($names))){
            throw new BadRequestException(self::NO_TWO_SCORES_CAN_HAVE_THE_SAME_NAME);
        }
        if(count($indices)!==count(array_unique($indices))){
            throw new BadRequestException(self::NO_TWO_SCORES_CAN_HAVE_THE_SAME_INDEX);
        }
        if(count($hotkeys)!==count(array_unique($hotkeys))){
            throw new BadRequestException(self::NO_TWO_SCORES_CAN_HAVE_THE_SAME_HOTKEY);
        }
        foreach($scores as $score){
            static::require(['name','color','hotkey'],$score);
            static::requireOneOf(['index','scoreindex'],$score);
            if(isset($score['index'])){ $score['scoreindex']=$score['index']; }
            if(is_numeric($score['color']) && strlen(trim($score['color'],'-'))!==6){
                //Colours are stored in RockMaker as negative decimal integers, so we need to convert to hex
                //We may receive them as positive or negative, so abs() them.
                //Then subtract 1, convert to binary, flip the bits.
                Log::debug('Score "'.$score['name'].'" has color "'.$score['color'].'", converting to hex...');
                $decimal=abs(1*$score['color'])-1;
                $full=hexdec('ffffff');
                $score['color']=substr("000000" . dechex($full-$decimal), -6);

                Log::debug('Score "'.$score['name'].'" has color "'.$score['color'].'"');
            } else {
                Log::debug('Score "'.$score['name'].'" has non-numeric color "'.$score['color'].'"');
            }
            if(!preg_match('/^[0-9a-fA-F]{6}$/', $score['color'])){
                throw new BadRequestException(self::BAD_COLOR_CODE.' '.$score['color']);
            }
            $score['color']=strtolower($score['color']);
            $score['label']=$score['name'];
            $score['name']=$scoringSystem['name'].'_'.$score['name'];
            $score['crystalscoringsystemid']=$scoringSystem['id'];
            crystalscore::create($score);
        }
        Log::info('...created.');
        $scoringSystem=static::getScoringSystem($scoringSystem['name']);
        Log::debug('Returning from ImageScoring::createScoringSystem');
        return $scoringSystem;
    }

}