<?php
class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm
{
    private $criteriaOptions;
    private $stringCriteriaOptions;
    private $numericCriteriaOptions;
    private $dateTimeCriteriaOptions;
    private $timePeriodCriteriaOptions;
    private $sortOptions;
    private $limitOptions;
    private $isOrNotCriteriaOptions;
    private $trackTypeOptions;

    /* We need to know if the criteria value will be a string
     * or numeric value in order to populate the modifier
     * select list
     */
    private $criteriaTypes = array(
        0              => "",
        "album_title"  => "s",
        "bit_rate"     => "n",
        "bpm"          => "n",
        "composer"     => "s",
        "conductor"    => "s",
        "copyright"    => "s",
        "cuein"        => "n",
        "cueout"       => "n",
        "description"  => "s",
        "artist_name"  => "s",
        "encoded_by"   => "s",
        "utime"        => "d",
        "mtime"        => "d",
        "lptime"       => "d",
        "genre"        => "s",
        "isrc_number"  => "s",
        "label"        => "s",
        "language"     => "s",
        "length"       => "n",
        "mime"         => "s",
        "mood"         => "s",
        "owner_id"     => "s",
        "replay_gain"  => "n",
        "sample_rate"  => "n",
        "track_title"  => "s",
        "track_number" => "n",
        "info_url"     => "s",
        "year"         => "n",
        "track_type"   => "tt"
    );

    private function getCriteriaOptions($option = null)
    {
        if (!isset($this->criteriaOptions)) {
            $this->criteriaOptions = array(
                0              => _("Select criteria"),
                "album_title"  => _("Album"),
                "bit_rate"     => _("Bit Rate (Kbps)"),
                "bpm"          => _("BPM"),
                "composer"     => _("Composer"),
                "conductor"    => _("Conductor"),
                "copyright"    => _("Copyright"),
                "cuein"        => _("Cue In"),
                "cueout"       => _("Cue Out"),
                "description"  => _("Description"),
                "artist_name"  => _("Creator"),
                "encoded_by"   => _("Encoded By"),
                "genre"        => _("Genre"),
                "isrc_number"  => _("ISRC"),
                "label"        => _("Label"),
                "language"     => _("Language"),
                "mtime"        => _("Last Modified"),
                "lptime"       => _("Last Played"),
                "length"       => _("Length"),
                "track_type"   => _("Track Type"),
                "mime"         => _("Mime"),
                "mood"         => _("Mood"),
                "owner_id"     => _("Owner"),
                "replay_gain"  => _("Replay Gain"),
                "sample_rate"  => _("Sample Rate (kHz)"),
                "track_title"  => _("Title"),
                "track_number" => _("Track Number"),
                "utime"        => _("Uploaded"),
                "info_url"     => _("Website"),
                "year"         => _("Year")
            );
        }

        if (is_null($option)) return $this->criteriaOptions;
        else return $this->criteriaOptions[$option];
    }

    private function getStringCriteriaOptions()
    {
        if (!isset($this->stringCriteriaOptions)) {
            $this->stringCriteriaOptions = array(
                "0"                => _("Select modifier"),
                "contains"         => _("contains"),
                "does not contain" => _("does not contain"),
                "is"               => _("is"),
                "is not"           => _("is not"),
                "starts with"      => _("starts with"),
                "ends with"        => _("ends with")
            );
        }
        return $this->stringCriteriaOptions;
    }

    private function getNumericCriteriaOptions()
    {
        if (!isset($this->numericCriteriaOptions)) {
            $this->numericCriteriaOptions = array(
                "0"               => _("Select modifier"),
                "is"              => _("is"),
                "is not"          => _("is not"),
                "is greater than" => _("is greater than"),
                "is less than"    => _("is less than"),
                "is in the range" => _("is in the range")
            );
        }
        return $this->numericCriteriaOptions;
    }


    private function getDateTimeCriteriaOptions()
    {
        if (!isset($this->dateTimeCriteriaOptions)) {
            $this->dateTimeCriteriaOptions = array(
                "0"               => _("Select modifier"),
                "before"          => _("before"),
                "after"           => _("after"),
                "between"         => _("between"),
                "is"              => _("is"),
                "is not"          => _("is not"),
                "is greater than" => _("is greater than"),
                "is less than"    => _("is less than"),
                "is in the range" => _("is in the range")
            );
        }
        return $this->dateTimeCriteriaOptions;
    }


    private function getTimePeriodCriteriaOptions()
    {
        if (!isset($this->timePeriodCriteriaOptions)) {
            $this->timePeriodCriteriaOptions = array(
                "0"             => _("Select unit of time"),
                "minute"        => _("minute(s)"),
                "hour"          => _("hour(s)"),
                "day"           => _("day(s)"),
                "week"          => _("week(s)"),
                "month"         => _("month(s)"),
                "year"          => _("year(s)")
            );
        }
        return $this->timePeriodCriteriaOptions;
    }


