<?php
class Ie_Validate{

    const c0_9 = '0-9';

    const validUFs = [
        'AC','AL','AP','AM','BA','CE','DF','ES','GO','MA','MT','MS',
        'MG','PA','PB','PR','PE','PI','RJ','RN','RS','RO','RR','SC',
        'SP','SE','TO','EX'
    ];

    const cPESOS =
        [[0  ,2  ,3  ,4  ,5  ,6  ,7  ,8  ,9  ,2   ,3   ,4   ,5   ,6 ],
            [0  ,0  ,2  ,3  ,4  ,5  ,6  ,7  ,8  ,9   ,2   ,3   ,4   ,5 ],
            [2  ,0  ,3  ,4  ,5  ,6  ,7  ,8  ,9  ,2   ,3   ,4   ,5   ,6 ],
            [0  ,2  ,3  ,4  ,5  ,6  ,0  ,0  ,0  ,0   ,0   ,0   ,0   ,0 ],
            [0  ,8  ,7  ,6  ,5  ,4  ,3  ,2  ,1  ,0   ,0   ,0   ,0   ,0 ],
            [0  ,2  ,3  ,4  ,5  ,6  ,7  ,0  ,0  ,8   ,9   ,0   ,0   ,0 ],
            [0  ,2  ,3  ,4  ,5  ,6  ,7  ,8  ,9  ,1   ,2   ,3   ,4   ,5 ],
            [0  ,2  ,3  ,4  ,5  ,6  ,7  ,2  ,3  ,4   ,5   ,6   ,7   ,8 ],
            [0  ,0  ,2  ,3  ,4  ,5  ,6  ,7  ,2  ,3   ,4   ,5   ,6   ,7 ],
            [0  ,0  ,2  ,1  ,2  ,1  ,2  ,1  ,2  ,1   ,1   ,2   ,1   ,0 ],
            [0  ,2  ,3  ,4  ,5  ,6  ,7  ,8  ,9  ,10  ,11  ,2   ,3   ,0 ],
            [0  ,0  ,0  ,0  ,10 ,8  ,7  ,6  ,5  ,4   ,3   ,1   ,0   ,0 ],
            [0  ,2  ,3  ,4  ,5  ,6  ,7  ,8  ,9  ,10  ,2   ,3   ,0   ,0 ],
            [0  ,0  ,2  ,3  ,4  ,5  ,6  ,7  ,8  ,3   ,4   ,5   ,6   ,7 ]];

    public function __construct()
    {

    }

    public function isValidIE($value, $params)
    {

        $inscricaoEstadual = $value;
        $errorMessage = '';

        if (isset($params['uf']) && !is_null($params['uf'])){
            $UF = $params['uf'];
        } else {
            $UF = '';
        }

        if (strtoupper($inscricaoEstadual) == 'ISENTO'){
            return;
        }

        if(is_null($UF) || $UF == ''){
            $errorMessage = 'Informe a UF.';

            return $errorMessage;
        }

        $isValidUF = in_array($UF, self::validUFs);
        if(!$isValidUF){
            $errorMessage = "A UF informada é invalida!";
            return $errorMessage;
        }

        if(!is_numeric(substr($inscricaoEstadual, 2, strlen($inscricaoEstadual))) ||
            (!is_numeric($inscricaoEstadual[0]) && $inscricaoEstadual[0] != 'P') ){

            $errorMessage = 'Caracteres inválidos na Inscrição Estadual';

            return $errorMessage;
        }

        $size = 0;
        $xROT = 'E';
        $xMD = 11;
        $xTP = 1;
        $yROT = '';
        $yMD = 0;
        $yTP = 0;
        $fatorF = 0;
        $fatorG = 0;

        $digits = ['','','','','','','','','','','','','',''];

        switch($UF){
            case 'AC':
                if(strlen($inscricaoEstadual) == 9){
                    $size = 9;
                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '1', '0', '', '', '', '', ''];
                } else {
                    $size = 13;
                    $xTP = 2;
                    $yROT = 'E';
                    $yMD = 11;
                    $yTP = 1;
                    $digits = ['DVY', 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '1', '0', ''];
                }
                break;

            case 'AL':
                $size = 9;
                $xROT = 'BD';
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '4', '2', '', '', '', '', ''];
                break;

