/**
 * @brief Function called to describe the plugin controls and features.
 * @param[in, out]   desc       Effect descriptor
 * @param[in]        context    Application context
 */
void AVReaderPluginFactory::describeInContext(OFX::ImageEffectDescriptor& desc, OFX::EContext context)
{
    // Create the mandated output clip
    OFX::ClipDescriptor* dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
    dstClip->addSupportedComponent(OFX::ePixelComponentRGBA);
    dstClip->addSupportedComponent(OFX::ePixelComponentRGB);
    dstClip->addSupportedComponent(OFX::ePixelComponentAlpha);
    dstClip->setSupportsTiles(kSupportTiles);

    describeReaderParamsInContext(desc, context);

    // Groups
    OFX::GroupParamDescriptor* formatGroup = desc.defineGroupParam(kParamFormatGroup);
    OFX::GroupParamDescriptor* videoGroup = desc.defineGroupParam(kParamVideoGroup);
    OFX::GroupParamDescriptor* metaGroup = desc.defineGroupParam(kParamMetaGroup);

    formatGroup->setLabel("Format");
    videoGroup->setLabel("Video");
    metaGroup->setLabel("Metadata");

    formatGroup->setAsTab();
    videoGroup->setAsTab();
    metaGroup->setAsTab();

    /// FORMAT PARAMETERS
    avtranscoder::FormatContext formatContext(AV_OPT_FLAG_DECODING_PARAM);
    avtranscoder::OptionArray formatOptions = formatContext.getOptions();
    common::addOptionsToGroup(desc, formatGroup, formatOptions, common::kPrefixFormat);

    OFX::GroupParamDescriptor* formatDetailedGroup = desc.defineGroupParam(kParamFormatDetailedGroup);
    formatDetailedGroup->setLabel("Detailed");
    formatDetailedGroup->setAsTab();
    formatDetailedGroup->setParent(formatGroup);

    avtranscoder::OptionArrayMap formatDetailedGroupOptions = avtranscoder::getAvailableOptionsPerOutputFormat();
    common::addOptionsToGroup(desc, formatDetailedGroup, formatDetailedGroupOptions, common::kPrefixFormat);

    /// VIDEO PARAMETERS
    AVCodecContext* videoContext = avcodec_alloc_context3(NULL);
    avtranscoder::OptionArray videoOptions;
    avtranscoder::loadOptions(videoOptions, videoContext, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM);
    common::addOptionsToGroup(desc, videoGroup, videoOptions, common::kPrefixVideo);
    av_free(videoContext);

    OFX::BooleanParamDescriptor* useCustomSAR = desc.defineBooleanParam(kParamUseCustomSAR);
    useCustomSAR->setLabel("Override SAR");
    useCustomSAR->setDefault(false);
    useCustomSAR->setHint("Override the file SAR (Storage Aspect Ratio) with a custom SAR value.");
    useCustomSAR->setParent(videoGroup);

    OFX::DoubleParamDescriptor* customSAR = desc.defineDoubleParam(kParamCustomSAR);
    customSAR->setLabel("Custom SAR");
    customSAR->setDefault(1.0);
    customSAR->setDisplayRange(0., 3.);
    customSAR->setRange(0., 10.);
    customSAR->setHint("Choose a custom value to override the file SAR (Storage Aspect Ratio). Maximum value: 10.");
    customSAR->setParent(videoGroup);

    OFX::IntParamDescriptor* streamIndex = desc.defineIntParam(kParamVideoStreamIndex);
    streamIndex->setLabel(kParamVideoStreamIndexLabel);
    streamIndex->setDefault(0);
    streamIndex->setDisplayRange(0., 16.);
    streamIndex->setRange(0., 100.);
    streamIndex->setHint("Choose a custom value to decode the video stream you want. Maximum value: 100.");
    streamIndex->setParent(videoGroup);

    OFX::GroupParamDescriptor* videoDetailedGroup = desc.defineGroupParam(kParamVideoDetailedGroup);
    videoDetailedGroup->setLabel("Detailed");
    videoDetailedGroup->setAsTab();
    videoDetailedGroup->setParent(videoGroup);

    avtranscoder::OptionArrayMap videoDetailedGroupOptions = avtranscoder::getAvailableOptionsPerVideoCodec();
    common::addOptionsToGroup(desc, videoDetailedGroup, videoDetailedGroupOptions, common::kPrefixVideo);

    /// METADATA PARAMETERS
    AVCodecContext* metaDataContext = avcodec_alloc_context3(NULL);
    avtranscoder::OptionArray metaDataOptions;
    avtranscoder::loadOptions(metaDataOptions, metaDataContext, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_METADATA);
    common::addOptionsToGroup(desc, metaGroup, metaDataOptions, common::kPrefixMetaData);
    av_free(metaDataContext);

    OFX::StringParamDescriptor* metaDataWrapper = desc.defineStringParam(kParamMetaDataWrapper);
    metaDataWrapper->setLabel(kParamMetaDataWrapperLabel);
    metaDataWrapper->setEnabled(false);
    metaDataWrapper->setStringType(OFX::eStringTypeMultiLine);
    metaDataWrapper->setParent(metaGroup);

    OFX::StringParamDescriptor* metaDataVideo = desc.defineStringParam(kParamMetaDataVideo);
    metaDataVideo->setLabel(kParamMetaDataVideoLabel);
    metaDataVideo->setEnabled(false);
    metaDataVideo->setStringType(OFX::eStringTypeMultiLine);
    metaDataVideo->setParent(metaGroup);

    OFX::StringParamDescriptor* metaDataAudio = desc.defineStringParam(kParamMetaDataAudio);
    metaDataAudio->setLabel(kParamMetaDataAudioLabel);
    metaDataAudio->setEnabled(false);
    metaDataAudio->setStringType(OFX::eStringTypeMultiLine);
    metaDataAudio->setParent(metaGroup);

    OFX::StringParamDescriptor* metaDataData = desc.defineStringParam(kParamMetaDataData);
    metaDataData->setLabel(kParamMetaDataDataLabel);
    metaDataData->setEnabled(false);
    metaDataData->setStringType(OFX::eStringTypeMultiLine);
    metaDataData->setParent(metaGroup);

    OFX::StringParamDescriptor* metaDataSubtitle = desc.defineStringParam(kParamMetaDataSubtitle);
    metaDataSubtitle->setLabel(kParamMetaDataSubtitleLabel);
    metaDataSubtitle->setEnabled(false);
    metaDataSubtitle->setStringType(OFX::eStringTypeMultiLine);
    metaDataSubtitle->setParent(metaGroup);

    OFX::StringParamDescriptor* metaDataAttachement = desc.defineStringParam(kParamMetaDataAttachement);
    metaDataAttachement->setLabel(kParamMetaDataAttachementLabel);
    metaDataAttachement->setEnabled(false);
    metaDataAttachement->setStringType(OFX::eStringTypeMultiLine);
    metaDataAttachement->setParent(metaGroup);

    OFX::StringParamDescriptor* metaDataUnknown = desc.defineStringParam(kParamMetaDataUnknown);
    metaDataUnknown->setLabel(kParamMetaDataUnknownLabel);
    metaDataUnknown->setEnabled(false);
    metaDataUnknown->setStringType(OFX::eStringTypeMultiLine);
    metaDataUnknown->setParent(metaGroup);

    /// VERBOSE
    OFX::BooleanParamDescriptor* useVerbose = desc.defineBooleanParam(kParamVerbose);
    useVerbose->setLabel("Set to verbose");
    useVerbose->setDefault(false);
    useVerbose->setHint("Set plugin to verbose to get debug informations.");
}
void LensCalibrationPluginFactory::describeInContext(OFX::ImageEffectDescriptor& desc, OFX::ContextEnum context)
{
  //Input Clip
  OFX::ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
  srcClip->addSupportedComponent(OFX::ePixelComponentRGBA);
  srcClip->setTemporalClipAccess(false);
  srcClip->setSupportsTiles(false);
  srcClip->setIsMask(false);
  srcClip->setOptional(false);
  
  //Output clip
  OFX::ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
  dstClip->addSupportedComponent(OFX::ePixelComponentRGBA);
  dstClip->setSupportsTiles(false);
  
  //Calibration Group
  {
    OFX::GroupParamDescriptor *groupCalibration = desc.defineGroupParam(kParamGroupCalibration);
    groupCalibration->setLabel("Calibration");
    groupCalibration->setAsTab();

    {
      OFX::Int2DParamDescriptor *param = desc.defineInt2DParam(kParamImageSize);
      param->setLabel("Image Size");
      param->setHint("Input image size used to calibrate the optics. Obviously, all images should have the same size.");
      param->setDefault(0, 0);
      param->setDisplayRange(0, 0, 10000, 10000);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
      param->setEnabled(false); // should not be edited by the user
    }

    {
      OFX::BooleanParamDescriptor *param = desc.defineBooleanParam(kParamInputImageIsGray);
      param->setLabel("Input image is gray");
      param->setHint("Input image is gray");
      param->setParent(*groupCalibration);
    }
    
    {
      OFX::ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamPatternType);
      param->setLabel("Pattern Type");
      param->setHint("Type of pattern to detect");
      param->appendOptions(kStringParamPatternType);
      param->setDefault(eParamPatternTypeChessboard);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }

    {
      OFX::Int2DParamDescriptor *param = desc.defineInt2DParam(kParamPatternSize);
      param->setLabel("Pattern Size");
      param->setHint("Number of inner corners per one of board dimension Width Height");
      param->setDefault(10, 7);
      param->setRange(2, 2, kOfxFlagInfiniteMax, kOfxFlagInfiniteMax);
      param->setDisplayRange(2, 2, 15, 15);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }
    
    {
      OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamSquareSize);
      param->setLabel("Square Size");
      param->setHint("Define the size of the grid's square cells (mm)");
      param->setDisplayRange(0, 100);
      param->setDefault(1);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
      param->setLayoutHint(OFX::eLayoutHintDivider);
    }

    {
      OFX::IntParamDescriptor *param = desc.defineIntParam(kParamNbRadialCoef);
      param->setLabel("Nb Radial Coef");
      param->setHint("Number of radial coefficient.");
      param->setRange(0, 6);
      param->setDisplayRange(0, 6);
      param->setDefault(3);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }

    {
      OFX::IntParamDescriptor *param = desc.defineIntParam(kParamMaxFrames);
      param->setLabel("Max Frames");
      param->setHint("Maximal number of frames to extract from the video file.");
      param->setRange(0, kOfxFlagInfiniteMax);
      param->setDisplayRange(0, 1000);
      param->setDefault(0);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }

    {
      OFX::IntParamDescriptor *param = desc.defineIntParam(kParamMaxCalibFrames);
      param->setLabel("Max Calibration Frames");
      param->setHint("Maximal number of frames to use to calibrate from the selected frames.");
      param->setRange(0, kOfxFlagInfiniteMax);
      param->setDisplayRange(0, 1000);
      param->setDefault(100);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }

    {
      OFX::IntParamDescriptor *param = desc.defineIntParam(kParamCalibGridSize);
      param->setLabel("Max Calibration Grid Size");
      param->setHint("Define the number of cells per edge.");
      param->setRange(0, kOfxFlagInfiniteMax);
      param->setDisplayRange(0, 100);
      param->setDefault(10);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }

    {
      OFX::IntParamDescriptor *param = desc.defineIntParam(kParamMinInputFrames);
      param->setLabel("Min Input Frames");
      param->setHint("Minimal number of frames to limit the calibration refinement loop.");
      param->setRange(0, kOfxFlagInfiniteMax);
      param->setDisplayRange(0, 1000);
      param->setDefault(10);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }

    {
      OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamMaxTotalAvgErr);
      param->setLabel("Max Total Average Error");
      param->setHint("Maximal limit of the total average error");
      param->setRange(0, 1);
      param->setDisplayRange(0, 1);
      param->setDefault(0.1);
      param->setAnimates(false);
      param->setParent(*groupCalibration);
    }
    
    {
      OFX::PushButtonParamDescriptor *param = desc.definePushButtonParam(kParamCalibrate);
      param->setLabel("Calibrate");
      param->setHint("calibrate");
      param->setParent(*groupCalibration);
    }

  }

  //Output Group
  {
    OFX::GroupParamDescriptor *groupOutput = desc.defineGroupParam(kParamGroupOutput);
    groupOutput->setLabel("Output");
    groupOutput->setAsTab();
    
    {
      OFX::BooleanParamDescriptor *param = desc.defineBooleanParam(kParamOutputIsCalibrated);
      param->setLabel("Is calibrated");
      param->setHint("Is calibrated");
      param->setParent(*groupOutput);
    }
    
    {
      OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputAvgReprojErr);
      param->setLabel("Average Reprojection Error");
      param->setDisplayRange(0, 10);
      param->setEvaluateOnChange(false);
      param->setEnabled(false);
      param->setAnimates(true);
      param->setParent(*groupOutput);
      param->setLayoutHint(OFX::eLayoutHintDivider);
    }
    
    {
      OFX::GroupParamDescriptor *groupCamera = desc.defineGroupParam(kParamOutputCameraGroup);
      groupCamera->setLabel("Intrinsics Camera Parameters");
      groupCamera->setParent(*groupOutput);
      groupCamera->setOpen(true);

      {
        OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputFocalLenght);
        param->setLabel("Focal Length");
        param->setDisplayRange(1, 100);
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupCamera);
      }

      {
        OFX::Double2DParamDescriptor *param = desc.defineDouble2DParam(kParamOutputPrincipalPointOffset);
        param->setLabel("Principal Point");
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupCamera);
      }
    }
    
    {    
      OFX::GroupParamDescriptor *groupLensDistortion = desc.defineGroupParam(kParamOutputLensDistortionGroup);
      groupLensDistortion->setLabel("Lens Distortion Coefficients");
      groupLensDistortion->setParent(*groupOutput);
      groupLensDistortion->setOpen(true);
        
      {
        OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputRadialCoef1);
        param->setLabel("Radial Coef1");
        param->setDisplayRange(0, 10);
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupLensDistortion);
      }

      {
        OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputRadialCoef2);
        param->setLabel("Radial Coef2");
        param->setDisplayRange(0, 10);
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupLensDistortion);
      }

      {
        OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputRadialCoef3);
        param->setLabel("Radial Coef3");
        param->setDisplayRange(0, 10);
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupLensDistortion);
      }

      {
        OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputTangentialCoef1);
        param->setLabel("Tangential Coef1");
        param->setDisplayRange(0, 10);
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupLensDistortion);
      }

      {
        OFX::DoubleParamDescriptor *param = desc.defineDoubleParam(kParamOutputTangentialCoef2);
        param->setLabel("Tangential Coef2");
        param->setDisplayRange(0, 10);
        param->setAnimates(true);
        param->setEvaluateOnChange(false);
        param->setEnabled(false);
        param->setAnimates(false);
        param->setParent(*groupLensDistortion);
        param->setLayoutHint(OFX::eLayoutHintDivider);
      }
    }
    
    {
      OFX::PushButtonParamDescriptor *param = desc.definePushButtonParam(kParamOutputClear);
      param->setLabel("Clear");
      param->setHint("clear");
      param->setEnabled(false);
      param->setParent(*groupOutput);
    }
  }
  
  //Debug Group
  {
    OFX::GroupParamDescriptor *groupDebug = desc.defineGroupParam(kParamGroupDebug);
    groupDebug->setLabel("Debug");
    groupDebug->setAsTab();

    {
      OFX::BooleanParamDescriptor *param = desc.defineBooleanParam(kParamDebugEnable);
      param->setLabel("Enable Debug");
      param->setHint("Would you want to export undistorted images?");
      param->setParent(*groupDebug);
    }

    {
      OFX::StringParamDescriptor *param = desc.defineStringParam(kParamDebugRejectedImgFolder);
      param->setLabel("Rejected Frames");
      param->setHint("Folder to export delete images during the calibration refinement loop.");
      param->setStringType(OFX::eStringTypeDirectoryPath);
      param->setFilePathExists(true);
      param->setParent(*groupDebug);
    }

    {
      OFX::StringParamDescriptor *param = desc.defineStringParam(kParamDebugSelectedImgFolder);
      param->setLabel("Selected Frames");
      param->setHint("Folder to export debug images.");
      param->setStringType(OFX::eStringTypeDirectoryPath);
      param->setFilePathExists(true);
      param->setParent(*groupDebug);
    }
  }
}
/**
 * @brief Function called to describe the plugin controls and features.
 * @param[in, out]   desc       Effect descriptor
 * @param[in]        context    Application context
 */
