<?php

namespace SilverStripe\Forms;

/**
 * NullableField is a field that wraps other fields when you want to allow the user to specify
 * whether the value of the field is null or not.
 *
 * The classic case is to wrap a TextField so that the user can distinguish between an empty string
 * and a null string.
 *
 * $a = new NullableField(new TextField("Field1", "Field 1", "abc"));
 *
 * It displays the field that is wrapped followed by a checkbox that is used to specify if the
 * value is null or not. It uses the Title of the wrapped field for its title.
 *
 * When a form is submitted the field tests the value of the "is null" checkbox and sets its value
 * accordingly. You can retrieve the value of the wrapped field from the NullableField as follows:
 *
 * $field->Value() or $field->dataValue()
 *
 * You can specify the label to use for the "is null" checkbox. If you want to use i18n for this
 * label then specify it like this:
 *
 * $field->setIsNullLabel(_T(SOME_MODULE_ISNULL_LABEL, "Is Null"));
 *
 * @author Pete Bacon Darwin
 */
class NullableField extends FormField
{
    /**
     * The field that holds the value of this field
     *
     * @var FormField
     */
    protected $valueField;

    /**
     * The label to show next to the is null check box.
     *
     * @var string
     */
    protected $isNullLabel;

    /**
     * Create a new nullable field
     *
     * @param FormField $valueField
     * @param null|string $isNullLabel
     */
    public function __construct(FormField $valueField, $isNullLabel = null)
    {
        $this->valueField = $valueField;

        if (isset($isNullLabel)) {
            $this->setIsNullLabel($isNullLabel);
        } else {
            $this->isNullLabel = _t('SilverStripe\\Forms\\NullableField.IsNullLabel', 'Is Null');
        }

        parent::__construct(
            $valueField->getName(),
            $valueField->Title(),
            $valueField->Value()
        );

        $this->setForm($valueField->getForm());
        $this->setRightTitle($valueField->RightTitle());
        $this->setReadonly($valueField->isReadonly());
    }

    /**
     * Get the label used for the Is Null checkbox.
     *
     * @return string
     */
    public function getIsNullLabel()
    {
        return $this->isNullLabel;
    }

    /**
     * Set the label used for the Is Null checkbox.
     *
     * @param $isNulLabel string
     *
     * @return $this
     */
    public function setIsNullLabel($isNulLabel)
    {
        $this->isNullLabel = $isNulLabel;

        return $this;
    }

    /**
     * Get the id used for the Is Null check box.
     *
     * @return string
     */
    public function getIsNullId()
    {
        return $this->getName() . "_IsNull";
    }

    /**
     * @param array $properties
     *
     * @return string
     */
    public function Field($properties = array())
    {
        if ($this->isReadonly()) {
            $nullableCheckbox = new CheckboxField_Readonly($this->getIsNullId());
        } else {
            $nullableCheckbox = new CheckboxField($this->getIsNullId());
        }

        $nullableCheckbox->setValue(is_null($this->dataValue()));

        return sprintf(
            '%s %s&nbsp;<span>%s</span>',
            $this->valueField->Field(),
            $nullableCheckbox->Field(),
            $this->getIsNullLabel()
        );
    }

    /**
     * Value is sometimes an array, and sometimes a single value, so we need to handle both cases
     *
     * @param mixed $value
     * @param null|array $data
     *
     * @return $this
     */
    public function setValue($value, $data = null)
    {
        $id = $this->getIsNullId();

        if (is_array($data) && array_key_exists($id, $data) && $data[$id]) {
            $value = null;
        }

        $this->valueField->setValue($value);

        parent::setValue($value);

        return $this;
    }

    /**
     * @param string $name
     *
     * @return $this
     */
    public function setName($name)
    {
        $this->valueField->setName($name);

        parent::setName($name);

        return $this;
    }

    /**
     * @return string
     */
    public function debug()
    {
        $result = sprintf(
            '%s (%s: $s : <span style="color: red">%s</span>) = ',
            static::class,
            $this->name,
            $this->title,
            $this->message
        );

        if ($this->value === null) {
            $result .= "<<null>>";
        } else {
            $result .= (string) $this->value;
        }

        return $result;
    }
}
