<?php

class CronValidator 
{      
    const MINUTE = 'minute';
    const HOUR = 'hour';
    const DAY = 'day';
    const MONTH = 'month';
    const DAYOFWEEK = 'dayOfWeek';
    
    public static function cronCanStart($cronRule)
    {
        if ($cronRule == '') {
            return false;
        }

        $explodeRule = explode(' ', $cronRule);
        if (count($explodeRule) != 5) {
            Log::e("Cron-rule '$cronRule' is not valid!", 'CronController');
            return false;
        }

        $currentServerDateTime = PhonalisaDate::newServerDate();
        $currentMinute = ltrim(PhonalisaDate::convertServerDateTo('i', $currentServerDateTime), 0);
        $currentHour = PhonalisaDate::convertServerDateTo('G', $currentServerDateTime, true);
        $currentDay = PhonalisaDate::convertServerDateTo('j', $currentServerDateTime, true);
        $currentMonth = PhonalisaDate::convertServerDateTo('n', $currentServerDateTime);
        $currentDayOfWeek = PhonalisaDate::convertServerDateTo('w', $currentServerDateTime);

        $minute = CronValidator::compareCronWithCurrentTime($explodeRule[0], $currentMinute, self::MINUTE);
        $hour = CronValidator::compareCronWithCurrentTime($explodeRule[1], $currentHour, self::HOUR);
        $day = CronValidator::compareCronWithCurrentTime($explodeRule[2], $currentDay, self::DAY);
        $month = CronValidator::compareCronWithCurrentTime($explodeRule[3], $currentMonth, self::MONTH);
        $dayOfWeek = CronValidator::compareCronWithCurrentTime($explodeRule[4], $currentDayOfWeek, self::DAYOFWEEK);

        return ($minute && $hour && $day && $month && $dayOfWeek);
    }

    protected static function compareCronWithCurrentTime($value, $currentTime, $type)
    {
        $result = false;

        if (strpos($value, ',')) {
            $result = CronValidator::valueContainsList($value, $currentTime, $type);
        } else if (strpos($value, '-')) {
            $result = CronValidator::valueContainsRange($value, $currentTime, $type);
        } else if (strpos($value, '/')) {
            $result = CronValidator::valueContainsSlash($value, $currentTime, $type);
        } else if ($value === '*') {
            $result = true;
        } else {
            $result = CronValidator::valueIsInt($value, $currentTime, $type);
        }
        return $result;
    }

    protected static function valueContainsList($value, $currentTime, $type)
    {
        $explValue = explode(',', $value);
        if (!CronValidator::cronRuleIsValid($explValue, $type)) {
            return false;
        }
        return in_array($currentTime, $explValue);
    }

    protected static function valueContainsRange($value, $currentTime, $type)
    {
        $explValue = explode('-', $value);
        if (!CronValidator::cronRuleIsValid($explValue, $type)) {
            return false;
        }
        return in_array($currentTime, $explValue);
    }

    protected static function valueContainsSlash($value, $currentTime, $type)
    {
        $explValue = explode('/', $value);
        if (!CronValidator::cronRuleIsValid($explValue[1], $type)) {
            return false;
        }

        if ((int) $explValue[1] === 0) {
            return false;
        }

        $result = false;
        if ($currentTime % $explValue[1] === 0) {
            $result = true;
        }
        return $result;
    }

    protected static function valueIsInt($value, $currentTime, $type)
    {
        if (!is_numeric($value)) {
            Log::e("'$value' as '$type' is not a valid cron-rule!", 'CronController');
            return false;
        }

        if (strpos($value, '.')) {
            Log::e("'$value' as '$type' is not a valid cron-rule!", 'CronController');
            return false;
        }

        if (!CronValidator::cronRuleIsValid($value, $type)) {
            return false;
        }
        $result = false;
        if ((int) $value == (int) $currentTime) {
            $result = true;
        }
        return $result;
    }

    protected static function cronRuleIsValid($cronValue, $type)
    {
        $result = true;
        if (is_array($cronValue)) {
            foreach ($cronValue as $value) {
                if (!CronValidator::isValidByTypeAndRange($value, $type)) {
                    $result = false;
                    break;
                }
            }
        } else {
            if (!CronValidator::isValidByTypeAndRange($cronValue, $type)) {
                $result = false;
            }
        }
        return $result;
    }

    protected static function isValidByTypeAndRange($value, $type)
    {
        $result = false;
        if ($type == self::MINUTE) {
            if ($value >= 0 && $value <= 59) {
                $result = true;
            }
        } else if ($type == self::HOUR) {
            if ($value >= 0 && $value <= 23) {
                $result = true;
            }
        } else if ($type == self::DAY) {
            if ($value >= 0 && $value <= 31) {
                $result = true;
            }
        } else if ($type == self::MONTH) {
            if ($value >= 0 && $value <= 12) {
                $result = true;
            }
        } else if ($type == self::DAYOFWEEK) {
            if ($value >= 0 && $value <= 6) {
                $result = true;
            }
        }
        if (!$result) {
            Log::e("'$value' as '$type' is not a valid cron-rule!", 'CronController');
        }
        return $result;
    }
}