<?php
// патч: создаем rule_filter для таблиц, который не создавался при конверте прав доступа
$rules = sql_select_array($config['table_prefix'] . "acc_rules", "ALL_ROWS");
if (is_array($rules)) {
    foreach ($rules as $one_rule_id => $one_rule) {
        $rule_filter = revision_temp_form_rule_filter($one_rule['table_id'], $one_rule['group_id']);
        if ($one_rule['global']) {
            sql_update($config['table_prefix'] . "acc_tables", array('rule_filter' => $rule_filter), '`table_id`=',
                $one_rule['table_id']);
        } else {
            sql_update($config['table_prefix'] . "acc_tables", array('rule_filter' => $rule_filter), '`table_id`=',
                $one_rule['table_id'], ' and `group_id`=', $one_rule['group_id']);
        }
    }
}

// Формируем фильтр для данной таблицы, для данной группы, на основе текущих правил
function revision_temp_form_rule_filter($table_id, $group_id)
{
    global $config;
    // Строим смежную карту прав, по каждому правилу
    $rule_maps = array();

    // получаем данные таблицы
    $sqlQuery = "SELECT * FROM " . $config['table_prefix'] . "tables WHERE id='$table_id'";
    $result = run_query($sqlQuery);
    $table = sql_fetch_assoc($result);

    // права на таблицу
    $table['rule_filter'] = '';
    $table['rules'] = array();
    $cat_acc = sql_select_array($config['table_prefix'] . "acc_cats", '`cat_id`=', $table['cat_id'], ' and `group_id`=',
        $group_id);
    if ($cat_acc['access']) { // Доступ на уровне категории разрешен
        $table_acc = sql_select_array($config['table_prefix'] . "acc_tables", '`table_id`=', $table_id,
            ' and `group_id`=', $group_id);
        if ($table_acc['acc']) {
            $table['access'] = $table_acc['acc'];
            $table['vis'] = $table_acc['vis_acc'];
            $table['add'] = $table_acc['add_acc'];
            $table['del'] = $table_acc['del_acc'];
            $table['arc'] = $table_acc['arc_acc'];
            $table['imp'] = $table_acc['imp_acc'];
            $table['exp'] = $table_acc['exp_acc'];
            $table['adf'] = $table_acc['adf_acc'];
            $table['bed'] = $table_acc['bed_acc'];
            $table['rule_filter'] = $table_acc['rule_filter'];

            // Выбираем правила доступа
            $result3 = sql_select($config['table_prefix'] . "acc_rules", '`table_id`=', $table_id, ' AND (`group_id`=',
                $group_id, ' OR `global`=1) ORDER BY num');
            while ($row = sql_fetch_assoc($result3)) {
                $rule['id'] = $row['id'];
                $rule['name'] = $row['name'];
                $rule['num'] = $row['num'];
                $rule['condition_php'] = $row['condition_php'];
                $rule['rights'] = unserialize($row['rights']);
                if (!is_array($rule['rights'])) {
                    $rule['rights'] = array();
                }
                // Выбираем доступ к таблице по правилу
                $table['rules'][$rule['id']] = $rule;
            }
        }
    }
    $table['rules_reverse'] = array_reverse($table['rules'], 1);

    // получаем поля таблицы
    $sqlQuery = "SELECT * FROM " . $config['table_prefix'] . "fields WHERE table_id=" . $table_id . " ORDER BY field_num";
    $result = run_query($sqlQuery);
    while ($row = sql_fetch_assoc($result)) {
        $table_fields[] = $row;
    }

    // далее пытаемся оформить фильтр
    if (!$table['access']) {
        return "";
    } // Доступа нет, фильтр не нужен
    // Подключаем правило по умолчанию
    foreach ($table_fields as $one_field) {
        $union_acc = sql_select_array($config['table_prefix'] . "acc_fields", '`field_id`=', $one_field['id'],
            ' and `group_id`=', $group_id);
        $rule_maps[$one_field['id']] = array();
        if ($union_acc['view'] || $union_acc['view_tb'] || $union_acc['view_edit']) { // Есть доступ из таблицы
            $rule_maps[$one_field['id']][0] = 1;
        } else {
            $rule_maps[$one_field['id']][0] = 0;
        }
    }

    // Подключаем все остальные правила
    foreach ($table['rules'] as $rule) {
        $rule_id = $rule['id'];
        $rule_info = sql_select_array($config['table_prefix'] . "acc_rules", '`id`=',
            $rule_id);  // Массив условий не подгружается по умолчанию
        $table['rules'][$rule_id]['condition'] = unserialize($rule_info['condition']);
        foreach ($rule['rights'] as $one_right) {
            if ($one_right['field']) { // Если управление по полю
                if (!$rule_maps[$one_right['field']][$rule_id]) {
                    $rule_maps[$one_right['field']][$rule_id] = $one_right['view'] || $one_right['view_tb'] || $one_right['view_edit'] ? 1 : 0;
                }
            }
        }
    }
    // Сортируем в обратном порядке, т.к. более высокое правило - это более высокий приоритет
    foreach ($rule_maps as $f_id => $rule_list) {
        $rule_maps[$f_id] = array_reverse($rule_list, 1);
    }

    // Cтроим условие
    $long_cond = "1!=1";
    foreach ($rule_maps as $f_id => $rule_list) {
        $prev_acc = "N";
        foreach ($rule_list as $r_num => $acc) {
            if ($acc) { // Утверждение
                if ($prev_acc === 'N') {
                    $long_cond .= " || (";
                } else {
                    if (!$prev_acc) {
                        $long_cond .= " && (";
                    } else {
                        $long_cond .= " || (";
                    }
                }
                if ($r_num == 0) {
                    $long_cond .= '1';
                } else {
                    $long_cond .= '$R' . $r_num;
                }
            } else { // Отрицание
                if ($prev_acc === 'N') {
                    $long_cond .= " || (";
                } else {
                    if (!$prev_acc) {
                        $long_cond .= " && (";
                    } else {
                        $long_cond .= " || (";
                    }
                }
                if ($r_num == 0) {
                    $long_cond .= '0';
                } else {
                    $long_cond .= '!$R' . $r_num;
                }
            }
            $prev_acc = $acc;
        }
        // Закрываем все скобки
        for ($i = 0; $i < count($rule_list); $i++) {
            $long_cond .= ")";
        }
    }
    $short_cond = revision_temp_sub_simple_cond($long_cond);
    // На основе простого условия формируем фильтр
    if ($short_cond == '0') {
        return "0";
    } // Всегда недопустимо
    if ($short_cond == '1') {
        return "1";
    } // Всегда допустимо
    // формируем для каждого из правил условия sql фильтры и подменяем ими значения в упрощенных выражениях
    foreach ($table['rules'] as $rule) {
        $rule_id = $rule['id'];
        $sql_cond = revision_temp_form_sql_condition($rule['condition']);
        // Подменяем R(id) на sql_условие
        $short_cond = str_replace('$R' . $rule_id, $sql_cond, $short_cond);
    }

    $table_cache = array();
    $user['group_id'] = $old_group;

    return $short_cond;
}