    private function getLimitOptions()
    {
        if (!isset($this->limitOptions)) {
            $this->limitOptions = array(
                "hours"   => _("hours"),
                "minutes" => _("minutes"),
                "items"   => _("items"),
                "remaining" => _("time remaining in show")
            );
        }
        return $this->limitOptions;
    }
        private function getSortOptions()
    {
        if (!isset($this->sortOptions)) {
            $this->sortOptions = array(
                "random"   => _("Randomly"),
                "newest" => _("Newest"),
                "oldest"   => _("Oldest"),
                "mostrecentplay" => _("Most recently played"),
                "leastrecentplay" => _("Least recently played")
            );
        }
        return $this->sortOptions;
    }

    private function getIsNotOptions()
    {
        if (!isset($this->isOrNotCriteriaOptions)) {
            $this->isOrNotCriteriaOptions = array(
                "0"                => _("Select modifier"),
                "is"               => _("is"),
                "is not"           => _("is not")
            );
        }
        return $this->isOrNotCriteriaOptions;
    }

    private function getTracktypeOptions()
    {
        if (!isset($this->trackTypeOptions)) {
            $tracktypes = Application_Model_Tracktype::getTracktypes();
            $names[] = _("Select Track Type");
            foreach($tracktypes as $arr => $a){
                $names[$a["code"]] = $tracktypes[$arr]["type_name"];
            }
        }
        return $names;
    }

    public function init()
    {
    }

    /*
     * converts UTC timestamp citeria into user timezone strings.
     */
    private function convertTimestamps(&$criteria)
    {
    	$columns = array("utime", "mtime", "lptime");

    	foreach ($columns as $column) {

    		if (isset($criteria[$column])) {

    			foreach ($criteria[$column] as &$constraint) {
    			    // convert to appropriate timezone timestamps only if the modifier is not a relative time
                    if (!in_array($constraint['modifier'], array('before','after','between'))) {
                        $constraint['value'] =
                            Application_Common_DateHelper::UTCStringToUserTimezoneString($constraint['value']);
    				    if (isset($constraint['extra'])) {
    				  	  $constraint['extra'] =
    				  	  Application_Common_DateHelper::UTCStringToUserTimezoneString($constraint['extra']);
    				    }
                    }
    			}
    		}
    	}
    }

    /*
     * This function takes a blockID as param and creates the data structure for the form displayed with the view
     * smart-block-criteria.phtml
     *
     * A description of the dataflow. First it loads the block and determines if it is a static or dynamic smartblock.
     * Next it adds a radio selector for static or dynamic type.
     * Then it loads the criteria via the getCriteria() function, which returns an array for each criteria.
     *
     *
     */