void LensDistortPluginFactory::describeInContext( OFX::ImageEffectDescriptor& desc, OFX::EContext context )
{
	// Create the mandated output clip
	OFX::ClipDescriptor* dstClip = desc.defineClip( kOfxImageEffectOutputClipName );

	dstClip->addSupportedComponent( OFX::ePixelComponentRGBA );
	dstClip->addSupportedComponent( OFX::ePixelComponentAlpha );
	dstClip->setSupportsTiles( true );

	// create the mandated source clip
	OFX::ClipDescriptor* srcClip = desc.defineClip( kOfxImageEffectSimpleSourceClipName );
	srcClip->addSupportedComponent( OFX::ePixelComponentRGBA );
	srcClip->addSupportedComponent( OFX::ePixelComponentAlpha );
	srcClip->setSupportsTiles( true );

	// declare an optional clip reference for RoD
	OFX::ClipDescriptor* srcRefClip = desc.defineClip( kClipOptionalSourceRef );
	srcRefClip->addSupportedComponent( OFX::ePixelComponentRGBA );
	srcRefClip->addSupportedComponent( OFX::ePixelComponentAlpha );
	srcRefClip->setSupportsTiles( true );
	srcRefClip->setOptional( true );
	srcRefClip->setLabel( "ref" );

	OFX::BooleanParamDescriptor* reverse = desc.defineBooleanParam( kParamReverse );
	reverse->setLabel( "Reverse" );
	reverse->setDefault( false );
	reverse->setHint( "Invert the effect.\n"
	                  "Distort becomes undistort, and vice versa." );

	// Controls
	OFX::BooleanParamDescriptor* displaySource = desc.defineBooleanParam( kParamDisplaySource );
	displaySource->setLabel( "displaySource" );
	displaySource->setDefault( false );
	displaySource->setHint( "Display the image source (usefull to parameter the distortion with lines overlays on the source image)." );

	OFX::ChoiceParamDescriptor* lensType = desc.defineChoiceParam( kParamLensType );
	lensType->setLabel( "Lens type" );
	lensType->appendOption( kParamLensTypeStandard );
	#ifndef TUTTLE_PRODUCTION
	lensType->appendOption( kParamLensTypeFishEye ); // not implemented yet...
	lensType->appendOption( kParamLensTypeAdvanced ); // not implemented yet...
	lensType->setIsSecret( true );
	#endif
	lensType->setDefault( 0 );

	OFX::DoubleParamDescriptor* coef1 = desc.defineDoubleParam( kParamCoef1 );
	coef1->setScriptName( "Main" );
	coef1->setDefault( 0.1 );
	coef1->setDisplayRange( -1.0, 1.0 );
	coef1->setHint( "Main distortion coeffecient\n"
	                ">0 : Barrel distortion\n"
	                "<0 : Pincushion distortion\n"
	                );

	OFX::DoubleParamDescriptor* coef2 = desc.defineDoubleParam( kParamCoef2 );
	coef2->setLabel( "Secondary" );
	coef2->setDefault( 0.0 );
	coef2->setDisplayRange( -1.0, 1.0 );
	coef2->setHint( "Secondary distortion coeffecient (usefull for fisheyes only)\n"
	                ">0 : Barrel distortion\n"
	                "<0 : Pincushion distortion\n"
	                );
	#ifdef TUTTLE_PRODUCTION
	coef2->setIsSecret( true );
	#endif

	OFX::DoubleParamDescriptor* squeeze = desc.defineDoubleParam( kParamSqueeze );
	squeeze->setLabel( "Squeeze" );
	#ifdef TUTTLE_PRODUCTION
	squeeze->setIsSecret( true );
	#endif
	//    squeeze->setDoubleType( eDoubleTypeNormalisedX );
	squeeze->setDefault( 1.0 );
	squeeze->setRange( 0.00001, 1.0 );
	squeeze->setDisplayRange( 0.01, 1.0 );
	squeeze->setHint( "Squeeze distortion coeffecient (usefull for bad quality lens...)" );

	OFX::Double2DParamDescriptor* asymmetric = desc.defineDouble2DParam( kParamAsymmetric );
	asymmetric->setLabel( "Asymmetric" );
	#ifdef TUTTLE_PRODUCTION
	asymmetric->setIsSecret( true );
	#endif
	//    asymmetric->setDoubleType( eDoubleTypeNormalisedXY );
	asymmetric->setDefault( 0.0, 0.0 );
	asymmetric->setRange( 0.0, 0.0, 1.0, 1.0 );
	asymmetric->setDisplayRange( 0.0, 0.0, 1.0, 1.0 );
	asymmetric->setHint( "asymmetric distortion coeffecient (usefull for bad quality lens...)" );

	OFX::Double2DParamDescriptor* center = desc.defineDouble2DParam( kParamCenter );
	center->setLabel( "Center" );
	center->setDoubleType( OFX::eDoubleTypePlain );
	center->setDefault( 0.0, 0.0 );
	center->setDisplayRange( -1.0, -1.0, 1.0, 1.0 );
	center->setHint( "Center parameter allows you to shift the center of distortion." );

	OFX::BooleanParamDescriptor* centerOverlay = desc.defineBooleanParam( kParamCenterOverlay );
	centerOverlay->setLabel( "Display distortion center" );
	centerOverlay->setDefault( false );
	centerOverlay->setEvaluateOnChange( false );
	centerOverlay->setHint( "Active distortion center point overlay." );

	OFX::ChoiceParamDescriptor* centerType = desc.defineChoiceParam( kParamCenterType );
	centerType->setLabel( "Center type" );
	#ifdef TUTTLE_PRODUCTION
	centerType->setIsSecret( true );
	#endif
	centerType->appendOption( kParamCenterTypeSource );
	centerType->appendOption( kParamCenterTypeRoW );
	centerType->setDefault( 0 );
	centerType->setHint( "Centered on source or output image." );

	OFX::DoubleParamDescriptor* preScale = desc.defineDoubleParam( kParamPreScale );
	preScale->setLabel( "Pre-scale" );
	//    preScale->setDoubleType( eDoubleTypeNormalisedXY );
	preScale->setDefault( 1.0 );
	preScale->setRange( 0.00001, std::numeric_limits<double>::max() );
	preScale->setDisplayRange( 0.0, 2.5 );
	preScale->setHint( "If the transformation of optics is high, you may need to change the scale of the result to be globally closer to the source image or preserve a good resolution." );

	OFX::DoubleParamDescriptor* postScale = desc.defineDoubleParam( kParamPostScale );
	postScale->setLabel( "Post-scale" );
	//    scale->setDoubleType( eDoubleTypeNormalisedXY );
	postScale->setDefault( 1.0 );
	postScale->setRange( 0.00001, std::numeric_limits<double>::max() );
	postScale->setDisplayRange( 0.0, 2.5 );
	postScale->setHint( "If the transformation of optics is high, you may need to change the scale of the result to be globally closer to the source image or preserve a good resolution." );

	OFX::ChoiceParamDescriptor* interpolation = desc.defineChoiceParam( kParamInterpolation );
	interpolation->setLabel( "Interpolation" );
	interpolation->appendOption( kParamInterpolationNearest );
	interpolation->appendOption( kParamInterpolationBilinear );
	interpolation->setDefault( 1 );
	interpolation->setHint( "Interpolation method" );

	OFX::ChoiceParamDescriptor* resizeRod = desc.defineChoiceParam( kParamResizeRod );
	resizeRod->setLabel( "Resize RoD" );
	resizeRod->appendOption( kParamResizeRodNo );
	resizeRod->appendOption( kParamResizeRodSourceRef );
	resizeRod->appendOption( kParamResizeRodMin );
	resizeRod->appendOption( kParamResizeRodMax );
	resizeRod->appendOption( kParamResizeRodManual );
	resizeRod->setDefault( 0 );
	resizeRod->setHint( "Resize output RoD" );

	OFX::DoubleParamDescriptor* scaleRod = desc.defineDoubleParam( kParamResizeRodManualScale );
	scaleRod->setLabel( "Scale RoD" );
	//    scaleRod->setDoubleType( eDoubleTypeNormalisedXY );
	//    scaleRod->setIsSecret( true );
	//    scaleRod->setEnabled( false );
	scaleRod->setDefault( 1.0 );
	scaleRod->setRange( 0.0, std::numeric_limits<double>::max() );
	scaleRod->setDisplayRange( 0, 2.5 );
	scaleRod->setHint( "Adjust the output RoD." );

	OFX::GroupParamDescriptor* displayOptions = desc.defineGroupParam( kParamDisplayOptions );
	displayOptions->setLabel( "Display options" );
	displayOptions->setHint( "Display options (change nothing on the image)" );

	OFX::BooleanParamDescriptor* displayGrid = desc.defineBooleanParam( kParamGridOverlay );
	displayGrid->setLabel( "Display grid" );
	displayGrid->setParent( *displayOptions );
	displayGrid->setDefault( false );
	displayGrid->setEvaluateOnChange( false );
	displayGrid->setHint( "Display the grid" );

	OFX::Double2DParamDescriptor* gridCenter = desc.defineDouble2DParam( kParamGridCenter );
	gridCenter->setLabel( "Grid center" );
	gridCenter->setDoubleType( OFX::eDoubleTypePlain );
	gridCenter->setParent( *displayOptions );
	gridCenter->setDefault( 0.0, 0.0 );
	gridCenter->setDisplayRange( -1.0, -1.0, 1.0, 1.0 );
	gridCenter->setEvaluateOnChange( false );
	gridCenter->setHint( "Allows you to shift the center of the  display grid." );

	OFX::BooleanParamDescriptor* gridCenterOverlay = desc.defineBooleanParam( kParamGridCenterOverlay );
	gridCenterOverlay->setLabel( "Display grid center" );
	gridCenterOverlay->setParent( *displayOptions );
	gridCenterOverlay->setDefault( false );
	gridCenterOverlay->setEvaluateOnChange( false );
	gridCenterOverlay->setHint( "Active grid center point overlay." );

	OFX::Double2DParamDescriptor* gridScale = desc.defineDouble2DParam( kParamGridScale );
	gridScale->setLabel( "Grid scale" );
	gridScale->setDoubleType( OFX::eDoubleTypePlain );
	gridScale->setParent( *displayOptions );
	gridScale->setDefault( 1.0, 1.0 );
	gridScale->setDisplayRange( -10.0, -10.0, 10.0, 10.0 );
	gridScale->setEvaluateOnChange( false );
	gridScale->setHint( "Allows you to scale the display grid." );

	OFX::GroupParamDescriptor* debugOptions = desc.defineGroupParam( kParamDebugOptions );
	debugOptions->setLabel( "Debug options" );
	debugOptions->setHint( "Debug options" );
	debugOptions->setParent( *displayOptions );
	#ifdef TUTTLE_PRODUCTION
	debugOptions->setIsSecret( true );
	#endif

	OFX::BooleanParamDescriptor* debugDisplayRoi = desc.defineBooleanParam( kParamDebugDisplayRoi );
	debugDisplayRoi->setLabel( "Display RoI" );
	#ifdef TUTTLE_PRODUCTION
	debugDisplayRoi->setIsSecret( true );
	#endif
	debugDisplayRoi->setParent( *debugOptions );
	debugDisplayRoi->setDefault( false );
	debugDisplayRoi->setEvaluateOnChange( false );
	debugDisplayRoi->setHint( "Display RoI" );

	OFX::PushButtonParamDescriptor* helpButton = desc.definePushButtonParam( kParamHelp );
	helpButton->setLabel( "Help" );
}
/**
 * @brief Function called to describe the plugin controls and features.
 * @param[in, out]   desc       Effect descriptor
 * @param[in]        context    Application context
 */