// Функция упрощает строку с переменными типа $R1 ... RN
// По методу Квайна-Мак-Класки
function revision_temp_sub_simple_cond($str)
{
    // Нормализуем переменные, чтобы отсчет шел от 1 и последловательно
    $orig_str = $str;
    $normal_str = $str;
    $normal_map = array();
    $cnt = 1;
    $found_pos = strpos($str, '$R');
    while ($found_pos !== false) {
        // Переменная присутвует
        $i = intval(substr($str, $found_pos + 2));
        $normal_map[$cnt] = $i;
        $normal_str = str_replace('$R' . $i, '$R' . $cnt, $normal_str);
        $str = str_replace('$R' . $i, '$T' . $i, $str);
        $cnt++;
        $found_pos = strpos($str, '$R');
    }
    $cnt = $cnt - 1;
    $str = $normal_str;

    $map_true = array();
    $map_false = array();
    // Составляем карту истинности
    for ($i = 0; $i < pow(2, $cnt); $i++) {
        $res = revision_temp_calc_expss($str, $i, $cnt);
        if ($res) { // Выделяем положительные результаты
            $map_true[$i] = 1;
        } else {
            $map_false[$i] = 1;
        }
    }
    // Выбираем карту в зависимости от того - чего больше 1 или 0
    $map = $map_true;

    if (count($map_true) == 0) {
        return '0';
    }
    if (count($map_false) == 0) {
        return '1';
    }

    //if (count($map_false)<count($map_true))
    //   { $map=$map_false; $false_map=1;}

    // По результируещей карте, выделяем заменяем все id - двоичными номерами
    // по двоичным номерам формируем группы, в соответсвии с количеством едениц
    $groups = array();
    foreach ($map as $k => $v) {
        $bin_key = decbin($k);
        // Дополняем чило нулями если нехватает разрядов
        while (strlen($bin_key) < $cnt) {
            $bin_key = "0" . $bin_key;
        }
        // Считаем количество едениц
        $g = 0;
        for ($i = 0; $i < strlen($bin_key); $i++) {
            if ($bin_key[$i] == 1) {
                $g++;
            }
        }
        $groups[$g][$bin_key] = $bin_key;
    }
    $was_splitt = 1;
    while ($was_splitt) {
        // Перебираем группы пытаемся провести склейку
        $was_splitt = 0;
        foreach ($groups as $i => $group) {
            if (!isset($groups[$i + 1])) {
                continue;
            }
            $next_group = $groups[$i + 1];
            foreach ($group as $k => $ipl) {
                foreach ($next_group as $k2 => $ipl2) {  // Сравниваем две импликанты, если разница 1 символ, то производим склейку
                    $diff_cnt = 0;
                    $res_ipl = "";
                    for ($j = 0; $j < strlen($ipl); $j++) {
                        if (($ipl[$j] != $ipl2[$j]) && ($ipl[$j] == '-' || $ipl2[$j] == '-')) { // Разница по уже существующему объединению, объединение не возможно
                            $diff_cnt = 999;
                            break;
                        }
                        if ($ipl[$j] != $ipl2[$j]) {
                            $res_ipl .= '-';
                            $diff_cnt++;
                            if ($diff_cnt > 1) {
                                break;
                            } // Если разница больше 1, то дальше продолжать смысла нет, соращение не возможно
                        } else {
                            $res_ipl .= $ipl[$j];
                        }
                    }
                    if ($diff_cnt <= 1) {
                        // Считаем количество 1 в новой импликанте
                        $g = 0;
                        for ($m = 0; $m < strlen($res_ipl); $m++) {
                            if ($res_ipl[$m] == 1) {
                                $g++;
                            }
                        }
                        unset($groups[$i][$k]);
                        unset($groups[$i + 1][$k2]);
                        if (count($groups[$i]) == 0) {
                            unset($groups[$i]);
                        }
                        if (count($groups[$i + 1]) == 0) {
                            unset($groups[$i + 1]);
                        }
                        unset($next_group[$k2]);
                        $groups[$g][$res_ipl] = $res_ipl;
                        $was_splitt = 1;
                    }
                }
            }
        }
    }
    //echo "<br>\nResult groups.\n";
    //print_r($groups);
    // Строим карту покрытия, и упрощаем в лоб каждое выражение на каждое
    $map2 = array();
    foreach ($groups as $i => $group) {
        foreach ($group as $k => $ipl) {
            $t = array();
            $common_mask = bindec(str_replace('-', '0', $ipl));

            for ($i = 0; $i < $cnt; $i++) {
                $pos_i = $cnt - $i - 1;
                if ($ipl[$pos_i] == '-') {
                    $t[] = pow(2, $i); // Запоминаем возможные варианты
                }
            }
            $v = array();
            $v[$common_mask] = 1;
            foreach ($t as $t1) {
                foreach ($v as $mask => $val) {
                    $p = $mask | $t1;
                    $v[$p] = 1;
                }
            }
            $map2[$ipl] = $v;
        }
    }
    // По карте покрытия смотрим есть ли включающие элементы
    foreach ($map2 as $ipl => $values) {
        $c_v = $values;
        // Проверяем включена ли данная импликанта в другие?
        foreach ($map2 as $ipl2 => $values2) {
            if ($ipl2 == $ipl) {
                continue;
            }// Пропускаем саму себя
            foreach ($values2 as $v => $v_one) {
                unset($c_v[$v]);
            }
        }
        // Если $c_v было обнулено значит оно входит в другую импликанту
        if (count($c_v) == 0) {
            unset($map2[$ipl]);
        }

    }
    //print_r($map2);
    // Строим результирующее выражение
    $res_simple = "";
    foreach ($map2 as $ipl => $v) {
        $ipl = "$ipl";
        $one_simple = "";
        for ($i = 1; $i <= $cnt; $i++) {
            $pos_i = $cnt - $i;
            //if (($ipl[$pos_i]=='1')||($false_map && $ipl[$pos_i]=='0'))
            if ($ipl[$pos_i] == '1') {
                $one_simple .= '$R' . $i . " and ";
            }
            if ($ipl[$pos_i] == '0') {
                $one_simple .= '!$R' . $i . " and ";
            }
        }
        if (strlen($one_simple)) {
            $one_simple = substr($one_simple, 0, -5);
            if (strpos($one_simple, 'and')) {
                $one_simple = "($one_simple)";
            }
            $res_simple .= $one_simple;
            $res_simple .= " or ";
        }
    }
    if (strlen($res_simple)) {
        $res_simple = substr($res_simple, 0, -4);
        if ($false_map) {
            $res_simple = "!($res_simple)";
        }
    }

    // Восстанавливаем из нормальной формы оригинальные переменные
    foreach ($normal_map as $cnt => $i) {
        $res_simple = str_replace('$R' . $cnt, '$R' . $i, $res_simple);
    }

    return $res_simple;
}

