<?php
namespace CorpusParole\Libraries\Filters;

use Config;
use CorpusParole\Libraries\Utils;

use Illuminate\Http\Request;

class CorpusFilterManager {

    const DATE_REGEXP = "/^([[:digit:]]{4})(?:-([[:digit:]]{4}))?$/";

    public function prepareIds($ids) {
        if(is_null($ids)) {
            return [];
        }

        if(is_string($ids)) {
            return explode(",", $ids);
        }
        else return $ids;
    }

    public function getIdsFilterPart($ids) {
        return [
            "bool" => [
                "filter" => [
                    "terms" => [
                        '_id' => $ids
                    ]
                ]
            ]
        ];
    }

    public function getLanguageNode($nodeId) {
        $node = null;
        $currentNodes = [config('corpusparole.languages_treemap'),];
        while(is_null($node) && !empty($currentNodes)) {
            $currentNode = array_pop($currentNodes);
            foreach($currentNode['children'] as $n) {
                if($n['id'] === $nodeId) {
                    $node = $n;
                    break;
                } elseif (array_key_exists('children', $n)) {
                    array_push($currentNodes, $n);
                }
            }
        }
        return $node;
    }

    private function getLanguageNodeChildren($nodeId) {
        $node = $this->getLanguageNode($nodeId);
        if(is_null($node)) {
            return [];
        } elseif (array_key_exists('values', $node)) {
            return $node['values'];
        } elseif (array_key_exists('children', $node)) {
            return array_map(function($c) {
                return $c['id'];
            }, $node['children']);
        } else {
            return [];
        }
    }

    public function prepareLanguages($languages) {

        if(is_null($languages)) {
            return [];
        }
        $resLanguage = [];
        if(is_string($languages)) {
            $languages = [ $languages, ];
        }

        $languages = array_slice($languages, 0, config('corpusparole.filter_max_languages_nb', 200));

        foreach($languages as $lang) {
            if($lang == config('corpusparole.language_corpus_node_global')) {
                // ignore global
                continue;
            }
            else if(Utils::startsWith($lang, config('corpusparole.lexvo_base_url'))) {
                array_push($resLanguage, $lang);
            } elseif(Utils::startsWith($lang, config('corpusparole.language_corpus_node_prefix'))) {
                $resLanguage = array_merge($resLanguage, $this->prepareLanguages($this->getLanguageNodeChildren($lang)));
            } else {
                array_push($resLanguage, config('corpusparole.lexvo_base_url').$lang);
            }
        }

        return array_unique($resLanguage);
    }

    public function getLanguagesFilterPart($languages) {
        return [
            'bool' => [
                'should' => [
                    [ 'terms' => [ 'language' => $languages ]]
                ]
            ]
        ];
    }

    public function prepareLocation($location) {
        if(empty($location)) {
            return null;
        }
        if(is_array($location)) {
            $location = $location[0]; //from test above we know ther is at least one element
        }
        if(preg_match(config('corpusparole.geonames_url_regexp'), $location, $m)) {
            $location = $m[1];
        }
        // for the moment we do not make this check. Incorrect loaction format will return an empty result
        /*if(!preg_match('/\d+/', $location)) {
            return null;
        }*/

        return $location;
    }

    /**
     * Search in geonames_hierarchy (values are bae geonames ids)
     */
    public function getLocationFilterPart($location) {
        return [
            'term' => [ 'geonames_hierarchy' => "$location" ]
        ];
    }

    public function prepareTheme($entities) {
        if(empty($entities)) {
            return [];
        }
        if(is_string($entities)) {
            $entities = [$entities,];
        }
        $entities = array_slice($entities, 0, config('corpusparole.filter_max_themes_nb', 200));

        return array_reduce($entities, function($res, $e) {
            if(preg_match(config('corpusparole.bnf_ark_url_regexp'), $e, $m)) {
                array_push($res, Utils::SUBJECT_TYPE_BNF."|".$m[1]);
            } elseif(Utils::startsWith($e, config('corpusparole.bnf_ark_id_base'))) {
                array_push($res, Utils::SUBJECT_TYPE_BNF."|".$e);
            } elseif(preg_match(config('corpusparole.lexvo_url_regexp'), $e, $m)) {
                array_push($res, Utils::SUBJECT_TYPE_LEXVO."|".$m[1]);
            } elseif(Utils::startsWith($e, Utils::SUBJECT_TYPE_OLAC.':')) {
                array_push($res, Utils::SUBJECT_TYPE_OLAC."|".substr($e,5));
            } else {
                array_push($res, Utils::SUBJECT_TYPE_TXT."|$e");
            }
            return $res;
        }, []);
    }

    public function getThemeFilterPart($themes) {
        $nestedQueries = [];
        foreach($themes as $t) {
            $nestedQueries[] = [
                'nested' => [
                    'path' => "subject",
                    'query' => [ 'term' => ['subject.code' => "$t" ] ]
                ]
            ];
        }
        return [
            'bool' => [
                    'must' => $nestedQueries
            ]
        ];
    }

    public function prepareDiscourse($entities) {
        if(empty($entities)) {
            return [];
        }
        if(is_string($entities)) {
            $entities = [$entities,];
        }
        $entities = array_slice($entities, 0, config('corpusparole.filter_max_discourses_nb', 200));
        return array_reduce($entities, function($res, $e) {
            if(preg_match(config('corpusparole.bnf_ark_url_regexp'), $e)) {
                array_push($res, $e);
            } elseif(Utils::startsWith($e, config('corpusparole.bnf_ark_id_base'))) {
                array_push($res, config('corpusparole.bnf_ark_base_url').$e);
            }
            return $res;
        }, []);

    }

