<?php class dataset extends baseobject {

    protected static array $fields=array(
        'name'=>validator::REQUIRED,
		'collecteddatetime'=>validator::DATETIME,
        'crystalid'=>array(validator::REQUIRED, validator::INTEGER),
        'diffractionrequestid'=>validator::INTEGER,
        'pdbdepositionid'=>validator::INTEGER,
		'remotedatasetid'=>validator::INTEGER,
		'remotedatasetobject'=>validator::JSON,
        'starrating'=>validator::INTEGER,
        'datalocation'=>validator::REQUIRED,
        'description'=>validator::ANY,
        'beamlineid'=>validator::INTEGER,
        'detectormanufacturer'=>validator::ANY,
        'detectormodel'=>validator::ANY,
        'detectortype'=>validator::ANY,
		'detectormode'=>validator::ANY,
		'bestresolution'=>validator::FLOAT,
        'wavelength'=>validator::FLOAT
    );

    protected static array $aliasedColumns=array(
        'proteinacronym',
        'shipmentid'
    );

    protected static array $helpTexts=array(
        'crystalid'=>'The crystal from which this dataset came',
        'diffractionrequestid'=>'The diffraction request from which this dataset came',
        'starrating'=>'A subjective score of this dataset\'s importance',
        'remotedatasetid'=>'The ID of this dataset in the synchrotron database',
        'datalocation'=>'The path, URL, or DOI where the raw data can be found'
    );

    protected static string $normalSelect='SELECT dataset.*, ROUND(dataset.bestresolution, 2) AS bestresolution,
        ROUND(dataset.wavelength, 4) AS wavelength,
       protein.proteinacronym, protein.name AS proteinname, protein.id AS proteinid, 
       construct.name AS constructname, construct.id AS constructid,
       crystal.name AS crystalname, crystal.dropimageid, pdbdeposition.name AS pdbcode, project.owner AS projectownerid,
            project.name AS projectname, project.isarchived as isarchived,
            diffractionrequest.crystalidatremotefacility, diffractionrequest.crystalurlatremotefacility,
            shipment.id AS shipmentid, shipment.name AS shipmentname, 
            shipment.proposalname as proposalname, shipment.sessionname AS sessionname,
            shipment.idatremotefacility AS shipmentidatremotefacility, shipment.urlatremotefacility AS shipmenturlatremotefacility,
            shipmentdestination.id AS shipmentdestinationid, shipmentdestination.name AS shipmentdestinationname,
            shipmentdestination.shipmenthandler
        FROM dataset JOIN crystal ON dataset.crystalid=crystal.id
            JOIN welldrop ON crystal.welldropid = welldrop.id
            JOIN construct ON welldrop.constructid = construct.id
            JOIN protein ON construct.proteinid = protein.id
            JOIN project on crystal.projectid=project.id 
            LEFT JOIN pdbdeposition ON dataset.pdbdepositionid=pdbdeposition.id 
            LEFT JOIN diffractionrequest ON diffractionrequest.id=dataset.diffractionrequestid
            LEFT JOIN shipment ON shipment.id=diffractionrequest.shipmentid
            LEFT JOIN shipmentdestination on shipment.shipmentdestinationid=shipmentdestination.id
        WHERE 1=1
    ';

    protected static string $adminSelect='SELECT dataset.*, ROUND(dataset.bestresolution, 2) AS bestresolution, 
	        ROUND(dataset.wavelength, 4) AS wavelength,
           crystal.name AS crystalname, crystal.dropimageid, pdbdeposition.name AS pdbcode, project.owner AS projectownerid,
         protein.proteinacronym, protein.name AS proteinname, protein.id AS proteinid, 
            project.name AS projectname, project.isarchived as isarchived,
           construct.name AS constructname, construct.id AS constructid,
            diffractionrequest.crystalidatremotefacility, diffractionrequest.crystalurlatremotefacility,
            shipment.id AS shipmentid, shipment.name AS shipmentname, 
            shipment.proposalname as proposalname, shipment.sessionname AS sessionname,
            shipment.idatremotefacility AS shipmentidatremotefacility, shipment.urlatremotefacility AS shipmenturlatremotefacility,
            shipmentdestination.id AS shipmentdestinationid, shipmentdestination.name AS shipmentdestinationname,
            shipmentdestination.shipmenthandler
        FROM dataset JOIN crystal ON dataset.crystalid=crystal.id
            JOIN welldrop ON crystal.welldropid = welldrop.id
            JOIN construct ON welldrop.constructid = construct.id
            JOIN protein ON construct.proteinid = protein.id
            JOIN project on crystal.projectid=project.id 
            LEFT JOIN pdbdeposition ON dataset.pdbdepositionid=pdbdeposition.id 
            LEFT JOIN diffractionrequest ON diffractionrequest.id=dataset.diffractionrequestid
            LEFT JOIN shipment ON shipment.id=diffractionrequest.shipmentid
            LEFT JOIN shipmentdestination on shipment.shipmentdestinationid=shipmentdestination.id
        WHERE 1=1
    ';

	public static function getById(int $id): ?array {
		$dataset=parent::getById($id);
		$dataset['locations']=static::getdatasetlocations($id);
		return $dataset;
	}

	public static function getAll(array $request=[]): ?array {
		$result=parent::getAll($request);
		return static::addLocations($result);
	}

	public static function getByProperty(string $key, string $value, array $request=[]): ?array {
		$result=parent::getByProperty($key, $value, $request);
		return static::addLocations($result);
	}

	public static function getByProperties(array $keyValuePairs, array $request=[]): ?array {
		$result=parent::getByProperties($keyValuePairs, $request);
		return static::addLocations($result);
	}

	/**
	 * @throws ServerException
	 * @throws BadRequestException
	 */
	private static function addLocations(?array $result): ?array {
		if(!$result){ return  $result; }
		foreach($result['rows'] as &$row){
			$locations=static::getdatasetlocations($row['id']);
			if(empty($locations)){
				$row['datasetlocations']=[];
			} else {
				$row['datasetlocations']=$locations["rows"];
			}
		}
		return $result;
	}

	public static function create(array $request=[]): array {
		shipment::setShipperAdmin();
        $crystalId=$request['crystalid'];
        if(!$crystalId){ throw new BadRequestException("Crystal ID not specified"); }
        $crystal=crystal::getById($crystalId);
        if(!$crystal){ throw new NotFoundException("Crystal not found"); }
		$dataLocation=$request['datalocation'].'';
		$request['datalocation']='-'; //TODO When dropping datalocation column, unset $request['datalocation']
        $request['projectid']=$crystal['projectid'];
        $request['name']='dataset'.microtime(true);
        $ret=parent::create($request);
		$location=datasetlocation::create(['type'=>'Original', 'datasetid'=>$ret['created']['id'], 'datalocation'=>$dataLocation]);
		$ret['datasetlocations']=[ $location['created'] ];
		shipment::revertShipperAdmin();
        return $ret;
    }

    public static function update(int $id, array $request=[]): array {
		shipment::setShipperAdmin();
        $dataset=static::getById($id);
        if(!$dataset){ throw new NotFoundException('Dataset does not exist or you do not have permission to see it'); }
        $ret=parent::update($id, $request);
		shipment::revertShipperAdmin();
        return $ret;
    }

    /**
     * @return bool
	 */
    public static function canCreate(): bool {
        return true; //but checked in create()
    }

	/**
	 * @param int $id
	 * @return bool
	 */
    public static function canUpdate(int $id): bool {
        return true; //but checked in create()
    }

	/**
	 * @param int $id
	 * @param array $request
	 * @return array|null
	 * @throws BadRequestException
	 * @throws ServerException
	 */
    public static function getautoprocessingresults(int $id, array $request=[]): ?array {
        return autoprocessingresult::getByProperty('datasetid', $id, $request);
    }

	/**
	 * @param int $id
	 * @param array $request
	 * @return array|null
	 * @throws BadRequestException
	 * @throws ServerException
	 */
	public static function getdatasetlocations(int $id, array $request=[]): ?array {
		return datasetlocation::getByProperty('datasetid', $id, $request);
	}

	/**
	 * @param int $parentId
	 * @throws BadRequestException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function updateFileNoteAndRecordingProjectIds(int $parentId): void {
        parent::updateFileNoteAndRecordingProjectIds($parentId);
    }

    /**
	 * Sets the "best resolution" field for this dataset, based on its autoprocessing results.
	 * Rationale: 2- and 1-star rated results are worse than unrated, so:
	 * - if there are any 5-star results, set the smallest resolution number
	 * - if not, look for 4-star, then 3-star, then unrated, then 2-star, then 1-star
     * @throws ServerException
     * @throws NotFoundException
     * @throws BadRequestException
     * @throws ForbiddenException
     */
    public static function setBestResolutionFromAutoProcessingResults(int $id): void {
        $results=static::getautoprocessingresults($id);
        if(!$results){ return; }
        $bestResolution=999999;
		foreach ([5,4,3,0,2,1] as $starRating) { //because 1 and 2 star are worse than unrated (0)
			$autoprocessingResults=autoprocessingresult::getByProperties([
				'datasetid'=>$id,
				'starrating'=>$starRating
			]);
			if($autoprocessingResults){
				foreach ($autoprocessingResults['rows'] as $autoprocessingResult){
					$resolution = 1 * $autoprocessingResult['bestresolution'];
					if ($resolution !== 0 && $resolution < $bestResolution) {
						$bestResolution = $resolution;
					}
				}
			}
			if(999999!==$bestResolution){ break; }
		}
        if(999999!=$bestResolution){
            dataset::update($id, ['bestresolution'=>$bestResolution]);
        }
    }

}

