<?php
    
    /**
     * This file is part of the PHP Video Toolkit v2 package.
     *
     * @author Oliver Lillie (aka buggedcom) <publicmail@buggedcom.co.uk>
     * @license Dual licensed under MIT and GPLv2
     * @copyright Copyright (c) 2008-2014 Oliver Lillie <http://www.buggedcom.co.uk>
     * @package PHPVideoToolkit V2
     * @version 2.1.7-beta
     * @uses ffmpeg http://ffmpeg.sourceforge.net/
     */
     
    namespace PHPVideoToolkit;

    /**
     * @access public
     * @author Oliver Lillie
     * @package default
     */
    class VideoFormat extends AudioFormat
    {
        const DIMENSION_SAME_AS_SOURCE  = 'SameAsSource';
        
        const DIMENSION_SQCIF   = '128x96';
        const DIMENSION_QCIF    = '176x144';
        const DIMENSION_CIF     = '352x288';
        const DIMENSION_4CIF    = '704x576';
        const DIMENSION_QQVGA   = '160x120';
        const DIMENSION_QVGA    = '320x240';
        const DIMENSION_VGA     = '640x480';
        const DIMENSION_SVGA    = '800x600';
        const DIMENSION_XGA     = '1024x768';
        const DIMENSION_UXGA    = '1600x1200';
        const DIMENSION_QXGA    = '2048x1536';
        const DIMENSION_SXGA    = '1280x1024';
        const DIMENSION_QSXGA   = '2560x2048';
        const DIMENSION_HSXGA   = '5120x4096';
        const DIMENSION_WVGA    = '852x480';
        const DIMENSION_WXGA    = '1366x768';
        const DIMENSION_WSXGA   = '1600x1024';
        const DIMENSION_WUXGA   = '1920x1200';
        const DIMENSION_WOXGA   = '2560x1600';
        const DIMENSION_WQSXGA  = '3200x2048';
        const DIMENSION_WQUXGA  = '3840x2400';
        const DIMENSION_WHSXGA  = '6400x4096';
        const DIMENSION_WHUXGA  = '7680x4800';
        const DIMENSION_CGA     = '320x200';
        const DIMENSION_EGA     = '640x350';
        const DIMENSION_HD480   = '852x480';
        const DIMENSION_HD720   = '1280x720';
        const DIMENSION_HD1080  = '1920x1080';
        
        protected $_restricted_video_bitrates;
        protected $_restricted_video_codecs;
        protected $_restricted_video_pixel_formats;
        protected $_restricted_video_frame_rates;

        public function __construct($input_output_type=Format::OUTPUT, Config $config=null)
        {
            parent::__construct($input_output_type, $config);
            
            $this->_format = array_merge($this->_format, array(
                'disable_video' => null,
                'video_codec' => null,
                'video_quality' => null,
                'video_dimensions' => null,
                'video_scale' => null,
                'video_padding' => null,
                'video_aspect_ratio' => null,
                'video_frame_rate' => null,
                'video_bitrate' => null,
                'video_pixel_format' => null,
                'video_rotation' => null,
                'video_flip_horizontal' => null,
                'video_flip_vertical' => null,
                'video_max_frames' => null,
                'video_filters' => null,
            ));

            $frame_rate_command = 'r';
            if($input_output_type === Format::INPUT)
            {
                $frame_rate_command = 'framerate';
            }

            $this->_format_to_command = array_merge($this->_format_to_command, array(
                'disable_video'             => '-vn',
                'video_quality'             => '-qscale:v <setting>',
                'video_codec'               => '-vcodec <setting>',
                'video_dimensions'          => '-s <width>x<height>',
                'video_scale'               => '-vf scale=<width>:<height>',
                'video_padding'             => '-vf pad=<width>:<height>:<x>:<y>:<colour>',
                'video_aspect_ratio'        => '-aspect <ratio>',
                'video_frame_rate'          => '-'.$frame_rate_command.' <setting>',
                'video_bitrate'             => '-b:v <setting>',
                'video_pixel_format'        => '-pix_fmt <setting>',
                'video_rotation'            => '-vf transpose=<setting>',
                'video_flip_horizontal'     => '-vf hflip',
                'video_flip_vertical'       => '-vf vflip',
                'video_max_frames'          => '-vframes <setting>',
            ));
            
            $this->_restricted_video_bitrates = null;
            $this->_restricted_video_codecs = null;
            $this->_restricted_video_pixel_formats = null;
            $this->_restricted_video_frame_rates = null;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @return void
         */
        public function updateFormatOptions(&$save_path, $overwrite)
        {
            parent::updateFormatOptions($save_path, $overwrite);

//          adjust the sample frequency if the audio codec is acc and frequency is not already set.
            if(in_array($this->_format['audio_codec'], array('libfdk_aac', 'aac')) === true && $this->_format['audio_sample_frequency'] === null)
            {
                $this->setAudioSampleFrequency(22050);
            }
            
            $video_data = $this->_media_object->readVideoComponent();

//          check to see if the aspect ratio has fixed the width and heights, if so we must apply the size to any output.
            if($video_data['dimensions']['aspect_ratio_fix_warning'] === true)
            {
                $this->setVideoDimensions($video_data['dimensions']['width'], $video_data['dimensions']['height']);
            }

//          if we have a rotation and it's set to true then we must autodetect the rotation according to the
//          meta data available.
//          !IMPORTANT that auto orientation is done before any automatic flipping.
            if(empty($this->_format['video_rotation']) === false && $this->_format['video_rotation'] === true)
            {
                if(empty($video_data['rotation']) === false)
                {
                    $current_rotation = (int) $video_data['rotation'];
                    $this->setVideoRotation(-$current_rotation);
                    $this->addCommand('-metadata:s:v', 'rotate=""');
                }
                else
                {
                    $this->_format['video_rotation'] = null;
                }
            }
            
//          if the aspect ratio of the rotated video is not the same as the final video we must update the final outputs
//          aspect ratio. However, we will only automatically do this if the aspect ratio is not already set.
            if(in_array($this->_format['video_rotation'], array(1, 2)) === true && $this->_format['video_aspect_ratio'] === null)
            {
                $aspect_ratio = $video_data['display_aspect_ratio'];
                if(preg_match('/^[0-9]+\.[0-9]+$/', $aspect_ratio, $_m) > 0)
                {
                    $this->setVideoAspectRatio(1/$aspect_ratio);
                }
                else if(preg_match('/^([0-9]+):([0-9]+)$/', $aspect_ratio, $matches) > 0)
                {
                    $this->setVideoAspectRatio($matches[2].':'.$matches[1]);
                }
            }

//          if video padding has been set we might need to update the width/height dimensions of the pad filter
            if(empty($this->_format['video_padding']) === false)
            {
                $padding = $this->_format['video_padding'];
                if(empty($padding['width']) === true || empty($padding['height']) === true)
                {
                    $this->setVideoPadding($padding['_padding']['top'], $padding['_padding']['right'], $padding['_padding']['bottom'], $padding['_padding']['left'], $padding['width'], $padding['height'], $padding['colour']);
                }
            }
            
            // TODO expand the video_filters format data
            if(empty($this->_format['video_filters']) === false)
            {
                
            }

            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param VideoFilter $filter 
         * @return void
         */
        public function addVideoFilter(VideoFilter $filter)
        {
            $this->_blockSetOnInputFormat('video filter');
            
            $this->_setFilter('video_filters', $filter);
            
            return $this;
        }
        
        /**
         * Enables FFmpegs' two pass output encoding.
         * Two pass output encoding is typically has better output.
         *
         * @access public
         * @author Oliver Lillie
         * @return void
         */
        public function enableTwoPassEncoding()
        {
            
        }

        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @return void
         */
        public function disableVideo()
        {
            if($this->_type === 'input')
            {
                throw new \LogicException('Video cannot be disabled on an input '.get_class($this).'.');
            }
            
            $this->_format['disable_video'] = true;
            
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @return void
         */
        public function enableVideo()
        {
            $this->_format['disable_video'] = false;
            
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $video_codec 
         * @return void
         */
        public function setVideoCodec($video_codec)
        {
            $this->_blockSetOnInputFormat('video codec');
            
            if($video_codec === null)
            {
                $this->_format['video_codec'] = null;
                return $this;
            }
            
//          get codecs and add special case for copy as it is not included in the codec list but is valid
            $codecs = $this->getCodecs('video');
            $codecs['copy'] = 1;
            
//          work around for h264/libx264 codec names. Thanks to Jorrit Schippers for this one.
            if(in_array($video_codec, array('h264', 'libx264')) === true)
            {
                $video_codec = isset($codecs['libx264']) === true ? 'libx264' : 'h264';
            }
//          work around for theora/libtheora names
            else if(in_array($video_codec, array('theora', 'libtheora')) === true)
            {
                $video_codec = isset($codecs['libtheora']) === true ? 'libtheora' : 'theora';
            }
//          work around for vp8/libvpx names
            else if(in_array($video_codec, array('vp8', 'libvpx')) === true)
            {
                $video_codec = isset($codecs['libvpx']) === true ? 'libvpx' : 'vp8';
            }
            
//          validate the video codecs that are available from ffmpeg.
            if(isset($codecs[$video_codec]) === false)
            {
                throw new \InvalidArgumentException('Unrecognised video codec "'.$video_codec.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoCodec');
            }
            
//          now check the class settings to see if restricted codecs have been set and have to be obeys
            if($this->_restricted_video_codecs !== null)
            {
                if(in_array($video_codec, $this->_restricted_video_codecs) === false)
                {
                    throw new \LogicException('The video codec "'.$video_codec.'" cannot be set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoCodec. Please select one of the following codecs: '.implode(', ', $this->_restricted_video_codecs));
                }
            }
            
            $this->_format['video_codec'] = $video_codec;
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $width 
         * @param string $height 
         * @param string $auto_adjust_dimensions_to_optimal 
         * @param string $force_aspect_ratio 
         * @return void
         */
        public function setVideoDimensions($width, $height=null, $auto_adjust_dimensions_to_optimal=false, $force_aspect_ratio=false)
        {
            if($width === null)
            {
                $this->_format['video_dimensions'] = null;
                return $this;
            }
            
            if($height === null)
            {
                if(in_array($width, array(self::DIMENSION_SAME_AS_SOURCE, self::DIMENSION_SQCIF, self::DIMENSION_QCIF, self::DIMENSION_CIF, self::DIMENSION_4CIF, self::DIMENSION_QQVGA, self::DIMENSION_QVGA, self::DIMENSION_VGA, self::DIMENSION_SVGA, self::DIMENSION_XGA, self::DIMENSION_UXGA, self::DIMENSION_QXGA, self::DIMENSION_SXGA, self::DIMENSION_QSXGA, self::DIMENSION_HSXGA, self::DIMENSION_WVGA, self::DIMENSION_WXGA, self::DIMENSION_WSXGA, self::DIMENSION_WUXGA, self::DIMENSION_WOXGA, self::DIMENSION_WQSXGA, self::DIMENSION_WQUXGA, self::DIMENSION_WHSXGA, self::DIMENSION_WHUXGA, self::DIMENSION_CGA, self::DIMENSION_EGA, self::DIMENSION_HD480, self::DIMENSION_HD720, self::DIMENSION_HD1080)) === false)
                {
                    throw new Exception();
                }
                
//              if we have the same as source dimensions...
                if($width === self::DIMENSION_SAME_AS_SOURCE)
                {
                    $this->_format['video_dimensions'] = 'sas';
                    return $this;
                }
                else
                {
                    $parts = explode('x', $width);
                    $width = (int) $parts[0];
                    $height = (int) $parts[1];
                }
            }
            else 
            {
                $width = (int) $width;
                $height = (int) $height;
            }
            
            if(empty($width) === true || $width <= 0)
            {
                throw new Exception('Unrecognised width dimension "'.$width.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoDimensions');
            }
            if(empty($height) === true || $height <= 0)
            {
                throw new Exception('Unrecognised height dimension "'.$height.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoDimensions');
            }
            
            $this->_format['video_dimensions'] = array(
                'width' => $width,
                'height' => $height,
                'auto_adjust_dimensions' => $auto_adjust_dimensions_to_optimal,
                'force_aspect' => $force_aspect_ratio,
            );
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $width 
         * @param string $height 
         * @return void
         */
        public function setVideoScale($width, $height)
        {
            $this->_blockSetOnInputFormat('video scale');
            
            if($width === null)
            {
                $this->_format['video_scale'] = null;
                return $this;
            }
            
            if($width <= 0)
            {
                throw new Exception('Unrecognised width dimension "'.$width.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoScale');
            }
            if($height <= 0)
            {
                throw new Exception('Unrecognised height dimension "'.$height.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoScale');
            }
            
            $this->_format['video_scale'] = array(
                'width' => $width,
                'height' => $height,
            );
            return $this;
        }
        
        // ERROR(s) 
        // Input area 0:8:176:152 not within the padded area 0:0:192:144 or zero-sized
        /**
         * WARNING! if you are segmenting or spliting the file, adding padding can and will take an extrordinaty amount
         * of time. You would be better of first segmenting/spliting to new files and then add padding to each segment.
         *
         * @access public
         * @author Oliver Lillie
         * @param string $top 
         * @param string $right 
         * @param string $bottom 
         * @param string $left 
         * @param string $width 
         * @param string $height 
         * @param string $colour 
         * @return void
         */
        public function setVideoPadding($top, $right, $bottom, $left, $width=null, $height=null, $colour='black')
        {
            $this->_blockSetOnInputFormat('video padding');
            
            if($top === null)
            {
                $this->_format['video_padding'] = null;
                return $this;
            }
            
//          if width or heights haven't been supplied then...
            if($width === null || $height === null)
            {
//              ..either get the dimension data from the format option
                if(empty($this->_format['video_dimensions']) === false)
                {
                    $width = $width === null ? $this->_format['video_dimensions']['width'] : $width;
                    $height = $height === null ? $this->_format['video_dimensions']['height'] : $height;
                }
//              ...or if the media object has been set get the dimensions of the media object.
                else if(empty($this->_media_object) === false)
                {
                    $dimensions = $this->_media_object->readDimensions();
                    if(empty($dimensions) === false)
                    {
                        $width = $width === null ? $dimensions['width'] : $width;
                        $height = $height === null ? $dimensions['height'] : $height;
                    }
                }
            }
            
            $this->_format['video_padding'] = array(
                '_padding' => array(
                    'top' => $top,
                    'right' => $right,
                    'bottom' => $bottom,
                    'left' => $left,
                ),
                'width' => $width === null ? null : $width+$right+$left,
                'height' => $height === null ? null : $height+$top+$bottom,
                'x' => $left,
                'y' => $top,
                'colour' => $colour,
            );
            
//          if width and height is set then we must use the scale filter instead of dimensions
            if($width !== null && $height !== null)
            {
                $this->setVideoDimensions(null);
                $this->setVideoScale($width, $height);
            }
            
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $aspect_ratio 
         * @param string $auto_adjust_dimensions 
         * @return void
         */
        public function setVideoAspectRatio($aspect_ratio, $auto_adjust_dimensions=false)
        {
            $this->_blockSetOnInputFormat('video aspect ratio');
            
            if($aspect_ratio === null)
            {
                $this->_format['video_aspect_ratio'] = null;
                return $this;
            }
            
            if(preg_match('/^[0-9]+.[0-9]+$/', $aspect_ratio) === 0 && preg_match('/^[0-9]+:[0-9]+$/', $aspect_ratio) === 0)
            {
                throw new Exception('Unrecognised aspect ratio "'.$aspect_ratio.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoAspectRatio');
            }
            
            $this->_format['video_aspect_ratio'] = array(
                'ratio' => $aspect_ratio,
                'auto_adjust_dimensions' => $auto_adjust_dimensions,
            );
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $frame_rate 
         * @return void
         */
        public function setVideoFrameRate($frame_rate)
        {
            // PEG1/2 does not support 5/1 fps
            if($frame_rate === null)
            {
                $this->_format['video_frame_rate'] = null;
                return $this;
            }
            
            if($frame_rate < 1)
            {
                throw new Exception('Unrecognised frame rate "'.$frame_rate.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoFrameRate');
            }
            else if(is_int($frame_rate) === false && is_float($frame_rate) === false && (is_string($frame_rate) === true && preg_match('/[0-9]+\/[0-9]+/', $frame_rate) === 0))
            {
                throw new Exception('If setting a frame rate please make sure it is either an integer, a float or a string in the "1/n" format (i.e. 1/60 = 1 frame every 60 seconds).');
            }
            
//          now check the class settings to see if restricted codecs have been set and have to be obeys
            if($this->_restricted_video_frame_rates !== null)
            {
                if(in_array($frame_rate, $this->_restricted_video_frame_rates) === false)
                {
                    throw new Exception('The frame rate "'.$frame_rate.'" cannot be set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoFrameRate. Please select one of the following frame rates: '.implode(', ', $this->_restricted_video_frame_rates));
                }
            }
            
            $this->_format['video_frame_rate'] = $frame_rate;
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $max_frame_count 
         * @return void
         */
        public function setVideoMaxFrames($max_frame_count)
        {
            $this->_blockSetOnInputFormat('video max frames');
            
            // PEG1/2 does not support 5/1 fps
            if($max_frame_count === null)
            {
                $this->_format['video_max_frames'] = null;
                return $this;
            }
            
            if($max_frame_count < 1)
            {
                throw new Exception('Unrecognised max frame count "'.$max_frame_count.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoFrameRate');
            }
            
            $this->_format['video_max_frames'] = $max_frame_count;
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $bitrate 
         * @return void
         */
        public function setVideoBitrate($bitrate)
        {
            $this->_blockSetOnInputFormat('video bitrate');
            
            if($bitrate === null)
            {
                $this->_format['video_bitrate'] = null;
                return $this;
            }
            
//          expand out any short hand
            if(preg_match('/^[0-9]+k$/', $bitrate) > 0)
            {
                // TODO make this exapnd out the kbs values
            }
            
//          now check the class settings to see if restricted codecs have been set and have to be obeys
            if($this->_restricted_video_bitrates !== null)
            {
                if(in_array($bitrate, $this->_restricted_video_bitrates) === false)
                {
                    throw new Exception('The bitrate "'.$bitrate.'" cannot be set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoBitrate. Please select one of the following bitrates: '.implode(', ', $this->_restricted_video_bitrates));
                }
            }
            
            $this->_format['video_bitrate'] = $bitrate;
            $this->setQualityVsStreamabilityBalanceRatio(NULL);
            return $this;
            
            //throw new Exception('Unrecognised video bitrate "'.$bitrate.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoBitrate');
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $pixel_format 
         * @return void
         */
        public function setVideoPixelFormat($pixel_format)
        {
            if($pixel_format === null)
            {
                $this->_format['video_pixel_format'] = null;
                return $this;
            }
            
//          now check the class settings to see if restricted pixel formats have been set and have to be obeyed
            if($this->_restricted_video_pixel_formats !== null)
            {
                if(in_array($pixel_format, $this->_restricted_video_pixel_formats) === false)
                {
                    throw new Exception('The video pixel format "'.$pixel_format.'" cannot be set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoPixelFormat. Please select one of the following pixel formats: '.implode(', ', $this->_restricted_video_pixel_formats));
                }
            }
            
            $valid_pixel_formats = $this->getPixelFormats();
            if(in_array($pixel_format, array_keys($valid_pixel_formats)) === true)
            {
                $this->_format['video_pixel_format'] = $pixel_format;
                return $this;
            }
            
            throw new Exception('Unrecognised pixel format "'.$pixel_format.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoPixelFormat');
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $quality 
         * @return void
         */
        public function setVideoQuality($quality)
        {
            $this->_blockSetOnInputFormat('video quality');
            
            if($quality === null)
            {
                $this->_format['video_quality'] = null;
                return $this;
            }
            
//          interpret quality into ffmpeg value
            $quality = 32 - round(($quality * 30/99) + 1);
            if($quality > 31 || $quality < 1)
            {
                throw new Exception('Unrecognised quality "'.$quality.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setQuality');
            }
            
            $this->_format['video_quality'] = $quality;
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @param string $rotation 
         * @return void
         */
        public function setVideoRotation($rotation)
        {
            $this->_blockSetOnInputFormat('video rotation');
            
            if(in_array('transpose', $this->getFilters()) === false)
            {
                throw new Exception('Unable to rotate the video as your version of ffmpeg does not support the transpose video filter.');
            }
            
            if($rotation === null)
            {
                $this->_format['video_rotation'] = null;
                return $this;
            }
            
//          if true is set we will attmempt to auto rotate the video according to meta data.
//          the meta data will then be removed from the outputted video.
            if($rotation === true)
            {
                $this->_format['video_rotation'] = true;
                return $this;
            }
            
//          otherwise accept only the following integers.
//          90 is the same as -270, etc.
            if(in_array($rotation, array(0, 90, 180, 270, -90, -270, -180)) === false)
            {
                throw new Exception('Unrecognised rotation "'.$rotation.'" set in \\PHPVideoToolkit\\'.get_class($this).'::setVideoRotation');
            }
            
//          get the transpose code. Note that we can't transpose 180 degrees, for that we must perform flips
            $transpose = $rotation === 90 || $rotation === -270 ? 1 : ($rotation === 270 || $rotation === -90 ? 2 : 'flip');
            if($transpose === 'flip')
            {
                $this->_format['video_rotation'] = null;
                $this->videoFlipVertical();
                $this->videoFlipHorizontal();
            }
            else
            {
                $this->_format['video_rotation'] = $transpose;
            }
            
            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @return void
         */
        public function videoFlipVertical()
        {
            $this->_blockSetOnInputFormat('video rotation');
            
            if(empty($this->_format['video_flip_vertical']) === false)
            {
                $this->_format['video_flip_vertical'] = null;
            }
            else
            {
                $this->_format['video_flip_vertical'] = true;
            }

            return $this;
        }
        
        /**
         * undocumented function
         *
         * @access public
         * @author Oliver Lillie
         * @return void
         */
        public function videoFlipHorizontal()
        {
            $this->_blockSetOnInputFormat('video rotation');
            
            if(empty($this->_format['video_flip_horizontal']) === false)
            {
                $this->_format['video_flip_horizontal'] = null;
            }
            else
            {
                $this->_format['video_flip_horizontal'] = true;
            }

            return $this;
        }
        
    }