// На основе массива условий, создать фильтр на sql
// обрабатывающего данное условие, вычисление формируется по умочанию с переменной $line
function revision_temp_form_sql_condition($cond_array, $table_prefix = '')
{
    $sql_condition = "";
    foreach ($cond_array as $one_cond) {
        if ($one_cond['field'] != 999999999) {
            $field_name = form_int_name($one_cond['field']);
        }
        if ($one_cond['term'] == ' like ') {
            $one_cond['value'] = "%" . $one_cond['value'] . "%";
        }
        if ($one_cond['term'] == ' not like ') {
            $one_cond['value'] = "%" . $one_cond['value'] . "%";
        }
        if ($one_cond['oper'] && $one_cond['interval'] && $one_cond['period']) {
            $cond_date = $one_cond['oper'] . $one_cond['interval'] . $one_cond['period'];
        } else {
            $cond_date = "";
        }
        if ($one_cond['value'] != 'now()' && $one_cond['value'] != 'curdate()') {
            $one_cond['value'] = "'" . $one_cond['value'] . "'";
        }
        $sql_condition .= '(' . ($table_prefix ? $table_prefix . '.' : '') . ($one_cond['value'] == 'curdate()' ? "left(" : "") . "`" . $field_name . "`" . ($one_cond['value'] == 'curdate()' ? ",10)" : "") . $one_cond['term'] . $one_cond['value'] . $cond_date . ')' . $one_cond['union'];
    }
    return $sql_condition;
}

// Функция вычисляет значение булевой функции в зависимости от маски выставленных булевых переменных
// $str  - выражение
// $mask - маска булевых переменных
// $cnt  - размерность
function revision_temp_calc_expss($str, $mask, $cnt)
{
    $res_str = $str;
    for ($i = 0; $i < $cnt; $i++) {
        $var_val = intval(($mask & pow(2, $i)) > 0);
        $res_str = str_replace('$R' . ($i + 1), $var_val, $res_str);
    }
    //echo "<br>\n$res_str : ".eval("return intval($res_str);");
    return eval("return intval($res_str);");
}

?>