<?php
/**
 * core.framework
 *
 * @category  Core
 * @package   Core_Validate
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */

/**
 * @category   Core
 * @package    Core_Validate
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */
class Core_Validate_Creditcard extends Core_Validate_Abstract
{
    // known card types
    const TYPE_AMERICAN_EXPRESS = 'American Express';
    const TYPE_DINERS           = 'Diners';
    const TYPE_MAESTRO          = 'Maestro';
    const TYPE_MASTERCARD       = 'MasterCard';
    const TYPE_VISA             = 'Visa';

    /**
     * @var array
     */
    protected $_types = array(
        self::TYPE_AMERICAN_EXPRESS => array(
            'lengths'  => array(15),
            'prefixes' => array('34', '37'),
        ),
        self::TYPE_DINERS => array(
            'lengths'  => array(14),
            'prefixes' => array('300', '301', '302', '303', '304', '305', '36'),
        ),
        self::TYPE_MAESTRO => array(
            'lengths'  => array(12, 13, 14, 15, 16, 17, 18, 19),
            'prefixes' => array('5018', '5020', '5038', '6304', '6759', '6761', '6763'),
        ),
        self::TYPE_MASTERCARD => array(
            'lengths'  => array(16),
            'prefixes' => array('51', '52', '53', '54', '55'),
        ),
        self::TYPE_VISA => array(
            'lengths'  => array(16),
            'prefixes' => array('4'),
        ),
    );

    /**
     * @var array
     */
    protected $_validTypes;

    /**
     * @var boolean|string
     */
    protected $_currentType = false;

    /**
     * @var string
     */
    protected $_defaultErrorMessage = 'Vrijednost nije valjani broj kreditne kartice';

    /**
     * @param array $validTypes
     *
     * @return \Core_Validate_Creditcard
     */
    public function setValidTypes(array $validTypes)
    {
        foreach ($validTypes as $validType) {
            $this->addValidType($validType);
        }
        return $this;
    }

    /**
     * @return array
     */
    public function getValidTypes()
    {
        if (null === $this->_validTypes) {
            return array_keys($this->_types);
        }
        return $this->_validTypes;
    }

    /**
     * @param type $validType
     *
     * @return \Core_Validate_Creditcard
     * @throws InvalidArgumentException
     */
    public function addValidType($validType)
    {
        if (!array_key_exists($validType, $this->_types)) {
            $message = sprintf('Failed addding valid credit card type %s, unknown card type', $validType);
            throw new InvalidArgumentException($message);
        }

        $this->_validTypes[] = $validType;
        return $this;
    }

    /**
     * @return string
     *
     * @throws RuntimeException
     */
    public function getCurrentType()
    {
        if (false === $this->_currentType) {
            $message = 'Failed fetching current credit card type, no valid card number was validated';
            throw new RuntimeException($message);
        }
        return $this->_currentType;
    }

    /**
     * @param mixed $value
     *
     * @return boolean
     */
    protected function _isValid($value)
    {
        $length             = strlen($value);
        $this->_currentType = false;
        foreach($this->getValidTypes() as $validType) {
            $card = $this->_types[$validType];
            if (in_array($length, $card['lengths'])) {
                // if the length matches and the prefix matches, we found our card type
                foreach($card['prefixes'] as $prefix) {
                    if (0 === strpos($value, $prefix)) {
                        $this->_currentType = $validType;
                        break 2;
                    }
                }
            }
        }
        if (false === $this->_currentType) {
            return false;
        }

        $sum    = 0;
        $weight = 2;
        for ($i = $length - 2; $i >= 0; $i--) {
            $digit = $weight * $value[$i];
            $sum += floor($digit / 10) + $digit % 10;
            $weight = $weight % 2 + 1;
        }

        if ((10 - $sum % 10) % 10 != $value[$length - 1]) {
            $this->_currentType = false;
            return false;
        }

        return true;
    }
}