void ImageStatisticsPluginFactory::describeInContext( OFX::ImageEffectDescriptor& desc,
                                                      OFX::EContext               context )
{
	OFX::ClipDescriptor* srcClip = desc.defineClip( kOfxImageEffectSimpleSourceClipName );

	srcClip->addSupportedComponent( OFX::ePixelComponentRGBA );
	srcClip->addSupportedComponent( OFX::ePixelComponentAlpha );
	srcClip->setSupportsTiles( kSupportTiles );

	// Create the mandated output clip
	OFX::ClipDescriptor* dstClip = desc.defineClip( kOfxImageEffectOutputClipName );
	dstClip->addSupportedComponent( OFX::ePixelComponentRGBA );
	dstClip->addSupportedComponent( OFX::ePixelComponentAlpha );
	dstClip->setSupportsTiles( kSupportTiles );

	OFX::ChoiceParamDescriptor* coordSystem = desc.defineChoiceParam( kParamCoordinateSystem );
	coordSystem->setLabel( "Coordinate system" );
	coordSystem->appendOption( kParamCoordinateSystemNormalized );
	coordSystem->appendOption( kParamCoordinateSystemCanonical );
	coordSystem->setDefault( 0 );

	OFX::Double2DParamDescriptor* rectCenter = desc.defineDouble2DParam( kParamRectCenter );
	rectCenter->setLabel( "Center" );
	rectCenter->setDoubleType( OFX::eDoubleTypePlain );
	//	rectCenter->setDoubleType( OFX::eDoubleTypeNormalisedXYAbsolute );
	rectCenter->setDefault( 0.5, 0.5 );

	OFX::Double2DParamDescriptor* rectSize = desc.defineDouble2DParam( kParamRectSize );
	rectSize->setLabel( "Size" );
	rectSize->setDoubleType( OFX::eDoubleTypePlain );
	//	rectSize->setDoubleType( OFX::eDoubleTypeNormalisedXYAbsolute );
	rectSize->setDefault( 0.5, 0.5 );

	OFX::ChoiceParamDescriptor* chooseOutput = desc.defineChoiceParam( kParamChooseOutput );
	chooseOutput->setLabel( "Choose output" );
	chooseOutput->appendOption( kParamChooseOutputSource );
	chooseOutput->appendOption( kParamChooseOutputAverage );
	chooseOutput->appendOption( kParamChooseOutputChannelMin );
	chooseOutput->appendOption( kParamChooseOutputChannelMax );
	chooseOutput->appendOption( kParamChooseOutputLuminosityMin );
	chooseOutput->appendOption( kParamChooseOutputLuminosityMax );
	chooseOutput->setDefault( 0 );

	OFX::GroupParamDescriptor* outputGroup = desc.defineGroupParam( kParamOutputGroup );
	outputGroup->setLabel( "Output" );

	// -----------------------------------------------------------------------------

	OFX::GroupParamDescriptor* rgbaGroup = desc.defineGroupParam( kParamOutputGroupRGBA );
	rgbaGroup->setLabel( "RGBA" );
	rgbaGroup->setParent( outputGroup );

	OFX::RGBAParamDescriptor* outputAverage = desc.defineRGBAParam( kParamOutputAverage );
	outputAverage->setLabel( "Average" );
	outputAverage->setParent( rgbaGroup );
	outputAverage->setEvaluateOnChange( false );

	OFX::RGBAParamDescriptor* outputChannelMin = desc.defineRGBAParam( kParamOutputChannelMin );
	outputChannelMin->setLabel( "Channels' min" );
	outputChannelMin->setHint( "Minimum value per channel" );
	outputChannelMin->setParent( rgbaGroup );
	outputChannelMin->setEvaluateOnChange( false );

	OFX::RGBAParamDescriptor* outputChannelMax = desc.defineRGBAParam( kParamOutputChannelMax );
	outputChannelMax->setLabel( "Channels' max" );
	outputChannelMax->setParent( rgbaGroup );
	outputChannelMax->setEvaluateOnChange( false );

	OFX::RGBAParamDescriptor* outputLuminosityMin = desc.defineRGBAParam( kParamOutputLuminosityMin );
	outputLuminosityMin->setLabel( "Luminosity min" );
	outputLuminosityMin->setParent( rgbaGroup );
	outputLuminosityMin->setEvaluateOnChange( false );

	OFX::RGBAParamDescriptor* outputLuminosityMax = desc.defineRGBAParam( kParamOutputLuminosityMax );
	outputLuminosityMax->setLabel( "Luminosity max" );
	outputLuminosityMax->setParent( rgbaGroup );
	outputLuminosityMax->setEvaluateOnChange( false );

	OFX::RGBAParamDescriptor* outputKurtosis = desc.defineRGBAParam( kParamOutputKurtosis );
	outputKurtosis->setLabel( "Kurtosis" );
	outputKurtosis->setParent( rgbaGroup );
	outputKurtosis->setEvaluateOnChange( false );

	OFX::RGBAParamDescriptor* outputSkewness = desc.defineRGBAParam( kParamOutputSkewness );
	outputSkewness->setLabel( "Skewness" );
	outputSkewness->setParent( rgbaGroup );
	outputSkewness->setEvaluateOnChange( false );

	// -----------------------------------------------------------------------------

	OFX::GroupParamDescriptor* hslGroup = desc.defineGroupParam( kParamOutputGroupHSL );
	hslGroup->setLabel( "HSL" );
	hslGroup->setParent( outputGroup );

	OFX::Double3DParamDescriptor* outputAverageHSL = desc.defineDouble3DParam( kParamOutputAverageHSL );
	outputAverageHSL->setLabel( "Average" );
	outputAverageHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputAverageHSL->setDimensionLabels( "h", "s", "l" );
	outputAverageHSL->setParent( hslGroup );
	outputAverageHSL->setEvaluateOnChange( false );

	OFX::Double3DParamDescriptor* outputChannelMinHSL = desc.defineDouble3DParam( kParamOutputChannelMinHSL );
	outputChannelMinHSL->setLabel( "Channels' min" );
	outputChannelMinHSL->setHint( "Minimum value per channel" );
	outputChannelMinHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputChannelMinHSL->setDimensionLabels( "h", "s", "l" );
	outputChannelMinHSL->setParent( hslGroup );
	outputChannelMinHSL->setEvaluateOnChange( false );

	OFX::Double3DParamDescriptor* outputChannelMaxHSL = desc.defineDouble3DParam( kParamOutputChannelMaxHSL );
	outputChannelMaxHSL->setLabel( "Channels' max" );
	outputChannelMaxHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputChannelMaxHSL->setDimensionLabels( "h", "s", "l" );
	outputChannelMaxHSL->setParent( hslGroup );
	outputChannelMaxHSL->setEvaluateOnChange( false );

	OFX::Double3DParamDescriptor* outputLuminosityMinHSL = desc.defineDouble3DParam( kParamOutputLuminosityMinHSL );
	outputLuminosityMinHSL->setLabel( "Luminosity min" );
	outputLuminosityMinHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputLuminosityMinHSL->setDimensionLabels( "h", "s", "l" );
	outputLuminosityMinHSL->setParent( hslGroup );
	outputLuminosityMinHSL->setEvaluateOnChange( false );

	OFX::Double3DParamDescriptor* outputLuminosityMaxHSL = desc.defineDouble3DParam( kParamOutputLuminosityMaxHSL );
	outputLuminosityMaxHSL->setLabel( "Luminosity max" );
	outputLuminosityMaxHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputLuminosityMaxHSL->setDimensionLabels( "h", "s", "l" );
	outputLuminosityMaxHSL->setParent( hslGroup );
	outputLuminosityMaxHSL->setEvaluateOnChange( false );

	OFX::Double3DParamDescriptor* outputKurtosisHSL = desc.defineDouble3DParam( kParamOutputKurtosisHSL );
	outputKurtosisHSL->setLabel( "Kurtosis" );
	outputKurtosisHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputKurtosisHSL->setDimensionLabels( "h", "s", "l" );
	outputKurtosisHSL->setParent( hslGroup );
	outputKurtosisHSL->setEvaluateOnChange( false );

	OFX::Double3DParamDescriptor* outputSkewnessHSL = desc.defineDouble3DParam( kParamOutputSkewnessHSL );
	outputSkewnessHSL->setLabel( "Skewness" );
	outputSkewnessHSL->setDoubleType( OFX::eDoubleTypePlain );
	outputSkewnessHSL->setDimensionLabels( "h", "s", "l" );
	outputSkewnessHSL->setParent( hslGroup );
	outputSkewnessHSL->setEvaluateOnChange( false );
}
/**
 * @brief Function called to describe the plugin controls and features.
 * @param[in, out]   desc       Effect descriptor
 * @param[in]        context    Application context
 */