    public function startForm($p_blockId, $p_isValid = false)
    {
        // load type
        $out = CcBlockQuery::create()->findPk($p_blockId);
        if ($out->getDbType() == "dynamic") {
            $blockType = 0;
        } else {
            $blockType = 1;
        }

        $spType = new Zend_Form_Element_Radio('sp_type');
        $spType->setLabel(_('Type:'))
               ->setDecorators(array('viewHelper'))
               ->setMultiOptions(array(
                    'dynamic' => _('Dynamic'),
                    'static' => _('Static')

                ))
               ->setValue($blockType);
        $this->addElement($spType);

        $bl = new Application_Model_Block($p_blockId);
        $storedCrit = $bl->getCriteriaGrouped();
        Logging::info($storedCrit);

        //need to convert criteria to be displayed in the user's timezone if there's some timestamp type.
        self::convertTimestamps($storedCrit["crit"]);

        /* $modRoadMap stores the number of same criteria
         * Ex: 3 Album titles, and 2 Track titles
         * We need to know this so we display the form elements properly
         */
        $modRowMap = array();

        $openSmartBlockOption = false;
        if (!empty($storedCrit)) {
            $openSmartBlockOption = true;
        }

        // this returns a number indexed array for each criteria found in the database
        $criteriaKeys = array();
        if (isset($storedCrit["crit"])) {
            $criteriaKeys = array_keys($storedCrit["crit"]);
        }
        //the way the everything is currently built it setups 25 smartblock criteria forms and then disables them
        //but this creates 29 elements
        $numElements = count($this->getCriteriaOptions());
        // loop through once for each potential criteria option ie album, composer, track
        // criteria from different groups are separated already by the getCriteriaGrouped call

        for ($i = 0; $i < $numElements; $i++) {
            $criteriaType = "";

            // if there is a criteria found then count the number of rows for this specific criteria ie > 1 track title
            // need to refactor this to maintain separation based upon criteria grouping
            if (isset($criteriaKeys[$i])) {
                //Logging::info($criteriaKeys[$i]);
                Logging::info($storedCrit["crit"][$criteriaKeys[$i]]);
                $critCount = count($storedCrit["crit"][$criteriaKeys[$i]]);
            } else {
                $critCount = 1;
            }
                // the challenge is that we need to increment the element for a new group
                // within the same criteria but not the reference point i in the array
                // and for these secondary groups they will have a differe$storedCrit["crit"][$criteriaKeys[$i]]nt j reference point
                // store the number of items with the same key in the ModRowMap
                $modRowMap[$i] = $critCount;

                /* Loop through all criteria with the same field
                 * Ex: all criteria for 'Album'
                 */
                for ($j = 0; $j < $critCount; $j++) {
                    /****************** CRITERIA ***********/
                    // hide the criteria drop down select on any rows after the first
                    if ($j > 0) {
                        $invisible = ' sp-invisible';
                    } else {
                        $invisible = '';
                    }

                    $criteria = new Zend_Form_Element_Select("sp_criteria_field_" . $i . "_" . $j);
                    $criteria->setAttrib('class', 'input_select sp_input_select' . $invisible)
                        ->setValue('Select criteria')
                        ->setDecorators(array('viewHelper'))
                        ->setMultiOptions($this->getCriteriaOptions());
                    // if this isn't the first criteria and there isn't an entry for it already disable it
                    if ($i != 0 && !isset($criteriaKeys[$i])) {
                        $criteria->setAttrib('disabled', 'disabled');
                    }
                    // add the numbering to the form ie the i loop for each specific criteria and
                    // the j loop starts at 0 and grows for each item matching the same criteria
                    // look up the criteria type using the criteriaTypes function from above based upon the criteria value
                    if (isset($criteriaKeys[$i])) {
                        $criteriaType = $this->criteriaTypes[$storedCrit["crit"][$criteriaKeys[$i]][$j]["criteria"]];
                        $criteria->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["criteria"]);
                    }
                    $this->addElement($criteria);


                    /****************** MODIFIER ***********/
                    // every element has an optional modifier dropdown select

                    $criteriaModifers = new Zend_Form_Element_Select("sp_criteria_modifier_" . $i . "_" . $j);
                    $criteriaModifers->setValue('Select modifier')
                        ->setAttrib('class', 'input_select sp_input_select')
                        ->setDecorators(array('viewHelper'));
                    if ($i != 0 && !isset($criteriaKeys[$i])) {
                        $criteriaModifers->setAttrib('disabled', 'disabled');
                    }
                    // determine the modifier based upon criteria type which is looked up based upon an array
                    if (isset($criteriaKeys[$i])) {
                        if ($criteriaType == "s") {
                            $criteriaModifers->setMultiOptions($this->getStringCriteriaOptions());
                        } elseif ($criteriaType == "d") {
                            $criteriaModifers->setMultiOptions($this->getDateTimeCriteriaOptions());
                        } elseif ($criteriaType == "tt") {
                            $criteriaModifers->setMultiOptions($this->getIsNotOptions());
                        } else {
                            $criteriaModifers->setMultiOptions($this->getNumericCriteriaOptions());
                        }
                        $criteriaModifers->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["modifier"]);
                    } else {
                        $criteriaModifers->setMultiOptions(array('0' => _('Select modifier')));
                    }
                    $this->addElement($criteriaModifers);

                    /****************** VALUE ***********/
                    /* The challenge here is that datetime */
                    if (isset($criteriaKeys[$i])) {
                      $modifierTest = (string)$storedCrit["crit"][$criteriaKeys[$i]][$j]["modifier"];
                      if (isset($criteriaType) && $criteriaType == "tt" &&
                          preg_match('/is|is not/', $modifierTest) == 1) {

                            $criteriaValue = new Zend_Form_Element_Select("sp_criteria_value_" . $i . "_" . $j);
                            $criteriaValue->setAttrib('class', 'input_select sp_input_select')->setDecorators(array('viewHelper'));

                            if (isset($criteriaKeys[$i]) ) {  //do if $relativeTT above
                                $criteriaValue->setAttrib('enabled', 'enabled');
                            } else {
                                $criteriaValue->setAttrib('disabled', 'disabled');
                            }
                      } else {
                            $criteriaValue = new Zend_Form_Element_Text("sp_criteria_value_" . $i . "_" . $j);
                            $criteriaValue->setAttrib('class', 'input_text sp_input_text')->setDecorators(array('viewHelper'));
                            if ($i != 0 && !isset($criteriaKeys[$i])) {
                                $criteriaValue->setAttrib('disabled', 'disabled');
                            }
                      }
                    } else {

                      $criteriaValue = new Zend_Form_Element_Text("sp_criteria_value_" . $i . "_" . $j);
                      $criteriaValue->setAttrib('class', 'input_text sp_input_text')->setDecorators(array('viewHelper'));
                      if ($i != 0 && !isset($criteriaKeys[$i])) {
                          $criteriaValue->setAttrib('disabled', 'disabled');
                      }
                    }
                    if (isset($criteriaKeys[$i])) {
                        /*
                         * Need to parse relative dates in a special way to populate select box down below
                         */
                        // this is used below to test whether the datetime select should be shown or hidden
                        $relativeDateTime = false;
                        $modifierTest = (string)$storedCrit["crit"][$criteriaKeys[$i]][$j]["modifier"];
                        if (isset($criteriaType) && $criteriaType == "d" &&
                            preg_match('/before|after|between/', $modifierTest) == 1) {
                            // set relativeDatetime boolean to true so that the datetime select is displayed below
                            $relativeDateTime = true;
                            $criteriaValue->setValue(filter_var($storedCrit["crit"][$criteriaKeys[$i]][$j]["value"], FILTER_SANITIZE_NUMBER_INT));
                        } elseif (isset($criteriaType) && $criteriaType == "tt" &&
                            preg_match('/is|is not/', $modifierTest) == 1) {
                            // set relativeDatetime boolean to true so that the datetime select is displayed below
                            $relativeDateTime = false;

                            $tracktypeSelectValue = preg_replace('/[0-9]+/', '', $storedCrit["crit"][$criteriaKeys[$i]][$j]["value"]);
                            $tracktypeSelectValue = trim(preg_replace('/\W\w+\s*(\W*)$/', '$1', $tracktypeSelectValue));
                            $criteriaValue->setMultiOptions($this->getTracktypeOptions());

                            if ($storedCrit["crit"][$criteriaKeys[$i]][$j]["value"]){
                                $criteriaValue->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["value"]);
                            } else {
                                $criteriaValue->setMultiOptions(array('0' => _('Select track type')));
                                $criteriaValue->setMultiOptions($this->getTracktypeOptions());
                                $criteriaValue->setValue($tracktypeSelectValue);
                            }
                            $criteriaValue->setAttrib('enabled', 'enabled');
                        } else {
                            $criteriaValue->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["value"]);
                        }
                    }
                    $this->addElement($criteriaValue);


                    /****************** DATETIME SELECT *************/
                    $criteriaDatetimeSelect = new Zend_Form_Element_Select("sp_criteria_datetime_select_" . $i . "_" . $j);
                    $criteriaDatetimeSelect->setAttrib('class', 'input_select sp_input_select')
                        ->setDecorators(array('viewHelper'));
                    if (isset($criteriaKeys[$i]) && $relativeDateTime) {
                        $criteriaDatetimeSelect->setAttrib('enabled', 'enabled');
                    } else {
                        $criteriaDatetimeSelect->setAttrib('disabled', 'disabled');
                    }
                    // check if the value is stored and it is a relative datetime field
                    if (isset($criteriaKeys[$i]) && isset($storedCrit["crit"][$criteriaKeys[$i]][$j]["value"])
                        && isset($criteriaType) && $criteriaType == "d" &&
                        preg_match('/before|after|between/', $modifierTest) == 1) {
                        // need to remove any leading numbers stored in the database
                        $dateTimeSelectValue = preg_replace('/[0-9]+/', '', $storedCrit["crit"][$criteriaKeys[$i]][$j]["value"]);
                        // need to strip white from front and ago from the end to match with the value of the time unit select dropdown
                        $dateTimeSelectValue = trim(preg_replace('/\W\w+\s*(\W*)$/', '$1', $dateTimeSelectValue));
                        $criteriaDatetimeSelect->setMultiOptions($this->getTimePeriodCriteriaOptions());
                        $criteriaDatetimeSelect->setValue($dateTimeSelectValue);
                        $criteriaDatetimeSelect->setAttrib('enabled', 'enabled');
                    } else {
                        $criteriaDatetimeSelect->setMultiOptions(array('0' => _('Select unit of time')));
                        $criteriaDatetimeSelect->setMultiOptions($this->getTimePeriodCriteriaOptions());

                    }

                    $this->addElement($criteriaDatetimeSelect);

                    /****************** EXTRA ***********/
                    $criteriaExtra = new Zend_Form_Element_Text("sp_criteria_extra_" . $i . "_" . $j);
                    $criteriaExtra->setAttrib('class', 'input_text sp_extra_input_text')
                        ->setDecorators(array('viewHelper'));
                    if (isset($criteriaKeys[$i]) && isset($storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"])) {
                        // need to check if this is a relative date time value
                        if (isset($criteriaType) && $criteriaType == "d" && $modifierTest == 'between') {
                            // the criteria value will be a number followed by time unit and ago so set input to number part
                            $criteriaExtra->setValue(filter_var($storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"], FILTER_SANITIZE_NUMBER_INT));
                        } else {
                            $criteriaExtra->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"]);
                        }
                        $criteriaValue->setAttrib('class', 'input_text sp_extra_input_text');
                    } else {
                        $criteriaExtra->setAttrib('disabled', 'disabled');
                    }
                    $this->addElement($criteriaExtra);
                    /****************** DATETIME SELECT EXTRA **********/

                    $criteriaExtraDatetimeSelect = new Zend_Form_Element_Select("sp_criteria_extra_datetime_select_" . $i . "_" . $j);
                    $criteriaExtraDatetimeSelect->setAttrib('class', 'input_select sp_input_select')
                        ->setDecorators(array('viewHelper'));

                    if (isset($criteriaKeys[$i]) && isset($storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"])
                        && $modifierTest == 'between') {
                        // need to remove the leading numbers stored in the database
                        $extraDateTimeSelectValue = preg_replace('/[0-9]+/', '', $storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"]);
                        // need to strip white from front and ago from the end to match with the value of the time unit select dropdown
                        $extraDateTimeSelectValue = trim(preg_replace('/\W\w+\s*(\W*)$/', '$1', $extraDateTimeSelectValue));
                        $criteriaExtraDatetimeSelect->setMultiOptions($this->getTimePeriodCriteriaOptions());
                        // Logging::info('THIS IS-'.$extraDateTimeSelectValue.'-IT');
                        $criteriaExtraDatetimeSelect->setValue($extraDateTimeSelectValue);
                        $criteriaExtraDatetimeSelect->setAttrib('enabled', 'enabled');

                    } else {
                        $criteriaExtraDatetimeSelect->setMultiOptions(array('0' => _('Select unit of time')));
                        $criteriaExtraDatetimeSelect->setMultiOptions($this->getTimePeriodCriteriaOptions());
                        $criteriaExtraDatetimeSelect->setAttrib('disabled', 'disabled');
                    }
                    $this->addElement($criteriaExtraDatetimeSelect);
                }//for
        }//for

        $repeatTracks = new Zend_Form_Element_Checkbox('sp_repeat_tracks');
        $repeatTracks->setDecorators(array('viewHelper'))
                     ->setLabel(_('Allow Repeated Tracks:'));
        if (isset($storedCrit["repeat_tracks"])) {
                $repeatTracks->setChecked($storedCrit["repeat_tracks"]["value"] == 1?true:false);
        }
        $this->addElement($repeatTracks);

        $overflowTracks = new Zend_Form_Element_Checkbox('sp_overflow_tracks');
        $overflowTracks->setDecorators(array('viewHelper'))
            ->setLabel(_('Allow last track to exceed time limit:'));
        if (isset($storedCrit["overflow_tracks"])) {
            $overflowTracks->setChecked($storedCrit["overflow_tracks"]["value"] == 1);
        }
        $this->addElement($overflowTracks);

        $sort = new Zend_Form_Element_Select('sp_sort_options');
        $sort->setAttrib('class', 'sp_input_select')
              ->setDecorators(array('viewHelper'))
              ->setLabel(_("Sort Tracks:"))
              ->setMultiOptions($this->getSortOptions());
        if (isset($storedCrit["sort"])) {
            $sort->setValue($storedCrit["sort"]["value"]);
        }
        $this->addElement($sort);

        $limit = new Zend_Form_Element_Select('sp_limit_options');
        $limit->setAttrib('class', 'sp_input_select')
              ->setDecorators(array('viewHelper'))
              ->setMultiOptions($this->getLimitOptions());
        if (isset($storedCrit["limit"])) {
            $limit->setValue($storedCrit["limit"]["modifier"]);
        }
        $this->addElement($limit);

        $limitValue = new Zend_Form_Element_Text('sp_limit_value');
        $limitValue->setAttrib('class', 'sp_input_text_limit')
                   ->setLabel(_('Limit to:'))
                   ->setDecorators(array('viewHelper'));
        $this->addElement($limitValue);
        if (isset($storedCrit["limit"])) {
            $limitValue->setValue($storedCrit["limit"]["value"]);
        } else {
            // setting default to 1 hour
            $limitValue->setValue(1);
        }


        $generate = new Zend_Form_Element_Button('generate_button');
        $generate->setAttrib('class', 'sp-button btn');
        $generate->setAttrib('title', _('Generate playlist content and save criteria'));
        $generate->setIgnore(true);
        if ($blockType == 0) {
            $generate->setLabel(_('Preview'));
        }
        else {
            $generate->setLabel(_('Generate'));
        }
        $generate->setDecorators(array('viewHelper'));
        $this->addElement($generate);

        $shuffle = new Zend_Form_Element_Button('shuffle_button');
        $shuffle->setAttrib('class', 'sp-button btn');
        $shuffle->setAttrib('title', _('Shuffle playlist content'));
        $shuffle->setIgnore(true);
        $shuffle->setLabel(_('Shuffle'));
        $shuffle->setDecorators(array('viewHelper'));
        $this->addElement($shuffle);

        $this->setDecorators(array(
                array('ViewScript', array('viewScript' => 'form/smart-block-criteria.phtml', "openOption"=> $openSmartBlockOption,
                        'criteriasLength' => $numElements, 'modRowMap' => $modRowMap))
        ));
    }
    /*
     * This is a simple function that determines if a modValue should enable a datetime
     */
    public function enableDateTimeUnit($modValue) {
        return (preg_match('/before|after|between/', $modValue) == 1);
    }

    public function preValidation($params)
    {
        $data = Application_Model_Block::organizeSmartPlaylistCriteria($params['data']);
        // add elements that needs to be added
        // set multioption for modifier according to criteria_field
        $modRowMap = array();
        if (!isset($data['criteria'])) {
            return $data;
        }

        foreach ($data['criteria'] as $critKey=>$d) {
            $count = 1;
            foreach ($d as $modKey=>$modInfo) {
                if ($modKey == 0) {
                    $eleCrit = $this->getElement("sp_criteria_field_".$critKey."_".$modKey);
                    $eleCrit->setValue($this->getCriteriaOptions($modInfo['sp_criteria_field']));
                    $eleCrit->setAttrib("disabled", null);

                    $eleMod = $this->getElement("sp_criteria_modifier_".$critKey."_".$modKey);
                    $criteriaType = $this->criteriaTypes[$modInfo['sp_criteria_field']];
                    if ($criteriaType == "s") {
                        $eleMod->setMultiOptions($this->getStringCriteriaOptions());
                    } elseif ($criteriaType == "n") {
                        $eleMod->setMultiOptions($this->getNumericCriteriaOptions());
                    } elseif ($criteriaType == "d") {
                        $eleMod->setMultiOptions($this->getDateTimeCriteriaOptions());
                    } elseif ($criteriaType == "tt") {
                        $eleMod->setMultiOptions($this->getIsNotOptions());
                    } else {
                        $eleMod->setMultiOptions(array('0' => _('Select modifier')));
                    }
                    $eleMod->setValue($modInfo['sp_criteria_modifier']);
                    $eleMod->setAttrib("disabled", null);

                    $eleDatetime = $this->getElement("sp_criteria_datetime_select_".$critKey."_".$modKey);
                    if ($this->enableDateTimeUnit($eleMod->getValue())) {
                        $eleDatetime->setAttrib("enabled", "enabled");
                        $eleDatetime->setValue($modInfo['sp_criteria_datetime_select']);
                        $eleDatetime->setAttrib("disabled", null);
                    }
                    else {
                        $eleDatetime->setAttrib("disabled","disabled");
                    }
                    $eleValue = $this->getElement("sp_criteria_value_".$critKey."_".$modKey);
                    $eleValue->setValue($modInfo['sp_criteria_value']);
                    $eleValue->setAttrib("disabled", null);

                    if (isset($modInfo['sp_criteria_extra'])) {
                        $eleExtra = $this->getElement("sp_criteria_extra_".$critKey."_".$modKey);
                        $eleExtra->setValue($modInfo['sp_criteria_extra']);
                        $eleValue->setAttrib('class', 'input_text sp_extra_input_text');
                        $eleExtra->setAttrib("disabled", null);
                    }
                    $eleExtraDatetime = $this->getElement("sp_criteria_extra_datetime_select_".$critKey."_".$modKey);
                    if ($eleMod->getValue() == 'between') {
                        $eleExtraDatetime->setAttrib("enabled", "enabled");
                        $eleExtraDatetime->setValue($modInfo['sp_criteria_extra_datetime_select']);
                        $eleExtraDatetime->setAttrib("disabled", null);
                    }
                    else {
                        $eleExtraDatetime->setAttrib("disabled","disabled");
                    }

                } else {
                    $criteria = new Zend_Form_Element_Select("sp_criteria_field_".$critKey."_".$modKey);
                    $criteria->setAttrib('class', 'input_select sp_input_select sp-invisible')
                    ->setValue('Select criteria')
                    ->setDecorators(array('viewHelper'))
                    ->setMultiOptions($this->getCriteriaOptions());

                    $criteriaType = $this->criteriaTypes[$modInfo['sp_criteria_field']];
                    $criteria->setValue($this->getCriteriaOptions($modInfo['sp_criteria_field']));
                    $this->addElement($criteria);

                    /****************** MODIFIER ***********/
                    $criteriaModifers = new Zend_Form_Element_Select("sp_criteria_modifier_".$critKey."_".$modKey);
                    $criteriaModifers->setValue('Select modifier')
                    ->setAttrib('class', 'input_select sp_input_select')
                    ->setDecorators(array('viewHelper'));

                    if ($criteriaType == "s") {
                        $criteriaModifers->setMultiOptions($this->getStringCriteriaOptions());
                    } elseif ($criteriaType == "n") {
                        $criteriaModifers->setMultiOptions($this->getNumericCriteriaOptions());
                    }
                      elseif ($criteriaType == "d") {
                          $criteriaModifers->setMultiOptions($this->getDateTimeCriteriaOptions());
                    } elseif ($criteriaType == "tt") {
                        $criteriaModifers->setMultiOptions($this->getIsNotOptions());
                    } else {
                        $criteriaModifers->setMultiOptions(array('0' => _('Select modifier')));
                    }
                    $criteriaModifers->setValue($modInfo['sp_criteria_modifier']);
                    $this->addElement($criteriaModifers);

                    /****************** VALUE ***********/
                    $criteriaValue = new Zend_Form_Element_Text("sp_criteria_value_".$critKey."_".$modKey);
                    $criteriaValue->setAttrib('class', 'input_text sp_input_text')
                    ->setDecorators(array('viewHelper'));
                    $criteriaValue->setValue($modInfo['sp_criteria_value']);
                    $this->addElement($criteriaValue);
                    /****************** DATETIME UNIT SELECT ***********/

                    $criteriaDatetimeSelect = new Zend_Form_Element_Select("sp_criteria_datetime_select_".$critKey."_".$modKey);
                    $criteriaDatetimeSelect->setAttrib('class','input_select sp_input_select')
                        ->setDecorators(array('viewHelper'));
                    if ($this->enableDateTimeUnit($criteriaValue->getValue())) {
                        $criteriaDatetimeSelect->setAttrib('enabled', 'enabled');
                        $criteriaDatetimeSelect->setAttrib('disabled', null);
                        $criteriaDatetimeSelect->setValue($modInfo['sp_criteria_datetime_select']);
                        $this->addElement($criteriaDatetimeSelect);
                    }
                    else {
                        $criteriaDatetimeSelect->setAttrib('disabled', 'disabled');
                    }
                    /****************** EXTRA ***********/
                    $criteriaExtra = new Zend_Form_Element_Text("sp_criteria_extra_".$critKey."_".$modKey);
                    $criteriaExtra->setAttrib('class', 'input_text sp_extra_input_text')
                    ->setDecorators(array('viewHelper'));
                    if (isset($modInfo['sp_criteria_extra'])) {
                        $criteriaExtra->setValue($modInfo['sp_criteria_extra']);
                        $criteriaValue->setAttrib('class', 'input_text sp_extra_input_text');
                    } else {
                        $criteriaExtra->setAttrib('disabled', 'disabled');
                    }
                    $this->addElement($criteriaExtra);

                    /****************** EXTRA DATETIME UNIT SELECT ***********/

                    $criteriaExtraDatetimeSelect = new Zend_Form_Element_Select("sp_criteria_extra_datetime_select_".$critKey."_".$modKey);
                    $criteriaExtraDatetimeSelect->setAttrib('class','input_select sp_input_select')
                        ->setDecorators(array('viewHelper'));
                    if ($criteriaValue->getValue() == 'between') {
                        $criteriaExtraDatetimeSelect->setAttrib('enabled', 'enabled');
                        $criteriaExtraDatetimeSelect->setAttrib('disabled', null);
                        $criteriaExtraDatetimeSelect->setValue($modInfo['sp_criteria_extra_datetime_select']);
                        $this->addElement($criteriaExtraDatetimeSelect);
                    }
                    else {
                        $criteriaExtraDatetimeSelect->setAttrib('disabled', 'disabled');
                    }
                    $count++;
                }
            }
            $modRowMap[$critKey] = $count;
        }

        $decorator = $this->getDecorator("ViewScript");
        $existingModRow = $decorator->getOption("modRowMap");
        foreach ($modRowMap as $key=>$v) {
            $existingModRow[$key] = $v;
        }
        $decorator->setOption("modRowMap", $existingModRow);

        // reconstruct the params['criteria'] so we can populate the form
        $formData = array();
        foreach ($params['data'] as $ele) {
            $formData[$ele['name']] = $ele['value'];
        }

        $this->populate($formData);

        // Logging::info($formData);
        return $data;
    }

    public function isValid($params)
    {
        $isValid = true;
        $data = $this->preValidation($params);
        $criteria2PeerMap = array(
            0 => "Select criteria",
            "album_title" => "DbAlbumTitle",
            "artist_name" => "DbArtistName",
            "bit_rate" => "DbBitRate",
            "bpm" => "DbBpm",
            "composer" => "DbComposer",
            "conductor" => "DbConductor",
            "copyright" => "DbCopyright",
            "cuein" => "DbCuein",
            "cueout" => "DbCueout",
            "description" => "DbDescription",
            "encoded_by" => "DbEncodedBy",
            "utime" => "DbUtime",
            "mtime" => "DbMtime",
            "lptime" => "DbLPtime",
            "genre" => "DbGenre",
            "info_url" => "DbInfoUrl",
            "isrc_number" => "DbIsrcNumber",
            "label" => "DbLabel",
            "language" => "DbLanguage",
            "length" => "DbLength",
            "mime" => "DbMime",
            "mood" => "DbMood",
            "owner_id" => "DbOwnerId",
            "replay_gain" => "DbReplayGain",
            "sample_rate" => "DbSampleRate",
            "track_title" => "DbTrackTitle",
            "track_number" => "DbTrackNumber",
            "year" => "DbYear",
            "track_type" => "DbTrackType"
        );

        // things we need to check
        // 1. limit value shouldn't be empty and has upperbound of 24 hrs
        // 2. sp_criteria or sp_criteria_modifier shouldn't be 0
        // 3. validate formate according to DB column type
        $multiplier = 1;
        $result = 0;

        // validation start
        if ($data['etc']['sp_limit_options'] == 'hours') {
            $multiplier = 60;
        }
        if ($data['etc']['sp_limit_options'] == 'hours' || $data['etc']['sp_limit_options'] == 'mins') {
            $element = $this->getElement("sp_limit_value");
            if ($data['etc']['sp_limit_value'] == "" || floatval($data['etc']['sp_limit_value']) <= 0) {
                $element->addError(_("Limit cannot be empty or smaller than 0"));
                $isValid = false;
            } else {
                $mins = floatval($data['etc']['sp_limit_value']) * $multiplier;
                if ($mins > 1440) {
                    $element->addError(_("Limit cannot be more than 24 hrs"));
                    $isValid = false;
                }
            }
        } else {
            $element = $this->getElement("sp_limit_value");
            if ($data['etc']['sp_limit_value'] == "" || floatval($data['etc']['sp_limit_value']) <= 0) {
                $element->addError(_("Limit cannot be empty or smaller than 0"));
                $isValid = false;
            } elseif (!ctype_digit($data['etc']['sp_limit_value'])) {
                $element->addError(_("The value should be an integer"));
                $isValid = false;
            } elseif (intval($data['etc']['sp_limit_value']) > 500) {
                $element->addError(_("500 is the max item limit value you can set"));
                $isValid = false;
            }
        }

        if (isset($data['criteria'])) {
            foreach ($data['criteria'] as $rowKey=>$row) {
                foreach ($row as $key=>$d) {
                    $element = $this->getElement("sp_criteria_field_".$rowKey."_".$key);
                    // check for not selected select box
                    if ($d['sp_criteria_field'] == "0" || $d['sp_criteria_modifier'] == "0") {
                        $element->addError(_("You must select Criteria and Modifier"));
                        $isValid = false;
                    } else {
                        $column = CcFilesPeer::getTableMap()->getColumnByPhpName($criteria2PeerMap[$d['sp_criteria_field']]);
                        // validation on type of column
                        if (in_array($d['sp_criteria_field'], array('length', 'cuein', 'cueout'))) {
                            if (!preg_match("/^(\d{2}):(\d{2}):(\d{2})/", $d['sp_criteria_value'])) {
                                $element->addError(_("'Length' should be in '00:00:00' format"));
                                $isValid = false;
                            }
                            // this looks up the column type for the criteria the modified time, upload time etc.
                        } elseif ($column->getType() == PropelColumnTypes::TIMESTAMP) {
                            // need to check for relative modifiers first - bypassing currently
                            if (in_array($d['sp_criteria_modifier'], array('before','after','between'))) {
                                if (!preg_match("/^[1-9][0-9]*$|0/",$d['sp_criteria_value'])) {
                                    $element->addError(_("Only non-negative integer numbers are allowed (e.g 1 or 5) for the text value"));
                                    $isValid = false;
                                    // TODO validate this on numeric input with whatever parsing also do for extra
                                    //if the modifier is before ago or between we skip validation until we confirm format
                                }
                                elseif (isSet($d['sp_criteria_datetime_select']) && $d['sp_criteria_datetime_select'] == "0") {
                                    $element->addError(_("You must select a time unit for a relative datetime."));
                                    $isValid = false;
                                }
                            } else {
                                if (!preg_match("/(\d{4})-(\d{2})-(\d{2})/", $d['sp_criteria_value'])) {
                                    $element->addError(_("The value should be in timestamp format (e.g. 0000-00-00 or 0000-00-00 00:00:00)"));
                                    $isValid = false;
                                } else {
                                    $result = Application_Common_DateHelper::checkDateTimeRangeForSQL($d['sp_criteria_value']);
                                    if (!$result["success"]) {
                                        // check for if it is in valid range( 1753-01-01 ~ 12/31/9999 )
                                        $element->addError($result["errMsg"]);
                                        $isValid = false;
                                    }
                                }
                            }
                            if (isset($d['sp_criteria_extra'])) {
                                if ($d['sp_criteria_modifier'] == 'between') {
                                    // validate that the input value only contains a number if using relative date times
                                    if (!preg_match("/^[1-9][0-9]*$|0/",$d['sp_criteria_extra'])) {
                                        $element->addError(_("Only non-negative integer numbers are allowed for a relative date time"));
                                        $isValid = false;
                                    }
                                    // also need to check to make sure they chose a time unit from the dropdown
                                    elseif ($d['sp_criteria_extra_datetime_select'] == "0") {
                                        $element->addError(_("You must select a time unit for a relative datetime."));
                                        $isValid = false;
                                    }
                                }
                                else {
                                    if (!preg_match("/(\d{4})-(\d{2})-(\d{2})/", $d['sp_criteria_extra'])) {
                                        $element->addError(_("The value should be in timestamp format (e.g. 0000-00-00 or 0000-00-00 00:00:00)"));
                                        $isValid = false;
                                    } else {
                                        $result = Application_Common_DateHelper::checkDateTimeRangeForSQL($d['sp_criteria_extra']);
                                        if (!$result["success"]) {
                                            // check for if it is in valid range( 1753-01-01 ~ 12/31/9999 )
                                            $element->addError($result["errMsg"]);
                                            $isValid = false;
                                        }
                                    }
                                }
                            }
                        } elseif ($column->getType() == PropelColumnTypes::INTEGER &&
                            $d['sp_criteria_field'] != 'owner_id') {
                            if (!is_numeric($d['sp_criteria_value'])) {
                                $element->addError(_("The value has to be numeric"));
                                $isValid = false;
                            }
                            // length check
                            if ($d['sp_criteria_value'] >= pow(2,31)) {
                                $element->addError(_("The value should be less then 2147483648"));
                                $isValid = false;
                            }
                        } elseif ($column->getType() == PropelColumnTypes::VARCHAR) {
                            if (strlen($d['sp_criteria_value']) > $column->getSize()) {
                                $element->addError(sprintf(_("The value should be less than %s characters"), $column->getSize()));
                                $isValid = false;
                            }
                        }
                    }

                    if ($d['sp_criteria_value'] == "") {
                        $element->addError(_("Value cannot be empty"));
                        $isValid = false;
                    }
                }//end foreach
            }//for loop
        }//if

        return $isValid;
    }
}
