/* -*- Mode: c++ -*- */
/***************************************************************************
 *            maintab.cc
 *
 *  Fri Mar 24 20:39:59 CET 2017
 *  Copyright 2017 André Nusser
 *  andre.nusser@googlemail.com
 ****************************************************************************/

/*
 *  This file is part of DrumGizmo.
 *
 *  DrumGizmo is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  DrumGizmo 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with DrumGizmo; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "maintab.h"

#include <translation.h>

namespace GUI
{

MainTab::MainTab(dggui::Widget* parent,
                 Settings& settings,
                 SettingsNotifier& settings_notifier,
                 Config& config)
	: Widget(parent)
	, drumkitframe_content{this, settings, settings_notifier, config}
	, statusframe_content{this, settings_notifier}
	, humanizerframe_content{this, settings, settings_notifier}
	, diskstreamingframe_content{this, settings, settings_notifier}
	, bleedcontrolframe_content{this, settings, settings_notifier}
	, resamplingframe_content{this, settings, settings_notifier}
	, timingframe_content{this, settings, settings_notifier}
	, sampleselectionframe_content{this, settings, settings_notifier}
	, visualizerframe_content{this, settings, settings_notifier}
	, powerframe_content{this, settings, settings_notifier}
	, voicelimit_content{this, settings, settings_notifier}
	, settings(settings)
	, settings_notifier(settings_notifier)
{
	layout.setSpacing(0);

	const std::string humanizer_tip = std::string(
		_("The first two knobs influence how DrumGizmo simulates the\n")) +
		_("stamina of a physical drummers, ie. the fact that they\n") +
		_("loose power in their strokes when playing fast notes:\n") +
		_("  * pAttack: How quickly the velocity gets reduced when\n") +
		_("     playing fast notes.\n") +
		_("     Lower values result in faster velocity reduction.\n") +
		_("  * pRelease: How quickly the drummer regains the velocity\n") +
		_("     when there are spaces between the notes.\n") +
		_("     Lower values result in faster regain.\n") +
		_("\n") +
		_("The last knob controls the randomization of the sample selection:\n") +
		_("  * pStdDev: The standard-deviation for the sample selection.\n") +
		_("     Higher value makes it more likely that a sample further\n") +
		_("     away from the input velocity will be played.");

	const std::string timing_tip = std::string(
		_("These three knobs influence how DrumGizmo simulates the tightness\n")) +
		_("of the drummer.\n") +
		_("The drifting is defined as the difference between\n") +
		_("the perfect metronome (defined by the note positions) and the 'internal'\n") +
		_("metronome of the drummer which can then drift back and forth in a\n") +
		_("controlled fashion:\n") +
		_("  * pTightness: For each note how much is the drummer allowed to drift.\n") +
		_("     Higher value make the drummer more tight, ie. drift less.\n") +
		_("  * pTimingRegain: Once the drifted, how fast does the drummer's 'internal'\n") +
		_("     metronome get back in sync with the perfect metronome.\n") +
		_("     Higher values moves the timing back towards perfect faster.\n") +
		_("  * pLaidback: Add or subtract a fixed delay in ms to all notes. This will\n") +
		_("     alter the feel of a beat.\n") +
		_("     Positive values are up-beat, negative values are back on the beat.\n") +
		_("\n") +
		_("NOTE: Enabling timing humanization will introduce a fixed delay into the\n") +
		_("audio stream.\n") +
		_("So this feature should be disabled when using DrumGizmo in\n") +
		_("a real-time scenario such as live with a MIDI drumkit.");

	const std::string sampleselection_tip = std::string(
		_("These three knobs influence how DrumGizmo selects\n")) +
		_("its samples in the following way:\n") +
		_("  * pClose: The importance given to choosing a sample close\n") +
		_("     to the actual MIDI value (after humanization)\n") +
		_("  * pDiversity: The importance given to choosing samples\n") +
		_("     which haven't been played recently.\n") +
		_("  * pRandom: The amount of randomness added.");

	const std::string visualizer_tip = std::string(
		_("This graph visualizes the time and velocity offsets of last note\n")) +
		_("played according to it's ideal input time and velocity.\n") +
		_("The green lines indicate the ideal time and velocity positions.\n") +
		_("The pink areas indicate the spread of the position and velocity\n") +
		_("of the next note in line.\n") +
		_("The wider the area the more the note can move in time and velocity.");

	const std::string power_tip = std::string(
		_("This function controls how the input powers are mapped to the powers\n")) +
		_("that DrumGizmo uses for selecting samples.\n") +
		_("You can control the function by dragging and dropping the\n") +
		_("three colorful control points.\n") +
		_("Additionally, you can either use a shelf, which draws a horizontal line\n") +
		_("from the green and red control points to the left/right side.\n") +
		_("Or you can turn off the shelf, and then the function goes through\n") +
		_("the lower left corner, then the three control points, and then\n") +
		_("the upper right corner, enabling to draw more complicated functions.");

	const std::string voice_limit_tip = std::string(
		_("This feature controls how many voices can simultaneously be in play for a given\n")) +
		_("instrument. When this feature is active, Drumgizmo will silence any excess \n") +
		_("voices to ease the burden of processing.\n") +
		_("\n") +
		_("This feature works on a per-instrument basis, e.g., voices played on the bass\n") +
		_("drum can only be silenced by other bass drum hits, and not by the snare.\n") +
		_("\n") +
		_("  * Max voices: The maximum number of voices that should be allowed to play.\n") +
		_("  * Rampdown time: How many seconds it takes to silence a voice.");

	layout.setResizeChildren(true);

	//Left column...
	add(_("Drumkit"), drumkit_frame, drumkitframe_content, 15, 0);
	add(_("Status"), status_frame, statusframe_content, 12, 0);
	add(_("Resampling"), resampling_frame, resamplingframe_content, 10, 0);
	add(_("Voice Limit"), voicelimit_frame, voicelimit_content, 10, 0);
	voicelimit_frame.setHelpText(voice_limit_tip);
	add(_("Disk Streaming"), diskstreaming_frame, diskstreamingframe_content, 9, 0);
	add(_("Bleed Control"), bleedcontrol_frame, bleedcontrolframe_content, 9, 0);

	//Right column
	add(_("Velocity Humanizer"), humanizer_frame, humanizerframe_content,10, 1);
	humanizer_frame.setHelpText(humanizer_tip);

	add(_("Timing Humanizer"), timing_frame, timingframe_content, 10, 1);
	timing_frame.setHelpText(timing_tip);

	add(_("Sample Selection"), sampleselection_frame,
	    sampleselectionframe_content, 10, 1);
	sampleselection_frame.setHelpText(sampleselection_tip);

	add(_("Visualizer"), visualizer_frame, visualizerframe_content, 14, 1);
	visualizer_frame.setHelpText(visualizer_tip);

	add(_("Velocity Curve"), power_frame, powerframe_content, 21, 1);
	power_frame.setHelpText(power_tip);

	humanizer_frame.setOnSwitch(settings.enable_velocity_modifier);
	bleedcontrol_frame.setOnSwitch(settings.enable_bleed_control);
	resampling_frame.setOnSwitch(settings.enable_resampling);
	timing_frame.setOnSwitch(settings.enable_latency_modifier);
	voicelimit_frame.setOnSwitch(settings.enable_voice_limit);

	// FIXME:
	bleedcontrol_frame.setEnabled(false);

	CONNECT(this, settings_notifier.enable_velocity_modifier,
	        &humanizer_frame, &dggui::FrameWidget::setOnSwitch);
	CONNECT(this, settings_notifier.enable_resampling,
	        &resampling_frame, &dggui::FrameWidget::setOnSwitch);
	CONNECT(this, settings_notifier.has_bleed_control,
	        &bleedcontrol_frame, &dggui::FrameWidget::setEnabled);
	CONNECT(&humanizer_frame, onSwitchChangeNotifier,
	        this, &MainTab::humanizerOnChange);
	CONNECT(&bleedcontrol_frame, onSwitchChangeNotifier,
	        this, &MainTab::bleedcontrolOnChange);
	CONNECT(&resampling_frame, onSwitchChangeNotifier,
	        this, &MainTab::resamplingOnChange);
	CONNECT(&timing_frame, onSwitchChangeNotifier,
	        this, &MainTab::timingOnChange);
	CONNECT(&bleedcontrol_frame, onEnabledChanged,
	        &bleedcontrolframe_content, &BleedcontrolframeContent::setEnabled);
	CONNECT(&settings_notifier, enable_powermap,
	        &power_frame, &dggui::FrameWidget::setOnSwitch);
	CONNECT(&power_frame, onSwitchChangeNotifier,
	        this, &MainTab::powerOnChange);
	CONNECT(&voicelimit_frame, onSwitchChangeNotifier,
	        this, &MainTab::voicelimitOnChange);

}

void MainTab::resize(std::size_t width, std::size_t height)
{
	dggui::Widget::resize(width, height);

	// DrumGizmo logo
	dggui::Painter painter(*this);
	painter.clear();
	painter.drawImage(width - logo.width(), height - logo.height(), logo);
}

void MainTab::humanizerOnChange(bool on)
{
	settings.enable_velocity_modifier.store(on);
}

void MainTab::bleedcontrolOnChange(bool on)
{
	settings.enable_bleed_control.store(on);
}

void MainTab::resamplingOnChange(bool on)
{
	settings.enable_resampling.store(on);
}

void MainTab::timingOnChange(bool on)
{
	settings.enable_latency_modifier.store(on);
}

void MainTab::powerOnChange(bool on)
{
	settings.enable_powermap.store(on);
}

void MainTab::voicelimitOnChange(bool status)
{
	settings.enable_voice_limit.store(status);
}

void MainTab::add(std::string const& title, dggui::FrameWidget& frame,
                  dggui::Widget& content, std::size_t height, int column)
{
	layout.addItem(&frame);
	frame.setTitle(title);
	frame.setContent(&content);

	auto grid_start = layout.lastUsedRow(column) + 1;
	auto range = dggui::GridLayout::GridRange{column,
	                                          column + 1,
	                                          grid_start,
	                                          grid_start + int(height)};
	layout.setPosition(&frame, range);
}

} // GUI::