void ColorSpacePluginFactory::describeInContext(OFX::ImageEffectDescriptor& desc, OFX::EContext context)
{
    OFX::ClipDescriptor* srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
    srcClip->addSupportedComponent(OFX::ePixelComponentRGBA);
    srcClip->addSupportedComponent(OFX::ePixelComponentRGB);
    srcClip->addSupportedComponent(OFX::ePixelComponentAlpha);
    srcClip->setSupportsTiles(kSupportTiles);

    // Create the mandated output clip
    OFX::ClipDescriptor* dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
    dstClip->addSupportedComponent(OFX::ePixelComponentRGBA);
    dstClip->addSupportedComponent(OFX::ePixelComponentRGB);
    dstClip->addSupportedComponent(OFX::ePixelComponentAlpha);
    dstClip->setSupportsTiles(kSupportTiles);

    /* ----------------------- INPUT PARAMETERS -------------------------- */

    OFX::GroupParamDescriptor* inGroup = desc.defineGroupParam(kColorSpaceIn);
    inGroup->setLabel("Input configuration");

    OFX::ChoiceParamDescriptor* inReferenceSpace = desc.defineChoiceParam(kColorSpaceReferenceSpaceIn);
    inReferenceSpace->setLabel("Reference Space");
    inReferenceSpace->setParent(inGroup);

    OFX::GroupParamDescriptor* inCustom = desc.defineGroupParam(kColorSpaceCustomizedIn);
    inCustom->setLabel("Custom");
    inCustom->setOpen(false);
    inCustom->setParent(inGroup);

    OFX::ChoiceParamDescriptor* inGradationLaw = desc.defineChoiceParam(kColorSpaceGradationLawIn);
    inGradationLaw->setLabel("Gradation Law");
    inGradationLaw->setParent(inCustom);

    OFX::DoubleParamDescriptor* inGammaValue = desc.defineDoubleParam(kColorSpaceInGammaValue);
    inGammaValue->setLabel("Gamma");
    inGammaValue->setDefault(1.0);
    inGammaValue->setRange(0.0, std::numeric_limits<double>::max());
    inGammaValue->setDisplayRange(0.0, 5.0);
    inGammaValue->setHint("Adjust the Gamma.");
    inGammaValue->setParent(inCustom);

    OFX::DoubleParamDescriptor* inBlackPoint = desc.defineDoubleParam(kColorSpaceInBlackPoint);
    inBlackPoint->setLabel("Black Point");
    inBlackPoint->setDefault(0.0);
    inBlackPoint->setRange(0.0, 1.0);
    inBlackPoint->setDisplayRange(0.0, 1.0);
    inBlackPoint->setHint("Adjust the Black Point.");
    inBlackPoint->setParent(inCustom);

    OFX::DoubleParamDescriptor* inWhitePoint = desc.defineDoubleParam(kColorSpaceInWhitePoint);
    inWhitePoint->setLabel("White Point");
    inWhitePoint->setDefault(1.0);
    inWhitePoint->setRange(0.0, 1.0);
    inWhitePoint->setDisplayRange(0.0, 1.0);
    inWhitePoint->setHint("Adjust the White Point.");
    inWhitePoint->setParent(inCustom);

    OFX::DoubleParamDescriptor* inGammaSensito = desc.defineDoubleParam(kColorSpaceInGammaSensito);
    inGammaSensito->setLabel("Gamma Sensito");
    inGammaSensito->setDefault(1.0);
    inGammaSensito->setRange(0.0, std::numeric_limits<double>::max());
    inGammaSensito->setDisplayRange(0.0, 5.0);
    inGammaSensito->setHint("Adjust the Gamma Sensito.");
    inGammaSensito->setParent(inCustom);

    OFX::ChoiceParamDescriptor* inLayout = desc.defineChoiceParam(kColorSpaceLayoutIn);
    inLayout->setLabel("Layout");
    inLayout->setParent(inCustom);

    OFX::ChoiceParamDescriptor* inTempColor = desc.defineChoiceParam(kColorSpaceTempColorIn);
    inTempColor->setLabel("Color Temperature");
    inTempColor->setHint("Select the color temperature.");
    inTempColor->setParent(inCustom);

    OFX::GroupParamDescriptor* inPrimaries = desc.defineGroupParam(kColorSpacePrimariesIn);
    inPrimaries->setLabel("Primaries color");
    inPrimaries->setOpen(false);
    inPrimaries->setParent(inCustom);

    OFX::DoubleParamDescriptor* inXr = desc.defineDoubleParam(kColorSpaceXrIn);
    inXr->setLabel("X red");
    inXr->setDefault(1.0);
    inXr->setRange(0.0, 1.0);
    inXr->setDisplayRange(0.0, 1.0);
    inXr->setHint("Adjust the X red primary color.");
    inXr->setParent(inPrimaries);

    OFX::DoubleParamDescriptor* inYr = desc.defineDoubleParam(kColorSpaceYrIn);
    inYr->setLabel("Y red");
    inYr->setDefault(1.0);
    inYr->setRange(0.0, 1.0);
    inYr->setDisplayRange(0.0, 1.0);
    inYr->setHint("Adjust the Y red primary color.");
    inYr->setParent(inPrimaries);

    OFX::DoubleParamDescriptor* inXg = desc.defineDoubleParam(kColorSpaceXgIn);
    inXg->setLabel("X green");
    inXg->setDefault(1.0);
    inXg->setRange(0.0, 1.0);
    inXg->setDisplayRange(0.0, 1.0);
    inXg->setHint("Adjust the X green primary color.");
    inXg->setParent(inPrimaries);

    OFX::DoubleParamDescriptor* inYg = desc.defineDoubleParam(kColorSpaceYgIn);
    inYg->setLabel("Y green");
    inYg->setDefault(1.0);
    inYg->setRange(0.0, 1.0);
    inYg->setDisplayRange(0.0, 1.0);
    inYg->setHint("Adjust the Y green primary color.");
    inYg->setParent(inPrimaries);

    OFX::DoubleParamDescriptor* inXb = desc.defineDoubleParam(kColorSpaceXbIn);
    inXb->setLabel("X blue");
    inXb->setDefault(1.0);
    inXb->setRange(0.0, 1.0);
    inXb->setDisplayRange(0.0, 1.0);
    inXb->setHint("Adjust the X blue primary color.");
    inXb->setParent(inPrimaries);

    OFX::DoubleParamDescriptor* inYb = desc.defineDoubleParam(kColorSpaceYbIn);
    inYb->setLabel("Y blue");
    inYb->setDefault(1.0);
    inYb->setRange(0.0, 1.0);
    inYb->setDisplayRange(0.0, 1.0);
    inYb->setHint("Adjust the Y blue primary color.");
    inYb->setParent(inPrimaries);

    /* ----------------------- OUTPUT PARAMETERS -------------------------- */

    OFX::GroupParamDescriptor* outGroup = desc.defineGroupParam(kColorSpaceOut);
    outGroup->setLabel("Output configuration");

    OFX::ChoiceParamDescriptor* outReferenceSpace = desc.defineChoiceParam(kColorSpaceReferenceSpaceOut);
    outReferenceSpace->setLabel("Reference Space");
    outReferenceSpace->setParent(outGroup);

    OFX::GroupParamDescriptor* outCustom = desc.defineGroupParam(kColorSpaceCustomizedOut);
    outCustom->setLabel("Custom");
    outCustom->setOpen(false);
    outCustom->setParent(outGroup);

    OFX::ChoiceParamDescriptor* outGradationLaw = desc.defineChoiceParam(kColorSpaceGradationLawOut);
    outGradationLaw->setLabel("Gradation Law");
    outGradationLaw->setParent(outCustom);

    OFX::DoubleParamDescriptor* outGammaValue = desc.defineDoubleParam(kColorSpaceOutGammaValue);
    outGammaValue->setLabel("Gamma");
    outGammaValue->setDefault(1.0);
    outGammaValue->setRange(0.0, std::numeric_limits<double>::max());
    outGammaValue->setDisplayRange(0.0, 5.0);
    outGammaValue->setHint("Adjust the Gamma.");
    outGammaValue->setParent(outCustom);

    OFX::DoubleParamDescriptor* outBlackPoint = desc.defineDoubleParam(kColorSpaceOutBlackPoint);
    outBlackPoint->setLabel("Black Point");
    outBlackPoint->setDefault(0.0);
    outBlackPoint->setRange(0.0, 1.0);
    outBlackPoint->setDisplayRange(0.0, 1.0);
    outBlackPoint->setHint("Adjust the Black Point.");
    outBlackPoint->setParent(outCustom);

    OFX::DoubleParamDescriptor* outWhitePoint = desc.defineDoubleParam(kColorSpaceOutWhitePoint);
    outWhitePoint->setLabel("White Point");
    outWhitePoint->setDefault(1.0);
    outWhitePoint->setRange(0.0, 1.0);
    outWhitePoint->setDisplayRange(0.0, 1.0);
    outWhitePoint->setHint("Adjust the White Point.");
    outWhitePoint->setParent(outCustom);

    OFX::DoubleParamDescriptor* outGammaSensito = desc.defineDoubleParam(kColorSpaceOutGammaSensito);
    outGammaSensito->setLabel("Gamma Sensito");
    outGammaSensito->setDefault(1.0);
    outGammaSensito->setRange(0.0, std::numeric_limits<double>::max());
    outGammaSensito->setDisplayRange(0.0, 5.0);
    outGammaSensito->setHint("Adjust the Gamma Sensito.");
    outGammaSensito->setParent(outCustom);

    OFX::ChoiceParamDescriptor* outLayout = desc.defineChoiceParam(kColorSpaceLayoutOut);
    outLayout->setLabel("Layout");
    outLayout->setParent(outCustom);

    OFX::ChoiceParamDescriptor* outTempColor = desc.defineChoiceParam(kColorSpaceTempColorOut);
    outTempColor->setLabel("Color Temperature");
    outTempColor->setHint("Select the color temperature.");
    outTempColor->setParent(outCustom);

    OFX::GroupParamDescriptor* outPrimaries = desc.defineGroupParam(kColorSpacePrimariesOut);
    outPrimaries->setLabel("Primaries color");
    outPrimaries->setOpen(false);
    outPrimaries->setParent(outCustom);

    OFX::DoubleParamDescriptor* outXr = desc.defineDoubleParam(kColorSpaceXrOut);
    outXr->setLabel("X red");
    outXr->setDefault(1.0);
    outXr->setRange(0.0, 1.0);
    outXr->setDisplayRange(0.0, 1.0);
    outXr->setHint("Adjust the X red primary color.");
    outXr->setParent(outPrimaries);

    OFX::DoubleParamDescriptor* outYr = desc.defineDoubleParam(kColorSpaceYrOut);
    outYr->setLabel("Y red");
    outYr->setDefault(1.0);
    outYr->setRange(0.0, 1.0);
    outYr->setDisplayRange(0.0, 1.0);
    outYr->setHint("Adjust the Y red primary color.");
    outYr->setParent(outPrimaries);

    OFX::DoubleParamDescriptor* outXg = desc.defineDoubleParam(kColorSpaceXgOut);
    outXg->setLabel("X green");
    outXg->setDefault(1.0);
    outXg->setRange(0.0, 1.0);
    outXg->setDisplayRange(0.0, 1.0);
    outXg->setHint("Adjust the X green primary color.");
    outXg->setParent(outPrimaries);

    OFX::DoubleParamDescriptor* outYg = desc.defineDoubleParam(kColorSpaceYgOut);
    outYg->setLabel("Y green");
    outYg->setDefault(1.0);
    outYg->setRange(0.0, 1.0);
    outYg->setDisplayRange(0.0, 1.0);
    outYg->setHint("Adjust the Y green primary color.");
    outYg->setParent(outPrimaries);

    OFX::DoubleParamDescriptor* outXb = desc.defineDoubleParam(kColorSpaceXbOut);
    outXb->setLabel("X blue");
    outXb->setDefault(1.0);
    outXb->setRange(0.0, 1.0);
    outXb->setDisplayRange(0.0, 1.0);
    outXb->setHint("Adjust the X blue primary color.");
    outXb->setParent(outPrimaries);

    OFX::DoubleParamDescriptor* outYb = desc.defineDoubleParam(kColorSpaceYbOut);
    outYb->setLabel("Y blue");
    outYb->setDefault(1.0);
    outYb->setRange(0.0, 1.0);
    outYb->setDisplayRange(0.0, 1.0);
    outYb->setHint("Adjust the Y blue primary color.");
    outYb->setParent(outPrimaries);

    /* -------------- ENUMS FILLING ----------------*/

    ttlc::ColorSpaceMaps csMaps;
    ttlc::ColorSpaceMap mapReferenceSpace = csMaps.getMapReferenceSpaces();
    ttlc::ColorSpaceMap mapGradationLaw = csMaps.getMapGradationLaw();
    ttlc::ColorSpaceMap mapLayout = csMaps.getMapLayout();
    ttlc::ColorSpaceMap mapColourTemp = csMaps.getMapColourTemp();
    ttlc::ColorSpaceMap::iterator it;
    ttlc::ColorSpacePair highest;

    /* Reference Space */
    highest = *mapReferenceSpace.rbegin(); // last element
    it = mapReferenceSpace.begin();
    do
    {
        inReferenceSpace->appendOption((*it).second);
        outReferenceSpace->appendOption((*it).second);

    } while(mapReferenceSpace.value_comp()(*it++, highest));

    it = mapReferenceSpace.begin();
    inReferenceSpace->setDefault((*it).first);
    outReferenceSpace->setDefault((*it).first);

    /* Gradation */
    highest = *mapGradationLaw.rbegin();
    it = mapGradationLaw.begin();
    do
    {
        inGradationLaw->appendOption((*it).second);
        outGradationLaw->appendOption((*it).second);

    } while(mapGradationLaw.value_comp()(*it++, highest));

    it = mapGradationLaw.begin();
    inGradationLaw->setDefault((*it).first);
    outGradationLaw->setDefault((*it).first);

    /* Layout */
    highest = *mapLayout.rbegin();
    it = mapLayout.begin();
    do
    {
        inLayout->appendOption((*it).second);
        outLayout->appendOption((*it).second);

    } while(mapLayout.value_comp()(*it++, highest));

    it = mapLayout.begin();
    inLayout->setDefault((*it).first);
    outLayout->setDefault((*it).first);

    /* Colour temperature */
    highest = *mapColourTemp.rbegin();
    it = mapColourTemp.begin();
    do
    {
        inTempColor->appendOption((*it).second);
        outTempColor->appendOption((*it).second);

    } while(mapColourTemp.value_comp()(*it++, highest));

    it = mapColourTemp.begin();
    inTempColor->setDefault((*it).first);
    outTempColor->setDefault((*it).first);
}