/* Copyright 2013-2019 Matt Tytel
 *
 * vital 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.
 *
 * vital 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 vital.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "wavetable_group.h"
#include "synth_constants.h"
#include "wave_frame.h"
#include "wave_source.h"
#include "wavetable.h"

int WavetableGroup::getComponentIndex(WavetableComponent* component) {
  for (int i = 0; i < components_.size(); ++i) {
    if (components_[i].get() == component)
      return i;
  }
  return -1;
}

void WavetableGroup::moveUp(int index) {
  if (index <= 0)
    return;

  components_[index].swap(components_[index - 1]);
}

void WavetableGroup::moveDown(int index) {
  if (index < 0 || index >= components_.size() - 1)
    return;

  components_[index].swap(components_[index + 1]);
}

void WavetableGroup::removeComponent(int index) {
  if (index < 0 || index >= components_.size())
    return;

  std::unique_ptr<WavetableComponent> component = std::move(components_[index]);
  components_.erase(components_.begin() + index);
}

void WavetableGroup::reset() {
  components_.clear();
  loadDefaultGroup();
}

void WavetableGroup::prerender() {
  for (auto& component : components_)
    component->prerender();
}

bool WavetableGroup::isShepardTone() {
  for (auto& component : components_) {
    if (component->getType() != WavetableComponentFactory::kShepardToneSource)
      return false;
  }

  return true;
}

void WavetableGroup::render(vital::WaveFrame* wave_frame, float position) const {
  wave_frame->index = position;

  for (auto& component : components_)
    component->render(wave_frame, position);
}

void WavetableGroup::renderTo(vital::Wavetable* wavetable) {
  for (int i = 0; i < vital::kNumOscillatorWaveFrames; ++i) {
    compute_frame_.index = i;

    for (auto& component : components_)
      component->render(&compute_frame_, i);

    wavetable->loadWaveFrame(&compute_frame_);
  }
}

void WavetableGroup::loadDefaultGroup() {
  WaveSource* wave_source = new WaveSource();
  wave_source->insertNewKeyframe(0);
  vital::WaveFrame* wave_frame = wave_source->getWaveFrame(0);
  for (int i = 0; i < vital::WaveFrame::kWaveformSize; ++i) {
    float t = i / (vital::WaveFrame::kWaveformSize - 1.0f);
    int half_shift = (i + vital::WaveFrame::kWaveformSize / 2) % vital::WaveFrame::kWaveformSize;
    wave_frame->time_domain[half_shift] = 1.0f - 2.0f * t;
  }
  wave_frame->toFrequencyDomain();

  addComponent(wave_source);
}

int WavetableGroup::getLastKeyframePosition() {
  int last_position = 0;
  for (auto& component : components_)
    last_position = std::max(last_position, component->getLastKeyframePosition());

  return last_position;
}

json WavetableGroup::stateToJson() {
  json json_components;
  for (auto& component : components_) {
    json json_component = component->stateToJson();
    json_components.push_back(json_component);
  }

  return { { "components", json_components } };
}

void WavetableGroup::jsonToState(json data) {
  components_.clear();

  json json_components = data["components"];
  for (json json_component : json_components) {
    std::string type = json_component["type"];
    WavetableComponent* component = WavetableComponentFactory::createComponent(type);
    component->jsonToState(json_component);
    addComponent(component);
  }
}