            case 'AP':
                $size = 9;
                $xROT = 'CE';
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '3', '0', '', '', '', '', ''];

                if($inscricaoEstadual >= '030170010' && $inscricaoEstadual <= '030190229'){
                    $fatorF = 1;
                } elseif($inscricaoEstadual >= '030190230') {
                    $xROT = 'E';
                }
                break;

            case 'AM':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', '', ''];
                break;

            case 'BA':
                if(strlen($inscricaoEstadual) < 9){
                    $inscricaoEstadual = str_pad($inscricaoEstadual, 9, '0', STR_PAD_LEFT);
                }

                $size = 9;
                $xTP = 2;
                $yTP = 3;
                $yROT = 'E';
                $digits = ['DVX', 'DVY', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', '', ''];

                if(strpos('0123458', $inscricaoEstadual[1]) != false){
                    $xMD = 10;
                    $yMD = 10;
                } else {
                    $xMD = 11;
                    $yMD = 11;
                }
                break;

            case 'CE':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '0', '', '', '', '', ''];
                break;

            case 'DF':
                $size = 13;
                $xTP = 2;
                $yROT = 'E';
                $yMD = 11;
                $yTP = 1;
                $digits = ['DVY', 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '7,8', '0', ''];
                break;

            case 'ES':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', '', ''];
                break;

            case 'GO':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '0,1,5', '1', '', '', '', '', ''];

                if($inscricaoEstadual >= '101031050' && $inscricaoEstadual <= '101199979'){
                    $fatorG = 1;
                }
                break;

            case 'MA':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '2', '1', '', '', '', '', ''];
                break;

            case 'MT':
                if(strlen($inscricaoEstadual) == 9){
                    $inscricaoEstadual = str_pad($inscricaoEstadual, 11, '0', STR_PAD_LEFT);
                }

                $size = 11;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', ''];
                break;

            case 'MS':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '8', '2', '', '', '', '', ''];
                break;

            case 'MG':
                $size = 13;

                $xROT = 'AE';
                $yROT = 'E';

                $xMD = 10;
                $yMD = 11;

                $xTP = 10;
                $yTP = 11;

                $digits = ['DVY', 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, ''];
                break;

            case 'PA':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '5', '1', '', '', '', '', ''];
                break;

            case 'PB':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '6', '1', '', '', '', '', ''];
                break;

            case 'PR':
                $size = 10;

                $xTP = 9;
                $yTP = 8;
                $yROT = 'E';
                $yMD = 11;

                $digits = ['DVY', 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', ''];
                break;

            case 'PE':
                if(strlen($inscricaoEstadual) == 14){
                    $size = 14;

                    $xTP = 7;
                    $fatorF = 1;

                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '1-9', '8', '1'];
                } else {
                    $size = 9;

                    $xTP = 14;
                    $yROT = 'E';
                    $yMD = 11;
                    $yTP = 7;

                    $digits = ['DVY', 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', '', ''];
                }
                break;

            case 'PI':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '9', '1', '', '', '', '', ''];
                break;

            case 'RJ':
                $size = 8;
                $xTP = 8;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '1,7,8,9', '', '', '', '', '', ''];
                break;

            case 'RN':
                if(strlen($inscricaoEstadual) == 9){
                    $size = 9;
                    $xROT = 'BD';
                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '0', '2', '', '', '', '', ''];
                } else {
                    $size = 10;
                    $xROT = 'BD';
                    $xTP = 11;
                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '0', '2', '', '', '', ''];
                }
                break;

            case 'RS':
                $size = 10;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', ''];
                break;

            case 'RO':
                $fatorF = 1;

                if(strlen($inscricaoEstadual) == 9){
                    $size = 9;
                    $xTP = 4;
                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '1-9', '', '', '', '', ''];
                } else {
                    $size = 14;
                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9];
                }
                break;

            case 'RR':
                $size = 9;

                $xROT = 'D';
                $xMD = 9;
                $xTP = 5;

                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', ''];
                break;

            case 'SC':
            case 'SE':
                $size = 9;
                $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', '', ''];
                break;

            case 'SP':
                $xROT = 'D';
                $xTP = 12;

                if($inscricaoEstadual[0] == 'P'){
                    $size = 13;
                    $digits = [self::c0_9, self::c0_9, self::c0_9, 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, 'P', ''];
                } else {
                    $size = 12;

                    $yROT = 'D';
                    $yMD = 11;
                    $yTP = 13;

                    $digits = ['DVY', self::c0_9, self::c0_9, 'DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', ''];
                }
                break;

            case 'TO':
                if($inscricaoEstadual[0] == 'P'){
                    $size = 11;
                    $xTP = 6;
                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '1,2,3,9', '0,9', '9', '2', '', '', ''];
                } else {
                    $size = 9;

                    $yROT = 'D';
                    $yMD = 11;
                    $yTP = 13;

                    $digits = ['DVX', self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, self::c0_9, '', '', '', '', ''];;
                }
                break;
        }

        if(isset($params['ajustar_tamanho']) && $params['ajustar_tamanho']){
            $inscricaoEstadual = str_pad($inscricaoEstadual, $size, "0", STR_PAD_LEFT);
        }

        $correct = ($size > 0) && (strlen($inscricaoEstadual) == $size);

        if(!$correct){
            $lengIE = strlen($inscricaoEstadual);
            $errorMessage = "Tamanho inválido: Esperado {$size} caracteres, mas foram digitados {$lengIE} caracteres";

            return $errorMessage;
        }

        $inscricaoEstadual = str_pad($inscricaoEstadual, 14, " ", STR_PAD_LEFT);

        $DVX = 0;
        $DVY = 0;
        $i   = 13;

        while($correct && $i >= 0){
            $digIE = $inscricaoEstadual[13-$i];

            if($digits[$i] == ''){
                $correct = ($digIE == ' ');
            } elseif($digits[$i] == 'DVX' || $digits[$i] == 'DVY' || $digits[$i] == self::c0_9){
                $correct = is_numeric($digIE);

                if($digits[$i] == 'DVX'){
                    $DVX = intval($digIE);
                } elseif($digits[$i] == 'DVY'){
                    $DVY = intval($digIE);
                }
            } elseif(strpos($digits[$i],',') !== false){
                $correct = (strpos($digits[$i], $digIE) !== false);
            } elseif(strpos($digits[$i],'-') !== false){
                $correct = $digIE >= substr($digits[$i], 1, 1) && $digIE <= substr($digits[$i], 3, 1);
            } else {
                $correct = $digIE == $digits[$i];
            }

            if(!$correct){
                $digAux = (14-$i-(14-$size));
                $errorMessage = "Digito {$digAux} deveria ser {$digits[$i]}";
            }

            $i--;
        }

        $step = 'X';
        $tpAux = $xTP - 1;
        while($correct && ($tpAux >= 0)){
            $soma = 0;
            $somaQ = 0;
            $w = 14;
            $wAux = 13;

            while($correct && ($w > 0)){
                $digIE = $inscricaoEstadual[14-$w];

                if(is_numeric($digIE)){
                    $numericDigit = intval($digIE);
                    $M = $numericDigit * self::cPESOS[$tpAux][$wAux];
                    $soma += $M;

                    if(strpos($xROT, 'A') !== false){
                        $somaQ += (int)($M/10);
                    }
                }

                $w--;
                $wAux--;
            }

            if(strpos($xROT, 'A') !== false){
                $soma += $somaQ;
            } elseif(strpos($xROT, 'B') !== false) {
                $soma *= 10;
            } elseif(strpos($xROT, 'C') !== false){
                $soma += 5 + (4 * $fatorF);
            }

            $dv = (int)($soma % $xMD); //mod - resto da divisao
            if(strpos($xROT, 'E') !== false){
                $dv = (int)($xMD - $dv);
            }

            if($dv == 10 ){
                $dv = $fatorG;
            } elseif($dv == 11){
                $dv = $fatorF;
            }

            if($step == 'X'){
                $correct = ($DVX == $dv);
            } else {
                $correct = ($DVY == $dv);
            }

            $digitoCalculado = intval($dv);

            if(!$correct){
                $errorMessage = "Dígito verificador inválido.";


                if(isset($params['exibeDigCorretoValidacaoIE']) && $params['exibeDigCorretoValidacaoIE']){
                    $errorMessage .= ".. Calculado: {$digitoCalculado}";
                }
            }

            if($step == 'X'){
                $step = 'Y';
                $xROT  = $yROT;
                $xMD   = $yMD;
                $tpAux   = $yTP-1;
            } else {
                break;
            }
        }

        $inscricaoEstadual = trim($inscricaoEstadual);
        if(trim($errorMessage) != ''){

            $errorMessage = "Insc.Estadual inválida para {$UF} {$errorMessage}";
            return $errorMessage;
        };
    }
}