/*
 * SPDX-License-Identifier: GPL-3.0-only
 * MuseScore-Studio-CLA-applies
 *
 * MuseScore Studio
 * Music Composition & Notation
 *
 * Copyright (C) 2021 MuseScore Limited and others
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "noteinputpreferencesmodel.h"

using namespace mu::preferences;

NoteInputPreferencesModel::NoteInputPreferencesModel(QObject* parent)
    : QObject(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
{
}

void NoteInputPreferencesModel::load()
{
    notationConfiguration()->defaultNoteInputMethodChanged().onNotify(this, [this]() {
        emit defaultNoteInputMethodChanged(defaultNoteInputMethod());
    });

    notationConfiguration()->addAccidentalDotsArticulationsToNextNoteEnteredChanged().onNotify(this, [this]() {
        emit addAccidentalDotsArticulationsToNextNoteEnteredChanged(addAccidentalDotsArticulationsToNextNoteEntered());
    });

    notationConfiguration()->useNoteInputCursorInInputByDurationChanged().onNotify(this, [this]() {
        emit useNoteInputCursorInInputByDurationChanged(useNoteInputCursorInInputByDuration());
    });

    notationConfiguration()->isMidiInputEnabledChanged().onNotify(this, [this]() {
        emit midiInputEnabledChanged(midiInputEnabled());
    });

    notationConfiguration()->startNoteInputAtSelectedNoteRestWhenPressingMidiKeyChanged().onNotify(this, [this]() {
        emit startNoteInputAtSelectedNoteRestWhenPressingMidiKeyChanged(startNoteInputAtSelectedNoteRestWhenPressingMidiKey());
    });

    playbackConfiguration()->playNotesWhenEditingChanged().onNotify(this, [this]() {
        emit playNotesWhenEditingChanged(playNotesWhenEditing());
    });

    playbackConfiguration()->playChordWhenEditingChanged().onReceive(this, [this](bool value) {
        emit playChordWhenEditingChanged(value);
    });

    playbackConfiguration()->playHarmonyWhenEditingChanged().onReceive(this, [this](bool value) {
        emit playChordSymbolWhenEditingChanged(value);
    });

    playbackConfiguration()->playNotesOnMidiInputChanged().onReceive(this, [this](bool value) {
        emit playNotesOnMidiInputChanged(value);
    });

    notationConfiguration()->playPreviewNotesWithScoreDynamicsChanged().onNotify(this, [this]() {
        emit playPreviewNotesWithScoreDynamicsChanged(playPreviewNotesWithScoreDynamics());
    });

    notationConfiguration()->useMidiVelocityAndDurationDuringNoteInputChanged().onReceive(this, [this](bool value) {
        emit useMidiVelocityAndDurationDuringNoteInputChanged(value);
    });

    notationConfiguration()->isPlayPreviewNotesInInputByDurationChanged().onNotify(this, [this]() {
        emit playPreviewNotesInInputByDurationChanged(playPreviewNotesInInputByDuration());
    });

    midiRemoteConfiguration()->advanceToNextNoteOnKeyReleaseChanged().onReceive(this, [this](bool value) {
        emit advanceToNextNoteOnKeyReleaseChanged(value);
    });

    notationConfiguration()->delayBetweenNotesInRealTimeModeMillisecondsChanged().onReceive(this, [this](int value) {
        emit delayBetweenNotesInRealTimeModeMillisecondsChanged(value);
    });

    notationConfiguration()->notePlayDurationMillisecondsChanged().onReceive(this, [this](int value) {
        emit notePlayDurationMillisecondsChanged(value);
    });

    engravingConfiguration()->dynamicsApplyToAllVoicesChanged().onReceive(this, [this](bool value) {
        emit dynamicsApplyToAllVoicesChanged(value);
    });

    notationConfiguration()->colorNotesOutsideOfUsablePitchRangeChanged().onReceive(this, [this](bool value) {
        emit colorNotesOutsideOfUsablePitchRangeChanged(value);
    });

    notationConfiguration()->warnGuitarBendsChanged().onReceive(this, [this](bool value) {
        emit warnGuitarBendsChanged(value);
    });

    engravingConfiguration()->autoUpdateFretboardDiagramsChanged().onReceive(this, [this](bool value) {
        emit autoUpdateFretboardDiagramsChanged(value);
    });
}

QVariantList NoteInputPreferencesModel::noteInputMethods() const
{
    using Method = mu::notation::NoteInputMethod;

    std::vector<std::pair<muse::actions::ActionCode, Method > > noteInputActions {
        { "note-input-by-note-name", Method::BY_NOTE_NAME },
        { "note-input-by-duration", Method::BY_DURATION },
    };

    QVariantList methods;

    for (const auto& pair : noteInputActions) {
        const muse::ui::UiAction& action = uiActionsRegister()->action(pair.first);

        QVariantMap method;
        method["value"] = static_cast<int>(pair.second);
        method["text"] = action.title.qTranslatedWithoutMnemonic();

        methods.emplace_back(std::move(method));
    }

    return methods;
}

int NoteInputPreferencesModel::defaultNoteInputMethod() const
{
    return static_cast<int>(notationConfiguration()->defaultNoteInputMethod());
}

bool NoteInputPreferencesModel::addAccidentalDotsArticulationsToNextNoteEntered() const
{
    return notationConfiguration()->addAccidentalDotsArticulationsToNextNoteEntered();
}

bool NoteInputPreferencesModel::useNoteInputCursorInInputByDuration() const
{
    return notationConfiguration()->useNoteInputCursorInInputByDuration();
}

bool NoteInputPreferencesModel::midiInputEnabled() const
{
    return notationConfiguration()->isMidiInputEnabled();
}

bool NoteInputPreferencesModel::startNoteInputAtSelectedNoteRestWhenPressingMidiKey() const
{
    return notationConfiguration()->startNoteInputAtSelectedNoteRestWhenPressingMidiKey();
}

bool NoteInputPreferencesModel::advanceToNextNoteOnKeyRelease() const
{
    return midiRemoteConfiguration()->advanceToNextNoteOnKeyRelease();
}

int NoteInputPreferencesModel::delayBetweenNotesInRealTimeModeMilliseconds() const
{
    return notationConfiguration()->delayBetweenNotesInRealTimeModeMilliseconds();
}

bool NoteInputPreferencesModel::playNotesWhenEditing() const
{
    return playbackConfiguration()->playNotesWhenEditing();
}

bool NoteInputPreferencesModel::playPreviewNotesInInputByDuration() const
{
    return notationConfiguration()->isPlayPreviewNotesInInputByDuration();
}

int NoteInputPreferencesModel::notePlayDurationMilliseconds() const
{
    return notationConfiguration()->notePlayDurationMilliseconds();
}

bool NoteInputPreferencesModel::playChordWhenEditing() const
{
    return playbackConfiguration()->playChordWhenEditing();
}

bool NoteInputPreferencesModel::playChordSymbolWhenEditing() const
{
    return playbackConfiguration()->playHarmonyWhenEditing();
}

bool NoteInputPreferencesModel::playPreviewNotesWithScoreDynamics() const
{
    return notationConfiguration()->playPreviewNotesWithScoreDynamics();
}

bool NoteInputPreferencesModel::playNotesOnMidiInput() const
{
    return playbackConfiguration()->playNotesOnMidiInput();
}

bool NoteInputPreferencesModel::useMidiVelocityAndDurationDuringNoteInput() const
{
    return notationConfiguration()->useMidiVelocityAndDurationDuringNoteInput();
}

bool NoteInputPreferencesModel::dynamicsApplyToAllVoices() const
{
    return engravingConfiguration()->dynamicsApplyToAllVoices();
}

bool NoteInputPreferencesModel::colorNotesOutsideOfUsablePitchRange() const
{
    return notationConfiguration()->colorNotesOutsideOfUsablePitchRange();
}

bool NoteInputPreferencesModel::warnGuitarBends() const
{
    return notationConfiguration()->warnGuitarBends();
}

void NoteInputPreferencesModel::setDefaultNoteInputMethod(int value)
{
    if (value == defaultNoteInputMethod()) {
        return;
    }

    notationConfiguration()->setDefaultNoteInputMethod(static_cast<mu::notation::NoteInputMethod>(value));
}

void NoteInputPreferencesModel::setAddAccidentalDotsArticulationsToNextNoteEntered(bool value)
{
    if (value == addAccidentalDotsArticulationsToNextNoteEntered()) {
        return;
    }

    notationConfiguration()->setAddAccidentalDotsArticulationsToNextNoteEntered(value);
}

void NoteInputPreferencesModel::setUseNoteInputCursorInInputByDuration(bool value)
{
    if (value == useNoteInputCursorInInputByDuration()) {
        return;
    }

    notationConfiguration()->setUseNoteInputCursorInInputByDuration(value);
}

void NoteInputPreferencesModel::setMidiInputEnabled(bool value)
{
    if (value == midiInputEnabled()) {
        return;
    }

    notationConfiguration()->setIsMidiInputEnabled(value);
}

void NoteInputPreferencesModel::setStartNoteInputAtSelectedNoteRestWhenPressingMidiKey(bool value)
{
    if (value == startNoteInputAtSelectedNoteRestWhenPressingMidiKey()) {
        return;
    }

    notationConfiguration()->setStartNoteInputAtSelectedNoteRestWhenPressingMidiKey(value);
}

void NoteInputPreferencesModel::setAdvanceToNextNoteOnKeyRelease(bool value)
{
    if (value == advanceToNextNoteOnKeyRelease()) {
        return;
    }

    midiRemoteConfiguration()->setAdvanceToNextNoteOnKeyRelease(value);
}

void NoteInputPreferencesModel::setDelayBetweenNotesInRealTimeModeMilliseconds(int delay)
{
    if (delay == delayBetweenNotesInRealTimeModeMilliseconds()) {
        return;
    }

    notationConfiguration()->setDelayBetweenNotesInRealTimeModeMilliseconds(delay);
}

void NoteInputPreferencesModel::setPlayNotesWhenEditing(bool value)
{
    if (value == playNotesWhenEditing()) {
        return;
    }

    playbackConfiguration()->setPlayNotesWhenEditing(value);
}

void NoteInputPreferencesModel::setPlayPreviewNotesInInputByDuration(bool value)
{
    if (value == playPreviewNotesInInputByDuration()) {
        return;
    }

    notationConfiguration()->setIsPlayPreviewNotesInInputByDuration(value);
}

void NoteInputPreferencesModel::setNotePlayDurationMilliseconds(int duration)
{
    if (duration == notePlayDurationMilliseconds()) {
        return;
    }

    notationConfiguration()->setNotePlayDurationMilliseconds(duration);
}

void NoteInputPreferencesModel::setPlayChordWhenEditing(bool value)
{
    if (value == playChordWhenEditing()) {
        return;
    }

    playbackConfiguration()->setPlayChordWhenEditing(value);
}

void NoteInputPreferencesModel::setPlayChordSymbolWhenEditing(bool value)
{
    if (value == playChordSymbolWhenEditing()) {
        return;
    }

    playbackConfiguration()->setPlayHarmonyWhenEditing(value);
}

void NoteInputPreferencesModel::setPlayPreviewNotesWithScoreDynamics(bool play)
{
    if (play == playPreviewNotesWithScoreDynamics()) {
        return;
    }

    notationConfiguration()->setPlayPreviewNotesWithScoreDynamics(play);
}

void NoteInputPreferencesModel::setPlayNotesOnMidiInput(bool value)
{
    if (value == playNotesOnMidiInput()) {
        return;
    }

    playbackConfiguration()->setPlayNotesOnMidiInput(value);
}

void NoteInputPreferencesModel::setUseMidiVelocityAndDurationDuringNoteInput(bool value)
{
    if (value == useMidiVelocityAndDurationDuringNoteInput()) {
        return;
    }

    notationConfiguration()->setUseMidiVelocityAndDurationDuringNoteInput(value);
}

void NoteInputPreferencesModel::setDynamicsApplyToAllVoices(bool value)
{
    if (value == dynamicsApplyToAllVoices()) {
        return;
    }

    engravingConfiguration()->setDynamicsApplyToAllVoices(value);
}

void NoteInputPreferencesModel::setColorNotesOutsideOfUsablePitchRange(bool value)
{
    if (value == colorNotesOutsideOfUsablePitchRange()) {
        return;
    }

    notationConfiguration()->setColorNotesOutsideOfUsablePitchRange(value);
}

void NoteInputPreferencesModel::setWarnGuitarBends(bool value)
{
    if (value == warnGuitarBends()) {
        return;
    }

    notationConfiguration()->setWarnGuitarBends(value);
}

bool NoteInputPreferencesModel::autoUpdateFretboardDiagrams() const
{
    return engravingConfiguration()->autoUpdateFretboardDiagrams();
}

void NoteInputPreferencesModel::setAutoUpdateFretboardDiagrams(bool value)
{
    if (value == autoUpdateFretboardDiagrams()) {
        return;
    }

    engravingConfiguration()->setAutoUpdateFretboardDiagrams(value);
}