    public function getDiscourseFilterPart($discourses) {
        $subDiscQueries = array_map(function($d) {
            return [ 'term' => ['discourse_types' => "$d" ]];
        }, $discourses);

        return [
            'bool' => [
                'must' => $subDiscQueries
            ]
        ];
    }

    public function prepareDate($dates) {
        if(empty($dates)) {
            return [];
        }
        if(is_string($dates)) {
            $dates = [$dates,];
        }

        $dates = array_slice($dates, 0, config('corpusparole.filter_max_dates_nb', 200));

        $res = array_reduce($dates, function($l, $d) {
            if(preg_match(self::DATE_REGEXP, $d, $m) === 1) {
                $l[] = $d;
            }
            return $l;
        }, []);
        sort($res);
        return $res;
    }

    public function getDateFilterPart($dates) {
        $dateSubQuesries = array_reduce($dates, function($filters, $d) {
            $mRes = preg_match(self::DATE_REGEXP, $d, $m);
            if($mRes === 1 && count($m) === 2){
                $filters[] = [
                    'nested' => [
                        'path' => "creation_years",
                        'query' => [ 'term' => ['creation_years.year' => intval($m[1]) ] ]
                    ]
                ];
            } elseif ($mRes === 1 && count($m) === 3) {
                $filters[] = [
                    'nested' => [
                        'path' => "creation_years",
                        'query' => [ 'range' => ['creation_years.year' => [ 'gte' => intval($m[1]), 'lte' => intval($m[2])] ]]
                    ]
                ];
            }
            return $filters;
        }, []);

        if(empty($dateSubQuesries)) {
            return [];
        } else {
            return [
                'bool' => [
                    'should' => $dateSubQuesries
                ]
            ];
        }
    }

    public function prepareFilters(Request $request) {

        $filters = [];

        $ids = $this->prepareIds($request->input('id', []));
        if(!empty($ids)) {
            $filters['id'] = $ids;
        }

        $languages = $this->prepareLanguages($request->input('language', []));
        if(!empty($languages)) {
            $filters['language'] = $languages;
        }

        $location = $this->prepareLocation($request->input('location', ''));
        if(!empty($location)) {
            $filters['location'] = $location;
        }

        $themes = $this->prepareTheme($request->input('theme', []));
        if(!empty($themes)) {
            $filters['themes'] = $themes;
        }

        $discourses = $this->prepareDiscourse($request->input('discourse', []));
        if(!empty($discourses)) {
            $filters['discourses'] = $discourses;
        }
        $dates = $this->prepareDate($request->input('date', []));
        if(!empty($dates)) {
            $filters['dates'] = $dates;
        }

        return $filters;
    }

    public function buildESFilters($filters) {

        if(empty($filters)) {
            return [];
        }

        $qFilterParts = [];
        if(array_key_exists('id', $filters) && !empty($filters['id'])) {
            $ids = $filters['id'];
            if(is_string($ids)) {
                $ids = [$ids,];
            }
            $fp = $this->getIdsFilterPart($ids);
            if(!empty($fp)) {
                $qFilterParts[] = $fp;
            }
        }
        if(array_key_exists('language', $filters) && !empty($filters['language'])) {
            $languages = $filters['language'];
            if(is_string($languages)) {
                $languages = [ $languages, ];
            }

            $fp = $this->getLanguagesFilterPart($languages);
            if(!empty($fp)) {
                $qFilterParts[] = $fp;
            }
        }
        if(array_key_exists('location', $filters) && !empty($filters['location'])) {
            $location = $filters['location'];
            if(is_array($location)) {
                $location = $location[0]; // we know it is not empty
            }
            $fp = $this->getLocationFilterPart($location);
            if(!empty($fp)) {
                $qFilterParts[] = $fp;
            }
        }
        if(array_key_exists('themes', $filters) && !empty($filters['themes'])) {
            $themes = $filters['themes'];
            if(is_string($themes)) {
                $themes = [$themes,]; // we know it is not empty
            }

            $fp = $this->getThemeFilterPart($themes);
            if(!empty($fp)) {
                $qFilterParts[] = $fp;
            }
        }
        if(array_key_exists('discourses', $filters) && !empty($filters['discourses'])) {
            $discourses = $filters['discourses'];
            if(is_string($discourses)) {
                $discourses = [$discourses,]; // we know it is not empty
            }

            $fp = $this->getDiscourseFilterPart($discourses);
            if(!empty($fp)) {
                $qFilterParts[] = $fp;
            }

        }
        if(array_key_exists('dates', $filters) && !empty($filters['dates'])) {
            $dates = $filters['dates'];
            if(is_string($dates)) {
                $dates = [$dates,]; // we know it is not empty
            }

            $fp = $this->getDateFilterPart($dates);
            if(!empty($fp)) {
                $qFilterParts[] = $fp;
            }
        }

        return $qFilterParts;
    }

    public function buildQuery($qFilterParts) {
        if(empty($qFilterParts)) {
            return [ "match_all" => (object) null ];
        } else {
            return ['constant_score' => [
                'filter' => [
                    'bool' => [
                        'must' => $qFilterParts
                    ]
                ]
            ] ];
        }

    }

}
