<?php
// This file is part of mod_datalynx for Moodle - http://moodle.org/
//
// It is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// It is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

namespace mod_datalynx\local\field;
use coding_exception;
use dml_exception;
use mod_datalynx;
use mod_datalynx\datalynx;
use moodle_exception;
use moodle_url;
use stdClass;

/**
 * Base class for Datalynx Field Types
 * @package mod_datalynx
 * @copyright 2025 Wunderbyte GmbH
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
abstract class datalynxfield_base {
    /** @var int Field is not visible to anyone. */
    const VISIBLE_NONE = 0;

    /** @var int Field is visible to the owner only. */
    const VISIBLE_OWNER = 1;

    /** @var int Field is visible to all users. */
    const VISIBLE_ALL = 2;

    /**
     * Subclasses must override the type with their name.
     *
     * @var string Fieldtype usually datalynxfield_fieldtype.
     */
    public $type = 'unknown';

    /**
     * The datalynx object that this field belongs to.
     *
     * @var datalynx
     */
    public $df = null;

    /**
     * The field object itself, if we know it
     *
     * @var object
     */
    public $field = null;

    /**
     * @var datalynxfield_renderer
     */
    protected $renderer = null;

    /**
     * @var array
     */
    protected $distinctvalues = null;

    /**
     * Can this field be used in fieldgroups?
     * Only fields where user can enter data via a form can be used in a fieldgroup.
     * Override if yes.
     *
     * @var bool
     */
    protected $forfieldgroup = false;

    /**
     * Class constructor
     *
     * TODO: $df should always be an int value.
     *
     * @param number $df datalynx id or class object
     * @param number $field fieldid or fieldobject
     */
    public function __construct($df = 0, $field = 0) {
        if (empty($df)) {
            throw new coding_exception('Datalynx id or object must be passed to view constructor.');
        } else {
            if ($df instanceof \mod_datalynx\datalynx) {
                $this->df = $df;
            } else {
                // Construct the datalynx object from the integer id.
                $this->df = new mod_datalynx\datalynx($df);
            }
        }

        if (!empty($field)) {
            // Variable field is the field record.
            if (is_object($field)) {
                $this->field = $field; // Programmer knows what they are doing, we hope.
                // Variable $field is a field id.
            } else {
                if ($fieldobj = $this->df->get_field_from_id($field)) {
                    $this->field = $fieldobj->field;
                } else {
                    throw new moodle_exception('invalidfield', 'datalynx', null, null, $field);
                }
            }
        }

        if (empty($this->field)) { // We need to define some default values.
            $this->set_field();
        }
    }

    /**
     * Sets up a field object
     *
     * @param stdClass $forminput
     */
    public function set_field($forminput = null) {
        $this->field = new stdClass();
        $this->field->id = !empty($forminput->id) ? $forminput->id : 0;
        $this->field->type = $this->type;
        $this->field->dataid = $this->df->id();
        $this->field->name = !empty($forminput->name) ? trim($forminput->name) : '';
        $this->field->description = !empty($forminput->description) ? trim($forminput->description) : '';
        $this->field->visible = isset($forminput->visible) ? $forminput->visible : 2;
        $this->field->edits = isset($forminput->edits) ? $forminput->edits : -1;
        $this->field->label = !empty($forminput->label) ? $forminput->label : '';
        for ($i = 1; $i <= 10; $i++) {
            $this->field->{"param$i"} = !empty($forminput->{"param$i"}) ? trim(
                $forminput->{"param$i"}
            ) : null;
        }
    }

    /**
     * Insert a new field in the database
     *
     * @param ?stdClass $fromform Form data.
     * @return bool|int
     */
    public function insert_field($fromform = null) {
        global $DB, $OUTPUT;

        if (!empty($fromform)) {
            $this->set_field($fromform);
        }

        if (!$this->field->id = $DB->insert_record('datalynx_fields', $this->field)) {
            echo $OUTPUT->notification('Insertion of new field failed!');
            return false;
        } else {
            return $this->field->id;
        }
    }

    /**
     * Update a field in the database
     *
     * @param ?stdClass $fromform Form data.
     * @return bool
     */
    public function update_field($fromform = null) {
        global $DB, $OUTPUT;
        if (!empty($fromform)) {
            $this->set_field($fromform);
        }

        if (!$DB->update_record('datalynx_fields', $this->field)) {
            echo $OUTPUT->notification('updating of field failed!');
            return false;
        }
        return true;
    }

    /**
     * Delete a field completely
     */
    public function delete_field() {
        global $DB;

        if (!empty($this->field->id)) {
            if ($filearea = $this->filearea()) {
                $fs = get_file_storage();
                $fs->delete_area_files($this->df->context->id, 'mod_datalynx', $filearea);
            }
            $this->delete_content();
            $DB->delete_records('datalynx_fields', ['id' => $this->field->id]);
        }
        return true;
    }

    /**
     * Getter
     *
     * @param string $var Property name.
     * @return mixed
     */
    public function get($var) {
        if (isset($this->field->$var)) {
            return $this->field->$var;
        } else {
            // Returns false if the property does not exist on the field object.
            return false;
        }
    }

    /**
     * Returns the field id
     */
    public function id() {
        return $this->field->id;
    }

    /**
     * Returns the field type
     */
    public function type() {
        return $this->type;
    }

    /**
     * Returns the name of the field
     */
    public function name() {
        return $this->field->name;
    }

    /**
     * Returns the type name of the field
     */
    public function typename() {
        return get_string('pluginname', "datalynxfield_{$this->type}");
    }

    /**
     * Returns an icon representing this field type using Font Awesome classes.
     *
     * @return string HTML for the icon
     */
    public function image() {
        $iconmap = [
            'approve'                      => 'fa-solid fa-circle-check',
            'checkbox'                     => 'fa-solid fa-square-check',
            'comment'                      => 'fa-solid fa-comment',
            'coursegroup'                  => 'fa-solid fa-sitemap',
            'datalynxview'                 => 'fa-solid fa-eye',
            'duration'                     => 'fa-solid fa-hourglass-half',
            'editor'                       => 'fa-solid fa-pen-fancy',
            'entry'                        => 'fa-solid fa-database',
            'entryauthor'                  => 'fa-solid fa-user',
            'entrygroup'                   => 'fa-solid fa-users',
            'entryteammemberprofilefield'  => 'fa-solid fa-address-card',
            'entrytime'                    => 'fa-solid fa-calendar-days',
            'fieldgroup'                   => 'fa-solid fa-folder',
            'file'                         => 'fa-solid fa-file',
            'gradeitem'                    => 'fa-solid fa-graduation-cap',
            'identifier'                   => 'fa-solid fa-fingerprint',
            'multiselect'                  => 'fa-solid fa-list-check',
            'number'                       => 'fa-solid fa-hashtag',
            'picture'                      => 'fa-solid fa-image',
            'radiobutton'                  => 'fa-solid fa-circle-dot',
            'rating'                       => 'fa-solid fa-star',
            'select'                       => 'fa-solid fa-chevron-down',
            'status'                       => 'fa-solid fa-flag',
            'tag'                          => 'fa-solid fa-tag',
            'teammemberselect'             => 'fa-solid fa-people-group',
            'text'                         => 'fa-solid fa-font',
            'textarea'                     => 'fa-solid fa-align-left',
            'time'                         => 'fa-solid fa-clock',
            'url'                          => 'fa-solid fa-link',
            'userinfo'                     => 'fa-solid fa-circle-user',
            'youtube'                      => 'fa-brands fa-youtube',
        ];
        $classes = $iconmap[$this->type] ?? 'fa-solid fa-question-circle';
        return \html_writer::tag('i', '', ['class' => $classes, 'aria-hidden' => 'true']);
    }

    /**
     * Returns the datalynx instance this field belongs to.
     *
     * @return datalynx
     */
    public function df() {
        return $this->df;
    }

    /**
     * Returns the edit form for this field type.
     *
     * @return moodleform
     */
    public function get_form() {
        if (class_exists('datalynxfield_' . $this->type . '\form')) {
            $formclass = 'datalynxfield_' . $this->type . '\form';
        } else {
            $formclass = 'mod_datalynx\form\datalynxfield_form';
        }
        $actionurl = new moodle_url(
            '/mod/datalynx/field/field_edit.php',
            ['d' => $this->df->id(), 'fid' => $this->id(), 'type' => $this->type]
        );
        return new $formclass($this, $actionurl);
    }

    /**
     * Is this field available for fieldgroups?
     *
     * @return boolean
     */
    public function for_use_in_fieldgroup() {
        return $this->forfieldgroup;
    }

    /**
     * Returns the field record as a form data object.
     *
     * @return stdClass
     */
    public function to_form() {
        return $this->field;
    }

    /**
     *
     * @return datalynxfield_renderer
     */
    public function renderer() {
        if (!$this->renderer) {
            $rendererclass = "datalynxfield_{$this->type}\\renderer";
            $this->renderer = new $rendererclass($this);
        }
        return $this->renderer;
    }

    /**
     * @var array Default render options applied when displaying field patterns.
     */
    protected static $defaultoptions = ['manage' => false, 'visible' => false, 'edit' => false,
            'editable' => false, 'disabled' => false, 'required' => false, 'internal' => false,
    ];

    // CONTENT MANAGEMENT.

    /**
     * Get definition of tags/pattern
     *
     * @param array $tags
     * @param stdClass $entry
     * @param array $options
     * @return array
     */
    public function get_definitions($tags, $entry, array $options) {
        return $this->renderer()->replacements(
            $tags,
            $entry,
            array_merge(self::$defaultoptions, $options)
        );
    }

    /**
     * Returns whether this field is an internal (non-user-editable) field.
     *
     * @return bool
     */
    public static function is_internal() {
        return false;
    }

    /**
     * Update the content of a field in an entry. That happens in table datalynx_conents.
     *
     * @param stdClass $entry
     * @param ?array $values
     * @return bool|int
     * @throws dml_exception
     */
    public function update_content(stdClass $entry, ?array $values = null) {
        global $DB;
        $fieldid = $this->field->id;
        $fieldid = $this->field->id;
        $contentid = isset($entry->{"c{$fieldid}_id"}) ? $entry->{"c{$fieldid}_id"} : null;

        // If the field was not submitted at all (e.g. a disabled input is not sent by the browser),
        // $values is an empty array []. Preserve the existing content in that case.
        // Note: a field submitted with an empty value produces ['' => ''] (non-empty array),
        // which correctly bypasses this guard and proceeds to clear the stored content.
        if (is_array($values) && empty($values) && !is_null($contentid)) {
            return true;
        }

        [$contents, $oldcontents] = $this->format_content($entry, $values);

        $rec = new stdClass();
        $rec->fieldid = $this->field->id;
        $rec->entryid = $entry->id;
        foreach ($contents as $key => $content) {
            $c = $key ? $key : '';
            $rec->{"content$c"} = $content;
        }

        // Note: when the first value in a list is empty the insert is skipped and ordering may be wrong.

        // Insert only if no old contents and there is new contents.
        if (is_null($contentid) && !empty($contents)) {
            return $DB->insert_record('datalynx_contents', $rec);
        }

        // Note: only the specific content row is deleted, not all content for the entry id.
        if (!is_null($contentid) && empty($contents)) {
            return $this->delete_content($entry->id);
        }

        // Update if new is different from old.
        if (!is_null($contentid)) {
            foreach ($contents as $key => $content) {
                if (!isset($oldcontents[$key]) || $content !== $oldcontents[$key]) {
                    $rec->id = $contentid; // MUST_EXIST.
                    $DB->update_record('datalynx_contents', $rec);
                    return $rec->id;
                }
            }
        }
        return true;
    }

    /**
     * Delete all content associated with the field.
     *
     * @param int $entryid
     * @return bool
     * @throws dml_exception
     */
    public function delete_content($entryid = 0) {
        global $DB;

        if ($entryid) {
            $params = ['fieldid' => $this->field->id, 'entryid' => $entryid];
        } else {
            $params = ['fieldid' => $this->field->id];
        }

        $rs = $DB->get_recordset('datalynx_contents', $params);
        if ($rs->valid()) {
            $fs = get_file_storage();
            foreach ($rs as $content) {
                $fs->delete_area_files(
                    $this->df->context->id,
                    'mod_datalynx',
                    'content',
                    $content->id
                );
            }
        }
        $rs->close();

        return $DB->delete_records('datalynx_contents', $params);
    }

    /**
     * Returns an array of distinct content of the field (GROUP BY)
     *
     * @param int $sortdir
     * @return array
     * @throws dml_exception
     */
    public function get_distinct_content($sortdir = 0) {
        global $DB;

        if (is_null($this->distinctvalues)) {
            $this->distinctvalues = [];
            $fieldid = $this->field->id;
            $sortdir = $sortdir ? 'DESC' : 'ASC';
            $contentname = $this->get_sort_sql();
            $sql = "SELECT DISTINCT $contentname
            FROM {datalynx_contents} c$fieldid
            WHERE c$fieldid.fieldid = $fieldid AND $contentname IS NOT NULL
            ORDER BY $contentname $sortdir";

            if ($options = $DB->get_records_sql($sql)) {
                foreach ($options as $data) {
                    $value = isset($data->content) ? $data->content : '';
                    if ($value === '') {
                        continue;
                    }
                    $this->distinctvalues[] = $value;
                }
            }
        }
        return $this->distinctvalues;
    }

    /**
     * Prepares content from uploaded csv files to be saved to DB
     *
     * @param array $data
     * @param array $importsettings
     * @param ?array $csvrecord
     * @param ?int $entryid
     * @return bool
     */
    public function prepare_import_content(&$data, $importsettings, ?array $csvrecord = null, ?int $entryid = null) {
        $fieldid = $this->field->id;
        $fieldname = $this->name();
        // Internal fields use a different import settings structure.
        if ($this->is_internal()) {
            $setting = reset($importsettings);
            $csvname = $setting['name'];
        } else {
            $csvname = $importsettings[$fieldname]['name'];
        }

        if (isset($csvrecord[$csvname]) && $csvrecord[$csvname] !== '') {
            $data->{"field_{$fieldid}_{$entryid}"} = $csvrecord[$csvname];
        }

        return true;
    }

    /**
     * Retrieve the submitted form data for a specific field
     * and return as array indexed by contentname
     *
     * @param number $entryid
     * @param stdClass $data submitted form data
     * @return array with
     */
    public function get_content_from_data($entryid, $data) {
        $fieldid = $this->field->id;
        $content = [];
        $fieldgroups = [];
        foreach ($data as $key => $value) {
            if (strpos($key, 'fieldgroup_') === 0) {
                $fieldgroups[$value] = $key;
            }
        }

        // Keeping this for backwards compatibility.
        foreach ($this->content_names() as $name) {
            // Add normal fields.
            $delim = $name ? '_' : '';
            $contentname = "field_{$fieldid}_$entryid" . $delim . $name;
            if (isset($data->$contentname)) {
                $content[$name] = $data->$contentname;
            }
            // Add fieldgroup fields.
            foreach ($fieldgroups as $fieldgroup) {
                // Search for fieldgroup.
                $contentname = "field_{$fieldid}_$entryid" . "_" . $fieldgroup . "_0" . $delim . $name;
                $i = 0;
                while (isset($data->$contentname)) {
                    $content[$fieldgroup] = true;
                    $content[$contentname] = $data->$contentname;
                    $i++;
                    $contentname = "field_{$fieldid}_$entryid" . "_" . $fieldgroup . "_" . $i . $delim . $name;
                }
            }
        }

        return $content;
    }

    /**
     * This function should be overriden in each field class which extends this base class.
     * If a field has more than one form element where user content is expected to be submitted
     * all of these elements have to be specified here
     * Example: Field of type file has these form elements: 'filemanager', 'alttext', 'delete',
     * 'editor'
     * So these values have to be returned like that: return array('filemanager', 'alttext',
     * 'delete', 'editor');
     *
     * @return array of strings
     */
    protected function content_names() {
        return [''];
    }

    /**
     * Formats content for database storage
     *
     * @param stdClass $entry stdClass object containing all the entry contents (from the database, NOT the
     *        form!)
     * @param ?array $values values from the entry form elements
     * @return array
     */
    protected function format_content(stdClass $entry, ?array $values = null) {
        $fieldid = $this->field->id;

        $newcontent = null;
        $oldcontent = null;

        if (!empty($values)) {
            $resetted = reset($values);
            $newcontent = !empty($resetted) ? $resetted : "";
            if (!empty($newcontent) && is_array($newcontent)) {
                // When no value ist selected, then a default value is saved by Quickform. Value is removed here:.
                if (($key = array_search('_qf__force_multiselect_submission', $newcontent)) !== false) {
                    unset($newcontent[$key]);
                }
                $newcontent = implode(",", $newcontent);
            }
            $newcontent = (string) clean_param($newcontent, PARAM_NOTAGS);
        }

        if (isset($entry->{"c{$fieldid}_content"})) {
            $oldcontent = $entry->{"c{$fieldid}_content"};
        }

        return [[$newcontent], [$oldcontent]];
    }

    /**
     * Checks whether the field supports 'group by' filtering option
     *
     * @return bool true if 'group by' is supported, false otherwise
     */
    public function supports_group_by() {
        return false;
    }

    /**
     * To be overriden by classes, that extend the field base class
     * This returns all the column names of the columns used to save content of one specific field
     * in the table "datalynx_contents".
     * Values can be 'content', 'content1', 'content2', until 'content4'
     *
     * @return array of strings
     */
    public function get_content_parts() {
        return ['content'];
    }

    /**
     * Get the select part of the sql query.
     *
     * @return string
     */
    public function get_select_sql() {
        if (is_numeric($this->field->id) && $this->field->id > 0) {
            $arr = [];
            $arr[] = " c{$this->field->id}.id AS c{$this->field->id}_id ";
            foreach ($this->get_content_parts() as $part) {
                $arr[] = $this->get_sql_compare_text($part) . " AS c{$this->field->id}_$part";
            }
            $selectsql = implode(',', $arr);
            return " $selectsql ";
        } else {
            return '';
        }
    }

    /**
     * Get the sort part of the sql query.
     *
     * @param string $paramname
     * @param string $paramcount
     * @return ?array
     */
    public function get_sort_from_sql($paramname = 'sortie', $paramcount = '') {
        $fieldid = $this->field->id;
        if (is_numeric($fieldid) && $fieldid > 0) {
            $sql = " LEFT JOIN {datalynx_contents} c$fieldid
            ON (c$fieldid.entryid = e.id AND c$fieldid.fieldid = :$paramname$paramcount)";
            return [$sql, $fieldid];
        } else {
            return null;
        }
    }

    /**
     * Returns the SQL fragment used for sorting by this field.
     *
     * @return string
     */
    public function get_sort_sql() {
        return $this->get_sql_compare_text();
    }

    /**
     * Returns the SQL JOIN fragment used when searching by this field.
     *
     * @return string
     */
    public function get_search_from_sql() {
        $fieldid = $this->field->id;
        if (is_numeric($fieldid) && $fieldid > 0) {
            return " LEFT JOIN {datalynx_contents} c$fieldid ON c$fieldid.entryid = e.id AND c$fieldid.fieldid = $fieldid ";
        } else {
            return '';
        }
    }

    /**
     * Create the SQL query based on different params.
     *
     * @param array $search
     * @return array $fieldsql, $fieldparams, $fromcontent or empty array
     */
    public function get_search_sql(array $search): array {
        global $DB;

        [$not, $operator, $value] = $search;

        static $i = 0;
        $i++;
        $fieldid = $this->field->id;
        $name = "df_{$fieldid}_{$i}";

        // For all NOT criteria except NOT Empty, exclude entries.
        // Which don't meet the positive criterion.
        // Because some fields may not have content records.
        // And the respective entries may be filter out.
        // Despite meeting the criterion.
        $excludeentries = (($not && $operator !== '') || (!$not && $operator === ''));

        if ($excludeentries) {
            $varcharcontent = $DB->sql_compare_text('content');
        } else {
            $varcharcontent = $this->get_sql_compare_text();
        }

        if ($operator === '') {
            [$sql, $params] = $DB->get_in_or_equal(
                '',
                SQL_PARAMS_NAMED,
                "df_{$fieldid}_",
                false
            );
            $sql = " $varcharcontent $sql ";
        } else {
            if ($operator === '=') {
                $searchvalue = trim($value);
                [$sql, $params] = $DB->get_in_or_equal(
                    $searchvalue,
                    SQL_PARAMS_NAMED,
                    "df_{$fieldid}_"
                );
                $sql = " $varcharcontent $sql ";
            } else {
                if ($operator === 'IN') {
                    $searchvalue = array_map('trim', $value);
                    [$sql, $params] = $DB->get_in_or_equal(
                        $searchvalue,
                        SQL_PARAMS_NAMED,
                        "df_{$fieldid}_"
                    );
                    $sql = " $varcharcontent $sql ";
                } else {
                    if (in_array($operator, ['LIKE', 'BETWEEN', ''])) {
                        $params = [$name => "%$value%"];
                        $sql = $DB->sql_like($varcharcontent, ":$name", false);
                    } else {
                        $params = [$name => "'$value'"];
                        $sql = " $varcharcontent $operator :$name ";
                    }
                }
            }
        }

        if ($excludeentries) {
            // Get entry ids for entries that meet the criterion.
            if ($eids = $this->get_entry_ids_for_content($sql, $params)) {
                // Get NOT IN sql.
                [$notinids, $params] = $DB->get_in_or_equal(
                    $eids,
                    SQL_PARAMS_NAMED,
                    "df_{$fieldid}_",
                    false
                );
                $sql = " e.id $notinids ";
                return [$sql, $params, false];
            } else {
                return ['', '', ''];
            }
        } else {
            return [$sql, $params, true];
        }
    }

    /**
     * Get the entry ids for the specific sql using the field id and the field value.
     *
     * @param string $sql
     * @param array $params
     * @return array
     */
    protected function get_entry_ids_for_content(string $sql, array $params): array {
        global $DB;
        $sql = " fieldid = :fieldid AND $sql ";
        $params['fieldid'] = $this->id();
        return $DB->get_records_select_menu('datalynx_contents', $sql, $params, '', 'id,entryid');
    }

    /**
     * Extracts the search value for this field from submitted form data.
     *
     * @param stdClass $formdata
     * @param int $i
     * @return mixed
     */
    public function parse_search($formdata, $i) {
        $fieldid = $this->field->id;
        if (!empty($formdata->{"f_{$i}_$fieldid"})) {
            return $formdata->{"f_{$i}_$fieldid"};
        } else {
            return false;
        }
    }

    /**
     * Formats the search parameters for display.
     *
     * @param array $searchparams
     * @return string|false
     */
    public function format_search_value($searchparams) {
        [$not, $operator, $value] = $searchparams;
        return $not . ' ' . $operator . ' ' . $value;
    }

    /**
     * Normalises and returns the search value ready for SQL comparison.
     *
     * @param mixed $value
     * @return mixed
     */
    public function get_search_value($value) {
        return $value;
    }

    /**
     * Returns the SQL fragment for comparing the field content column.
     *
     * @param string $column
     * @return string
     */
    protected function get_sql_compare_text(string $column = 'content'): string {
        global $DB;
        return $DB->sql_compare_text("c{$this->field->id}.$column");
    }

    /**
     * Whether this field provides join sql for fetching content
     *
     * @return bool
     */
    public function use_join() {
        return false;
    }

    /**
     * Whether this field content resides in datalynx_contents
     *
     * @return bool
     */
    public function is_datalynx_content() {
        return true;
    }

    /**
     * Return file area of field
     *
     * @param string $suffix
     * @return string|boolean
     */
    protected function filearea($suffix = null) {
        if (!empty($suffix)) {
            return 'field-' . str_replace(' ', '_', $suffix);
        } else {
            if (!empty($this->field->name)) {
                return 'field-' . str_replace(' ', '_', $this->field->name);
            } else {
                return false;
            }
        }
    }

    /**
     * Returns operators supported by this field.
     * It contains all operators present in the previous
     * version and should therefore be overridden by a concrete
     * field class to remove unsupported operators from the list.
     *
     * @return array an array of operators
     */
    public function get_supported_search_operators() {
        return []; // If search is not supported, offer no operators.
    }

    /**
     * Returns the number of arguments required for the given search operator.
     *
     * @param string $operator
     * @return int
     */
    public function get_argument_count(string $operator) {
        if ($operator === "") { // Empty operator requires no argument.
            return 0;
        } else {
            return 1;
        }
    }

    /**
     * Are fields of this field type suitable for use in customfilters?
     *
     * @return bool
     */
    public static function is_customfilterfield() {
        return false;
    }

    /**
     * Is $value a valid content or do we see an empty input?
     *
     * @param mixed $value The value to check.
     * @return bool
     */
    public static function is_fieldvalue_empty($value) {
        if ($value == '') {
            return true;
        }
        return false;
    }
}
