/*
 * Copyright (C) 2012 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"
#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
#include "DateTimeEditElement.h"

#include "DateComponents.h"
#include "DateTimeFieldElements.h"
#include "DateTimeFieldsState.h"
#include "DateTimeFormat.h"
#include "DateTimeSymbolicFieldElement.h"
#include "EventHandler.h"
#include "FontCache.h"
#include "HTMLNames.h"
#include "KeyboardEvent.h"
#include "MouseEvent.h"
#include "PlatformLocale.h"
#include "RenderStyle.h"
#include "StyleResolver.h"
#include "Text.h"
#include <wtf/DateMath.h>
#include <wtf/text/StringBuilder.h>

namespace WebCore {

using namespace HTMLNames;
using namespace WTF::Unicode;

class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
    WTF_MAKE_NONCOPYABLE(DateTimeEditBuilder);

public:
    // The argument objects must be alive until this object dies.
    DateTimeEditBuilder(DateTimeEditElement&, const DateTimeEditElement::LayoutParameters&, const DateComponents&);

    bool build(const String&);

private:
    bool needMillisecondField() const;
    bool shouldAMPMFieldDisabled() const;
    bool shouldDayOfMonthFieldDisabled() const;
    bool shouldHourFieldDisabled() const;
    bool shouldMillisecondFieldDisabled() const;
    bool shouldMinuteFieldDisabled() const;
    bool shouldSecondFieldDisabled() const;
    bool shouldYearFieldDisabled() const;
    inline const StepRange& stepRange() const { return m_parameters.stepRange; }
    DateTimeNumericFieldElement::Parameters createNumericFieldParameters(const Decimal& msPerFieldUnit, const Decimal& msPerFieldSize) const;

    // DateTimeFormat::TokenHandler functions.
    virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
    virtual void visitLiteral(const String&) OVERRIDE FINAL;

    DateTimeEditElement& m_editElement;
    const DateComponents m_dateValue;
    const DateTimeEditElement::LayoutParameters& m_parameters;
    int m_minDay;
    int m_maxDay;
    int m_minHour23;
    int m_maxHour23;
    int m_minMinute;
    int m_maxMinute;
    int m_minSecond;
    int m_maxSecond;
    int m_minMillisecond;
    int m_maxMillisecond;
};

DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& elemnt, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
    : m_editElement(elemnt)
    , m_dateValue(dateValue)
    , m_parameters(layoutParameters)
    , m_minDay(1)
    , m_maxDay(31)
    , m_minHour23(0)
    , m_maxHour23(23)
    , m_minMinute(0)
    , m_maxMinute(59)
    , m_minSecond(0)
    , m_maxSecond(59)
    , m_minMillisecond(0)
    , m_maxMillisecond(999)
{
    if (m_dateValue.type() == DateComponents::Date
        || m_dateValue.type() == DateComponents::DateTimeLocal
        || m_dateValue.type() == DateComponents::DateTime) {
        if (m_parameters.minimum.type() != DateComponents::Invalid
            && m_parameters.maximum.type() != DateComponents::Invalid
            && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
            && m_parameters.minimum.month() == m_parameters.maximum.month()
            && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
            m_minDay = m_parameters.minimum.monthDay();
            m_maxDay = m_parameters.maximum.monthDay();
        }
    }

    if (m_dateValue.type() == DateComponents::Time || m_minDay == m_maxDay) {
        if (m_parameters.minimum.type() != DateComponents::Invalid
            && m_parameters.maximum.type() != DateComponents::Invalid
            && m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
            m_minHour23 = m_parameters.minimum.hour();
            m_maxHour23 = m_parameters.maximum.hour();
        }
    }

    if (m_minHour23 == m_maxHour23 && m_parameters.minimum.minute() <= m_parameters.maximum.minute()) {
        m_minMinute = m_parameters.minimum.minute();
        m_maxMinute = m_parameters.maximum.minute();
    }
    if (m_minMinute == m_maxMinute && m_parameters.minimum.second() <= m_parameters.maximum.second()) {
        m_minSecond = m_parameters.minimum.second();
        m_maxSecond = m_parameters.maximum.second();
    }
    if (m_minSecond == m_maxSecond && m_parameters.minimum.millisecond() <= m_parameters.maximum.millisecond()) {
        m_minMillisecond = m_parameters.minimum.millisecond();
        m_maxMillisecond = m_parameters.maximum.millisecond();
    }
}

bool DateTimeEditBuilder::build(const String& formatString)
{
    m_editElement.resetFields();
    return DateTimeFormat::parse(formatString, *this);
}

bool DateTimeEditBuilder::needMillisecondField() const
{
    return m_dateValue.millisecond()
        || !stepRange().minimum().remainder(static_cast<int>(msPerSecond)).isZero()
        || !stepRange().step().remainder(static_cast<int>(msPerSecond)).isZero();
}

void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int count)
{
    const int countForAbbreviatedMonth = 3;
    const int countForFullMonth = 4;
    const int countForNarrowMonth = 5;
    Document* const document = m_editElement.document();

    switch (fieldType) {
    case DateTimeFormat::FieldTypeDayOfMonth: {
        RefPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, m_minDay, m_maxDay);
        m_editElement.addField(field);
        if (shouldDayOfMonthFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeHour11: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerHour * 12));
        RefPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
        m_editElement.addField(field);
        if (shouldHourFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeHour12: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerHour * 12));
        RefPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
        m_editElement.addField(field);
        if (shouldHourFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeHour23: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerDay));
        RefPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
        m_editElement.addField(field);
        if (shouldHourFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeHour24: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerDay));
        RefPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
        m_editElement.addField(field);
        if (shouldHourFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeMinute: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerMinute), static_cast<int>(msPerHour));
        RefPtr<DateTimeNumericFieldElement> field = DateTimeMinuteFieldElement::create(document, m_editElement, m_minMinute, m_maxMinute, parameters);
        m_editElement.addField(field);
        if (shouldMinuteFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeMonth: // Fallthrough.
    case DateTimeFormat::FieldTypeMonthStandAlone: {
        int minMonth = 0, maxMonth = 11;
        if (m_parameters.minimum.type() != DateComponents::Invalid
            && m_parameters.maximum.type() != DateComponents::Invalid
            && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
            && m_parameters.minimum.month() <= m_parameters.maximum.month()) {
            minMonth = m_parameters.minimum.month();
            maxMonth = m_parameters.maximum.month();
        }
        RefPtr<DateTimeFieldElement> field;
        switch (count) {
        case countForNarrowMonth: // Fallthrough.
        case countForAbbreviatedMonth:
            field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.shortMonthLabels() : m_parameters.locale.shortStandAloneMonthLabels(), minMonth, maxMonth);
            break;
        case countForFullMonth:
            field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.monthLabels() : m_parameters.locale.standAloneMonthLabels(), minMonth, maxMonth);
            break;
        default:
            field = DateTimeMonthFieldElement::create(document, m_editElement, m_parameters.placeholderForMonth, minMonth + 1, maxMonth + 1);
            break;
        }
        m_editElement.addField(field);
        if (minMonth == maxMonth && minMonth == m_dateValue.month() && m_dateValue.type() != DateComponents::Month) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypePeriod: {
        RefPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels());
        m_editElement.addField(field);
        if (shouldAMPMFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeSecond: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerSecond), static_cast<int>(msPerMinute));
        RefPtr<DateTimeNumericFieldElement> field = DateTimeSecondFieldElement::create(document, m_editElement, m_minSecond, m_maxSecond, parameters);
        m_editElement.addField(field);
        if (shouldSecondFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }

        if (needMillisecondField()) {
            visitLiteral(m_parameters.locale.localizedDecimalSeparator());
            visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
        }
        return;
    }

    case DateTimeFormat::FieldTypeFractionalSecond: {
        DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(1, static_cast<int>(msPerSecond));
        RefPtr<DateTimeNumericFieldElement> field = DateTimeMillisecondFieldElement::create(document, m_editElement, m_minMillisecond, m_maxMillisecond, parameters);
        m_editElement.addField(field);
        if (shouldMillisecondFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    case DateTimeFormat::FieldTypeWeekOfYear: {
        int minWeek = DateComponents::minimumWeekNumber;
        int maxWeek = DateComponents::maximumWeekNumber;
        if (m_parameters.minimum.type() != DateComponents::Invalid
            && m_parameters.maximum.type() != DateComponents::Invalid
            && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
            && m_parameters.minimum.week() <= m_parameters.maximum.week()) {
            minWeek = m_parameters.minimum.week();
            maxWeek = m_parameters.maximum.week();
        }
        m_editElement.addField(DateTimeWeekFieldElement::create(document, m_editElement, minWeek, maxWeek));
        return;
    }

    case DateTimeFormat::FieldTypeYear: {
        DateTimeYearFieldElement::Parameters yearParams;
        if (m_parameters.minimum.type() == DateComponents::Invalid) {
            yearParams.minimumYear = DateComponents::minimumYear();
            yearParams.minIsSpecified = false;
        } else {
            yearParams.minimumYear = m_parameters.minimum.fullYear();
            yearParams.minIsSpecified = true;
        }
        if (m_parameters.maximum.type() == DateComponents::Invalid) {
            yearParams.maximumYear = DateComponents::maximumYear();
            yearParams.maxIsSpecified = false;
        } else {
            yearParams.maximumYear = m_parameters.maximum.fullYear();
            yearParams.maxIsSpecified = true;
        }
        if (yearParams.minimumYear > yearParams.maximumYear) {
            std::swap(yearParams.minimumYear, yearParams.maximumYear);
            std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
        }
        yearParams.placeholder = m_parameters.placeholderForYear;
        RefPtr<DateTimeFieldElement> field = DateTimeYearFieldElement::create(document, m_editElement, yearParams);
        m_editElement.addField(field);
        if (shouldYearFieldDisabled()) {
            field->setValueAsDate(m_dateValue);
            field->setDisabled();
        }
        return;
    }

    default:
        return;
    }
}

bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
{
    return shouldHourFieldDisabled()
        || (m_minHour23 < 12 && m_maxHour23 < 12 && m_dateValue.hour() < 12)
        || (m_minHour23 >= 12 && m_maxHour23 >= 12 && m_dateValue.hour() >= 12);
}

bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
{
    return m_minDay == m_maxDay && m_minDay == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
}

bool DateTimeEditBuilder::shouldHourFieldDisabled() const
{
    if (m_minHour23 == m_maxHour23 && m_minHour23 == m_dateValue.hour()
        && !(shouldMinuteFieldDisabled() && shouldSecondFieldDisabled() && shouldMillisecondFieldDisabled()))
        return true;

    if (m_dateValue.type() == DateComponents::Time)
        return false;
    ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal || m_dateValue.type() == DateComponents::DateTime);

    if (shouldDayOfMonthFieldDisabled()) {
        ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
        ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
        return false;
    }

    const Decimal decimalMsPerDay(static_cast<int>(msPerDay));
    Decimal hourPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerDay) / static_cast<int>(msPerHour)).floor();
    return hourPartOfMinimum == m_dateValue.hour() && stepRange().step().remainder(decimalMsPerDay).isZero();
}

bool DateTimeEditBuilder::shouldMillisecondFieldDisabled() const
{
    if (m_minMillisecond == m_maxMillisecond && m_minMillisecond == m_dateValue.millisecond())
        return true;

    const Decimal decimalMsPerSecond(static_cast<int>(msPerSecond));
    return stepRange().minimum().abs().remainder(decimalMsPerSecond) == m_dateValue.millisecond() && stepRange().step().remainder(decimalMsPerSecond).isZero();
}

bool DateTimeEditBuilder::shouldMinuteFieldDisabled() const
{
    if (m_minMinute == m_maxMinute && m_minMinute == m_dateValue.minute())
        return true;

    const Decimal decimalMsPerHour(static_cast<int>(msPerHour));
    Decimal minutePartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerHour) / static_cast<int>(msPerMinute)).floor();
    return minutePartOfMinimum == m_dateValue.minute() && stepRange().step().remainder(decimalMsPerHour).isZero();
}

bool DateTimeEditBuilder::shouldSecondFieldDisabled() const
{
    if (m_minSecond == m_maxSecond && m_minSecond == m_dateValue.second())
        return true;

    const Decimal decimalMsPerMinute(static_cast<int>(msPerMinute));
    Decimal secondPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerMinute) / static_cast<int>(msPerSecond)).floor();
    return secondPartOfMinimum == m_dateValue.second() && stepRange().step().remainder(decimalMsPerMinute).isZero();
}

bool DateTimeEditBuilder::shouldYearFieldDisabled() const
{
    return m_parameters.minimum.type() != DateComponents::Invalid
        && m_parameters.maximum.type() != DateComponents::Invalid
        && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
        && m_parameters.minimum.fullYear() == m_dateValue.fullYear();
}

void DateTimeEditBuilder::visitLiteral(const String& text)
{
    DEFINE_STATIC_LOCAL(AtomicString, textPseudoId, ("-webkit-datetime-edit-text", AtomicString::ConstructFromLiteral));
    ASSERT(text.length());
    RefPtr<HTMLDivElement> element = HTMLDivElement::create(m_editElement.document());
    element->setPseudo(textPseudoId);
    if (m_parameters.locale.isRTL() && text.length()) {
        Direction dir = direction(text[0]);
        if (dir == SegmentSeparator || dir == WhiteSpaceNeutral || dir == OtherNeutral)
            element->appendChild(Text::create(m_editElement.document(), String(&rightToLeftMark, 1)));
    }
    element->appendChild(Text::create(m_editElement.document(), text));
    m_editElement.fieldsWrapperElement()->appendChild(element);
}

DateTimeNumericFieldElement::Parameters DateTimeEditBuilder::createNumericFieldParameters(const Decimal& msPerFieldUnit, const Decimal& msPerFieldSize) const
{
    ASSERT(!msPerFieldUnit.isZero());
    ASSERT(!msPerFieldSize.isZero());
    Decimal stepMilliseconds = stepRange().step();
    ASSERT(!stepMilliseconds.isZero());

    DateTimeNumericFieldElement::Parameters parameters(1, 0);

    if (stepMilliseconds.remainder(msPerFieldSize).isZero())
        stepMilliseconds = msPerFieldSize;

    if (msPerFieldSize.remainder(stepMilliseconds).isZero() && stepMilliseconds.remainder(msPerFieldUnit).isZero()) {
        parameters.step = static_cast<int>((stepMilliseconds / msPerFieldUnit).toDouble());
        parameters.stepBase = static_cast<int>((stepRange().stepBase() / msPerFieldUnit).floor().remainder(msPerFieldSize / msPerFieldUnit).toDouble());
    }
    return parameters;
}

// ----------------------------

DateTimeEditElement::EditControlOwner::~EditControlOwner()
{
}

DateTimeEditElement::DateTimeEditElement(Document* document, EditControlOwner& editControlOwner)
    : HTMLDivElement(divTag, document)
    , m_editControlOwner(&editControlOwner)
{
    DEFINE_STATIC_LOCAL(AtomicString, dateTimeEditPseudoId, ("-webkit-datetime-edit", AtomicString::ConstructFromLiteral));
    setPseudo(dateTimeEditPseudoId);
    setHasCustomStyleCallbacks();
}

DateTimeEditElement::~DateTimeEditElement()
{
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
        m_fields[fieldIndex]->removeEventHandler();
}

inline Element* DateTimeEditElement::fieldsWrapperElement() const
{
    ASSERT(firstChild());
    return toElement(firstChild());
}

void DateTimeEditElement::addField(PassRefPtr<DateTimeFieldElement> field)
{
    if (m_fields.size() == m_fields.capacity())
        return;
    m_fields.append(field.get());
    fieldsWrapperElement()->appendChild(field);
}

bool DateTimeEditElement::anyEditableFieldsHaveValues() const
{
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
        if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
            return true;
    }
    return false;
}

void DateTimeEditElement::blurByOwner()
{
    if (DateTimeFieldElement* field = focusedField())
        field->blur();
}

PassRefPtr<DateTimeEditElement> DateTimeEditElement::create(Document* document, EditControlOwner& editControlOwner)
{
    RefPtr<DateTimeEditElement> container = adoptRef(new DateTimeEditElement(document, editControlOwner));
    return container.release();
}

PassRefPtr<RenderStyle> DateTimeEditElement::customStyleForRenderer()
{
    // FIXME: This is a kind of layout. We might want to introduce new renderer.
    FontCachePurgePreventer fontCachePurgePreventer;
    RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForElement(this);
    RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
    float width = 0;
    for (Node* child = fieldsWrapperElement()->firstChild(); child; child = child->nextSibling()) {
        if (!child->isElementNode())
            continue;
        Element* childElement = toElement(child);
        if (childElement->isDateTimeFieldElement()) {
            // We need to pass the Font of this element because child elements
            // can't resolve inherited style at this timing.
            width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font());
        } else {
            // ::-webkit-datetime-edit-text case. It has no
            // border/padding/margin in html.css.
            width += style->font().width(childElement->textContent());
        }
    }
    style->setWidth(Length(ceilf(width), Fixed));
    return style.release();
}

void DateTimeEditElement::didBlurFromField()
{
    if (m_editControlOwner)
        m_editControlOwner->didBlurFromControl();
}

void DateTimeEditElement::didFocusOnField()
{
    if (m_editControlOwner)
        m_editControlOwner->didFocusOnControl();
}

void DateTimeEditElement::disabledStateChanged()
{
    updateUIState();
}

DateTimeFieldElement* DateTimeEditElement::fieldAt(size_t fieldIndex) const
{
    return fieldIndex < m_fields.size() ? m_fields[fieldIndex] : 0;
}

size_t DateTimeEditElement::fieldIndexOf(const DateTimeFieldElement& field) const
{
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
        if (m_fields[fieldIndex] == &field)
            return fieldIndex;
    }
    return invalidFieldIndex;
}

void DateTimeEditElement::focusIfNoFocus()
{
    if (focusedFieldIndex() != invalidFieldIndex)
        return;
    focusOnNextFocusableField(0);
}

void DateTimeEditElement::focusByOwner(Node* oldFocusedNode)
{
    if (oldFocusedNode && oldFocusedNode->isElementNode() && toElement(oldFocusedNode)->isDateTimeFieldElement()) {
        DateTimeFieldElement* oldFocusedField = static_cast<DateTimeFieldElement*>(oldFocusedNode);
        size_t index = fieldIndexOf(*oldFocusedField);
        if (index != invalidFieldIndex && oldFocusedField->isFocusable()) {
            oldFocusedField->focus();
            return;
        }
    }
    focusOnNextFocusableField(0);
}

DateTimeFieldElement* DateTimeEditElement::focusedField() const
{
    return fieldAt(focusedFieldIndex());
}

size_t DateTimeEditElement::focusedFieldIndex() const
{
    Node* const focusedFieldNode = document()->focusedNode();
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
        if (m_fields[fieldIndex] == focusedFieldNode)
            return fieldIndex;
    }
    return invalidFieldIndex;
}

void DateTimeEditElement::fieldValueChanged()
{
    if (m_editControlOwner)
        m_editControlOwner->editControlValueChanged();
}

bool DateTimeEditElement::focusOnNextFocusableField(size_t startIndex)
{
    for (size_t fieldIndex = startIndex; fieldIndex < m_fields.size(); ++fieldIndex) {
        if (m_fields[fieldIndex]->isFocusable()) {
            m_fields[fieldIndex]->focus();
            return true;
        }
    }
    return false;
}

bool DateTimeEditElement::focusOnNextField(const DateTimeFieldElement& field)
{
    const size_t startFieldIndex = fieldIndexOf(field);
    if (startFieldIndex == invalidFieldIndex)
        return false;
    return focusOnNextFocusableField(startFieldIndex + 1);
}

bool DateTimeEditElement::focusOnPreviousField(const DateTimeFieldElement& field)
{
    const size_t startFieldIndex = fieldIndexOf(field);
    if (startFieldIndex == invalidFieldIndex)
        return false;
    size_t fieldIndex = startFieldIndex;
    while (fieldIndex > 0) {
        --fieldIndex;
        if (m_fields[fieldIndex]->isFocusable()) {
            m_fields[fieldIndex]->focus();
            return true;
        }
    }
    return false;
}

bool DateTimeEditElement::isDisabled() const
{
    return m_editControlOwner && m_editControlOwner->isEditControlOwnerDisabled();
}

bool DateTimeEditElement::isFieldOwnerDisabled() const
{
    return isDisabled();
}

bool DateTimeEditElement::isFieldOwnerReadOnly() const
{
    return isReadOnly();
}

bool DateTimeEditElement::isReadOnly() const
{
    return m_editControlOwner && m_editControlOwner->isEditControlOwnerReadOnly();
}

void DateTimeEditElement::layout(const LayoutParameters& layoutParameters, const DateComponents& dateValue)
{
    DEFINE_STATIC_LOCAL(AtomicString, fieldsWrapperPseudoId, ("-webkit-datetime-edit-fields-wrapper", AtomicString::ConstructFromLiteral));
    if (!firstChild()) {
        RefPtr<HTMLDivElement> element = HTMLDivElement::create(document());
        element->setPseudo(fieldsWrapperPseudoId);
        appendChild(element.get());
    }
    Element* fieldsWrapper = fieldsWrapperElement();

    size_t focusedFieldIndex = this->focusedFieldIndex();
    DateTimeFieldElement* const focusedField = fieldAt(focusedFieldIndex);
    const AtomicString focusedFieldId = focusedField ? focusedField->shadowPseudoId() : nullAtom;

    DateTimeEditBuilder builder(*this, layoutParameters, dateValue);
    Node* lastChildToBeRemoved = fieldsWrapper->lastChild();
    if (!builder.build(layoutParameters.dateTimeFormat) || m_fields.isEmpty()) {
        lastChildToBeRemoved = fieldsWrapper->lastChild();
        builder.build(layoutParameters.fallbackDateTimeFormat);
    }

    if (focusedFieldIndex != invalidFieldIndex) {
        for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
            if (m_fields[fieldIndex]->shadowPseudoId() == focusedFieldId) {
                focusedFieldIndex = fieldIndex;
                break;
            }
        }
        if (DateTimeFieldElement* field = fieldAt(std::min(focusedFieldIndex, m_fields.size() - 1)))
            field->focus();
    }

    if (lastChildToBeRemoved) {
        for (Node* childNode = fieldsWrapper->firstChild(); childNode; childNode = fieldsWrapper->firstChild()) {
            fieldsWrapper->removeChild(childNode);
            if (childNode == lastChildToBeRemoved)
                break;
        }
        setNeedsStyleRecalc();
    }
}

AtomicString DateTimeEditElement::localeIdentifier() const
{
    return m_editControlOwner ? m_editControlOwner->localeIdentifier() : nullAtom;
}

void DateTimeEditElement::readOnlyStateChanged()
{
    updateUIState();
}

void DateTimeEditElement::resetFields()
{
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
        m_fields[fieldIndex]->removeEventHandler();
    m_fields.shrink(0);
}

void DateTimeEditElement::defaultEventHandler(Event* event)
{
    // In case of control owner forward event to control, e.g. DOM
    // dispatchEvent method.
    if (DateTimeFieldElement* field = focusedField()) {
        field->defaultEventHandler(event);
        if (event->defaultHandled())
            return;
    }

    HTMLDivElement::defaultEventHandler(event);
}

void DateTimeEditElement::setValueAsDate(const LayoutParameters& layoutParameters, const DateComponents& date)
{
    layout(layoutParameters, date);
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
        m_fields[fieldIndex]->setValueAsDate(date);
}

void DateTimeEditElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
{
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
        m_fields[fieldIndex]->setValueAsDateTimeFieldsState(dateTimeFieldsState);
}

void DateTimeEditElement::setEmptyValue(const LayoutParameters& layoutParameters, const DateComponents& dateForReadOnlyField)
{
    layout(layoutParameters, dateForReadOnlyField);
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
        m_fields[fieldIndex]->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
}

bool DateTimeEditElement::hasFocusedField()
{
    return focusedFieldIndex() != invalidFieldIndex;
}

void DateTimeEditElement::setOnlyYearMonthDay(const DateComponents& date)
{
    ASSERT(date.type() == DateComponents::Date);

    if (!m_editControlOwner)
        return;

    DateTimeFieldsState dateTimeFieldsState = valueAsDateTimeFieldsState();
    dateTimeFieldsState.setYear(date.fullYear());
    dateTimeFieldsState.setMonth(date.month() + 1);
    dateTimeFieldsState.setDayOfMonth(date.monthDay());
    setValueAsDateTimeFieldsState(dateTimeFieldsState);
    m_editControlOwner->editControlValueChanged();
}

void DateTimeEditElement::stepDown()
{
    if (DateTimeFieldElement* const field = focusedField())
        field->stepDown();
}

void DateTimeEditElement::stepUp()
{
    if (DateTimeFieldElement* const field = focusedField())
        field->stepUp();
}

void DateTimeEditElement::updateUIState()
{
    if (isDisabled()) {
        if (DateTimeFieldElement* field = focusedField())
            field->blur();
    }
}

String DateTimeEditElement::value() const
{
    if (!m_editControlOwner)
        return emptyString();
    return m_editControlOwner->formatDateTimeFieldsState(valueAsDateTimeFieldsState());
}

DateTimeFieldsState DateTimeEditElement::valueAsDateTimeFieldsState() const
{
    DateTimeFieldsState dateTimeFieldsState;
    for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
        m_fields[fieldIndex]->populateDateTimeFieldsState(dateTimeFieldsState);
    return dateTimeFieldsState;
}

} // namespace WebCore

#endif
