<?php class RigakuImport extends Device {

    const USERNAME='rigakuimporter';
    const USERFULLNAME='Rigaku Importer';

    const SCORING_SYSTEM_NAME='Rigaku';

    const BARCODE_PATTERN='LLNNNNNN'; // Two letters, six numbers, e.g., UR000123

    const RIGAKU_SCORES= array(
        array('color'=>'ffffff', 'index'=>'0', 'hotkey'=>'0', 'name'=>'Clear'),
        array('color'=>'aeaeae', 'index'=>'1', 'hotkey'=>'9', 'name'=>'Matter'),
        array('color'=>'c800ff', 'index'=>'2', 'hotkey'=>'8', 'name'=>'Phase separation'),
        array('color'=>'ff0041', 'index'=>'3', 'hotkey'=>'7', 'name'=>'Heavy precipitation'),
        array('color'=>'ff8200', 'index'=>'4', 'hotkey'=>'6', 'name'=>'Precipitation'),
        array('color'=>'009b00', 'index'=>'5', 'hotkey'=>'5', 'name'=>'Spherulites'),
        array('color'=>'00ff1b', 'index'=>'6', 'hotkey'=>'4', 'name'=>'Clusters'),
        array('color'=>'00ffeb', 'index'=>'7', 'hotkey'=>'3', 'name'=>'Crystalline'),
        array('color'=>'94c6ff', 'index'=>'8', 'hotkey'=>'2', 'name'=>'Micro-crystals'),
        array('color'=>'0064fa', 'index'=>'9', 'hotkey'=>'1', 'name'=>'Crystals')
    );

	/**
	 * @param array $params
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function createScores(array $params): array {
        Log::debug('In RigakuImport::createScores');
            foreach($params as $k=>$v){
                if(is_array($v)){
                    Log::debug('- '.$k.'=Array');
                } else {
                    Log::debug('- '.$k.'='.$v);
                }
            }
        if(!isset($params['name'])){
            Log::info('Setting default scoring system name '.static::SCORING_SYSTEM_NAME);
            $params['name']=static::SCORING_SYSTEM_NAME;
        }
        if(!isset($params['scores'])){
            Log::info('Setting default Rigaku scores');
            $params['scores']=static::RIGAKU_SCORES;
        }
        $scoringSystem=ImageScoring::createScoringSystem($params);
        Log::debug('Returning from RigakuImport::createScores');
        return $scoringSystem;
    }

    /**
     * @throws ServerException
	 * @throws BadRequestException
     */
    public static function getScores(): array {
        Log::debug('In RigakuImport::getScores');
        $scores=ImageScoring::getScoresWithScoringSystemName(static::SCORING_SYSTEM_NAME);
        Log::debug('Returning from RigakuImport::getScores');
        return $scores;
    }

	/**
	 * @param array $params
	 * @return array|null
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function getImagingProfiles(array $params): ?array {
        Log::debug('In getImagingProfiles');
        $profiles=static::createImagingProfiles($params);
        Log::debug('Returning from getImagingProfiles');
        return $profiles;
    }

	/**
	 * @param array $params
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function createImagingProfiles(array $params): array {
        Log::debug('In createImagingProfiles');
        static::require('profiles',$params);
        session::becomeAdmin();
        $sharedProjectId=baseproject::getSharedProjectId();
        foreach($params['profiles'] as $key=>&$rigakuProfile){
            static::require(['label','name','light'], $rigakuProfile);
            $label=$rigakuProfile['label'];
            $profileName=$rigakuProfile['name'];
            $profileVersionName=$profileName.'_v1';
            if($rigakuProfile['label']!==$key){
                throw new BadRequestException("Imaging profiles: Key $key does not match profile label/shortcode $label");
            }
            $iceBearProfile=imagingparameters::getByProperties([
                'name'=>$profileName,
                'manufacturer'=>'Rigaku'
            ]);

            if(!$iceBearProfile){
                $iceBearProfile=imagingparameters::create([
                    'name'=>$profileName,
                    'manufacturer'=>'Rigaku',
                    'manufacturerdatabaseid'=>0,
                    'projectid'=>$sharedProjectId
                ])['created'];
            } else {
                $iceBearProfile=$iceBearProfile['rows'][0];
            }

            $iceBearProfileVersion=imagingparametersversion::getByProperties([
                'name'=>$profileVersionName,
                'imagingparametersid'=>$iceBearProfile['id']
            ]);

            if(!$iceBearProfileVersion){
                $iceBearProfileVersion=imagingparametersversion::create([
                    'name'=>$profileVersionName,
                    'manufacturerdatabaseid'=>0,
                    'imagingparametersid'=>$iceBearProfile['id'],
                    'projectid'=>$sharedProjectId
                ])['created'];
            } else {
                $iceBearProfileVersion=$iceBearProfileVersion['rows'][0];
            }
            imagingparameters::update($iceBearProfile['id'], ['currentversionid'=>$iceBearProfileVersion['id']]);

            $rigakuProfile['profile']=$iceBearProfile;
            $rigakuProfile['profileVersion']=$iceBearProfileVersion;
        }
        Log::debug('Returning from createImagingProfiles');
        return $params;
    }

    /**
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws NotFoundException
     * @throws ServerException
     */
    public static function createUser(array $user): array{
        Log::debug('In RigakuImport::createUser');
        foreach($user as $k=>$v){
            Log::debug('- '.$k.'='.$v);
        }
        static::require('username',$user);
        $username=$user['username'];
        $usernameSanitized=preg_replace('/[^A-Za-z0-9_.-]/','',$username);
        if(!$username!==$usernameSanitized){
            Log::warning('Username contains illegal characters. Sanitized.');
            Log::warning("Supplied: $username");
            Log::warning("Sanitized: $usernameSanitized");
            $user['username']=$usernameSanitized;
            $username=$usernameSanitized;
        }
        if(!isset($user['fullName'])){
            Log::info('User fullName not set, setting to username: '.$username);
            $user['fullName']=$username;
        }
        $user=UserManagement::createUser($user);
        Log::debug('Returning from RigakuImport::createUser');
        return $user;
    }

	/**
	 * @param array $user
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function getUser(array $user): array {
        return static::createUser($user);
    }

    /**
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws NotFoundException
     * @throws ServerException
     */
    public static function createImager(array $params): array {
        Log::debug('In RigakuImport::createImager');
        static::require(['name','nominalTemperature'], $params);
        $params['manufacturerName']='Rigaku';
        $imager=ImagingManagement::createImager($params);
        Log::debug('Returning from RigakuImport::createImager');
        return $imager;
    }

	/**
	 * @param array $plateType
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function getPlateType(array $plateType): array {
        return static::createPlateType($plateType);
    }

	/**
	 * @param array $plateType
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function createPlateType(array $plateType): array {
        return PlateImport::createPlateType($plateType);
    }

	/**
	 * @param array $params
	 * @return array
	 * @throws BadRequestException
	 * @throws ServerException
	 */
    public static function getScreenByName(array $params): array {
        return PlateImport::getScreenByName($params);
    }

    /**
     * @throws ForbiddenException
     * @throws BadRequestException
     * @throws ServerException
     * @throws NotFoundException
     */
    public static function promoteScreenToStandard(array $params): array {
        static::require('screenId', $params);
        session::becomeAdmin();
        $screenId=$params['screenId'];
        $screen=screen::getById($screenId);
        if(!$screen){ throw new NotFoundException('No screen with ID '.$screenId); }
        screen::update($screenId, [ 'isstandard'=>1 ]);
        return screen::getById($screenId);
    }

	/**
	 * @param array $params
	 * @return array|mixed
	 * @throws BadRequestException
	 * @throws ServerException
	 */
    public static function getPlateByBarcode(array $params): ?array {
        Log::debug('In getPlateByBarcode');
        static::require('barcode',$params);
        $barcode=$params['barcode'];
        Log::debug("Plate barcode is $barcode");
        session::becomeAdmin();
        $plate=plate::getByName($barcode);
        session::revokeAdmin();
        Log::debug('Returning from getPlateByBarcode');
        return $plate;
    }

	/**
	 * @param array $params
	 * @return mixed|null
	 * @throws BadRequestException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function getInspectionsForPlate(array $params): ?array {
        static::require('barcode', $params);
        $barcode=$params['barcode'];
        session::becomeAdmin();
        $plate=plate::getByName($params['barcode']);
        if(!$plate){ throw new NotFoundException("No plate with barcode $barcode"); }
        $inspections=plate::getimagingsessions($plate['id']);
        if(!$inspections){ return null; }
        Log::debug('Populating numImages');
        foreach ($inspections['rows'] as &$inspection){
            $images=imagingsession::getdropimages($inspection['id']);
            if(!$images || !isset($images['total'])){
                $inspection['numImages']=0;
            } else {
                $inspection['numImages']=$images['total'];
            }
        }
        Log::debug('Returning from getInspectionsForPlate');
        return $inspections['rows'];
    }

    /**
     * @param $params
     * @return mixed
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws NotFoundException
     * @throws ServerException
     */
    public static function createInspection(array $params): array {
        Log::debug('In createInspection');
        static::require('plateBarcode',$params);
        static::require('imagedDateTime',$params);
        static::require('profileName',$params);
        static::require('lightType',$params);
        static::require('imagerName',$params);
        static::require('temperature',$params);
        foreach ($params as $key=>$value){
            $$key=$value;
        }
        session::becomeAdmin();

        /** @var string $lightType */
        /** @var string $temperature */
        /** @var string $imagerName */
        /** @var string $imagedDateTime */
        /** @var string $plateBarcode */
        /** @var string $profileName */

        if(!is_numeric($temperature)){
            throw new BadRequestException('Temperature must be numeric');
        }

        $imager=imager::getByName($imagerName);
        if(!$imager){
            throw new NotFoundException("No imager called $imagerName");
        }
        $imagerId=$imager['id'];

        if(!preg_match('/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/', $imagedDateTime)){
            throw new BadRequestException('imagedDateTime must be a 24-hour GMT datetime in YYYY-MM-DD hh:mm:ss format');
        }

        $plate=plate::getByName($plateBarcode);
        if(!$plate){
            throw new NotFoundException('No plate with barcode '.$plateBarcode);
        }
        $plateId=$plate['id'];

        $profile=imagingparameters::getByProperties([
            'name'=>$profileName,
            'manufacturer'=>'Rigaku'
        ]);
        if(!$profile || !isset($profile['rows'])){
            throw new NotFoundException("No imaging profile called $profileName");
        } else if(1*($profile['total'])!==1){
            throw new ServerException($profile['total']. " Rigaku imaging profiles found called $profileName. Should be only 1.");
        }
        $profile=$profile['rows'][0];
        $profileId=$profile['id'];
        $profileVersionId=$profile['currentversionid'];
        if(is_null($profileVersionId)){
            throw new ServerException("imagingparameters $profileId ($profileName) has null currentversionid");
        }

        $inspectionParameters=[
            'plateid'=>$plateId,
            'imageddatetime'=>$imagedDateTime,
            'imagingparametersversionid'=>$profileVersionId
        ];
        Log::debug('Looking for existing imagingsession with parameters:');
        foreach ($inspectionParameters as $k=>$v){
            Log::debug("$k: $v");
        }
        $inspectionName=$plateBarcode.'_'.str_replace(' ','_',$imagedDateTime).'_profile'.$profileId;
        $inspection=imagingsession::getByName($inspectionName);
        if($inspection){
            Log::debug('Imaging session exists, returning it');
        } else {
            Log::info('Imaging session does not exist, creating it');
            $inspectionParameters['name']=$inspectionName;
            $inspectionParameters['manufacturerdatabaseid']=0;
            $inspectionParameters['imagerid']=$imagerId;
            $inspectionParameters['lighttype']=$lightType;
            $inspectionParameters['temperature']=$temperature;
            $inspection=imagingsession::create($inspectionParameters)['created'];
        }

        Log::debug('Returning from createInspection');
        return $inspection;
    }

	/**
	 * @param array $plate
	 * @return mixed
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws NotModifiedException
	 * @throws ServerException
	 */
    public static function createPlate(array $plate): array {
        $plate['scoringSystemName']=self::SCORING_SYSTEM_NAME;
        return PlateImport::createPlate($plate);
    }

	/**
	 * @param array $params
	 * @return array
	 * @throws BadRequestException
	 * @throws ServerException
	 */
    public static function createImages(array $params): array {
        Log::debug('In createImages');
        return ImagingManagement::createImages($params);
    }

	/**
	 * @param array $params
	 * @return array|null
	 * @throws BadRequestException
	 * @throws ServerException
	 */
    public static function getProject(array $params): ?array {
        static::require('name', $params);
        return PlateImport::getProjectByName($params);
    }

	/**
	 * @param array $params
	 * @return mixed
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws ServerException
	 */
    public static function createProject(array $params): array {
        return PlateImport::createProject($params);
    }

	/**
	 * @param array $params
	 * @return array[]
	 * @throws BadRequestException
	 * @throws ServerException
	 */
    public static function updateDiskSpaces(array $params): array {
        $updatedDrives=Device::updateDiskSpaces($params);
        $content=array(
            'total'=>count($updatedDrives),
            'rows'=>$updatedDrives
        );
        caching::setContent(diskusage::FILESYSTEM_CACHE_RIGAKU, $content, '+12 hours');
        return $content;
    }

	/**
	 * @param array $params
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws NotModifiedException
	 * @throws ServerException
	 */
    public static function createScreen(array $params): array {
        if(!isset($params['projectId'])){
            session::becomeAdmin();
            $params['projectId']=baseproject::getSharedProjectId();
        }
        $screen=PlateImport::createScreen($params);
        if(empty($screen)){
            throw new Exception('createScreen returned empty');
        }
        return $screen;
    }

    /**
     * @throws NotFoundException
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public static function getPlateForReimport(): ?array {
        return PlateImport::getPlateForReimport(['imagerType'=>'Rigaku']);
    }

}