// Copyright 2015 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// 
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Generator for the T outputs.

#include "marbles/random/t_generator.h"

#include <algorithm>

#include "stmlib/dsp/units.h"

#include "marbles/resources.h"

namespace marbles {

using namespace std;
using namespace stmlib;

/* static */
DividerPattern TGenerator::divider_patterns[kNumDividerPatterns] = {
  { { { 1, 1 }, { 1, 1 } }, 1 },
  { { { 1, 1 }, { 2, 1 } }, 1 },
  { { { 1, 2 }, { 1, 1 } }, 2 },
  { { { 1, 1 }, { 4, 1 } }, 1 },
  { { { 1, 2 }, { 2, 1 } }, 2 },
  { { { 1, 1 }, { 3, 2 } }, 2 },
  { { { 1, 4 }, { 4, 1 } }, 4 },
  { { { 1, 4 }, { 2, 1 } }, 4 },
  { { { 1, 2 }, { 3, 2 } }, 2 },
  { { { 1, 1 }, { 8, 1 } }, 1 },
  { { { 1, 1 }, { 3, 1 } }, 1 },
  { { { 1, 3 }, { 1, 1 } }, 3 },
  { { { 1, 1 }, { 5, 4 } }, 4 },
  { { { 1, 2 }, { 5, 4 } }, 4 },
  { { { 1, 1 }, { 6, 1 } }, 1 },
  { { { 1, 3 }, { 2, 1 } }, 3 },
  { { { 1, 1 }, { 16, 1 } }, 1 },
};

/* static */
DividerPattern TGenerator::fixed_divider_patterns[kNumDividerPatterns] = {
  { { { 8, 1 }, { 1, 8 } }, 8 },
  { { { 6, 1 }, { 1, 6 } }, 6 },
  { { { 4, 1 }, { 1, 4 } }, 4 },
  { { { 3, 1 }, { 1, 3 } }, 3 },
  { { { 2, 1 }, { 1, 2 } }, 2 },
  { { { 3, 2 }, { 2, 3 } }, 6 },
  { { { 4, 3 }, { 3, 4 } }, 12 },
  { { { 5, 4 }, { 4, 5 } }, 20 },

  { { { 1, 1 }, { 1, 1 } }, 1 },

  { { { 4, 5 }, { 5, 4 } }, 20 },
  { { { 3, 4 }, { 4, 3 } }, 12 },
  { { { 2, 2 }, { 3, 2 } }, 6 },
  { { { 1, 2 }, { 2, 1 } }, 2 },
  { { { 1, 3 }, { 3, 1 } }, 3 },
  { { { 1, 4 }, { 4, 1 } }, 4 },
  { { { 1, 6 }, { 6, 1 } }, 6 },
  { { { 1, 8 }, { 8, 1 } }, 8 },
};

/* static */
Ratio TGenerator::input_divider_ratios[kNumInputDividerRatios] = {
  { 1, 4 },
  { 1, 3 },
  { 1, 2 },
  { 2, 3 },
  { 1, 1 },
  { 3, 2 },
  { 2, 1 },
  { 3, 1 },
  { 4, 1 },
};

/* static */
uint8_t TGenerator::drum_patterns[kNumDrumPatterns][kDrumPatternSize] = {
  { 1, 0, 0, 0, 2, 0, 0, 0 },
  { 0, 0, 1, 0, 2, 0, 0, 0 },

  { 1, 0, 1, 0, 2, 0, 0, 0 },
  { 0, 0, 1, 0, 2, 0, 0, 2 },

  { 1, 0, 1, 0, 2, 0, 1, 0 },
  { 0, 2, 1, 0, 2, 0, 0, 2 },

  { 1, 0, 0, 0, 2, 0, 1, 0 },
  { 0, 2, 1, 0, 2, 0, 1, 2 },

  { 1, 0, 0, 1, 2, 0, 0, 0 },
  { 0, 2, 1, 1, 2, 0, 1, 2 },

  { 1, 0, 0, 1, 2, 0, 1, 0 },
  { 0, 2, 1, 1, 2, 2, 1, 2 },

  { 1, 0, 0, 1, 2, 0, 1, 2 },
  { 0, 2, 0, 1, 2, 0, 1, 2 },

  { 1, 0, 1, 1, 2, 0, 1, 2 },
  { 2, 0, 1, 2, 0, 1, 2, 0 },

  { 1, 2, 1, 1, 2, 0, 1, 2 },
  { 2, 0, 1, 2, 0, 1, 2, 2 }
};

void TGenerator::Init(RandomStream* random_stream, float sr) {
  one_hertz_ = 1.0f / static_cast<float>(sr);
  model_ = T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI;
  range_ = T_GENERATOR_RANGE_1X;

  rate_ = 0.0f;
  bias_ = 0.5f;
  jitter_ = 0.0f;
  pulse_width_mean_ = 0.0f;
  pulse_width_std_ = 0.0f;

  master_phase_ = 0.0f;
  jitter_multiplier_ = 1.0f;
  phase_difference_ = 0.0f;
  previous_external_ramp_value_ = 0.0f;

  divider_pattern_length_ = 0;
  fill(&streak_counter_[0], &streak_counter_[kMarkovHistorySize], 0);
  fill(&markov_history_[0], &markov_history_[kMarkovHistorySize], 0);
  markov_history_ptr_ = 0;
  drum_pattern_step_ = 0;
  drum_pattern_index_ = 0;

  sequence_.Init(random_stream);
  ramp_divider_.Init();
  ramp_extractor_.Init(1000.0f / sr);
  ramp_generator_.Init();
  for (size_t i = 0; i < kNumTChannels; ++i) {
    slave_ramp_[i].Init();
  }
  bias_quantizer_.Init();
  rate_quantizer_.Init();
  
  use_external_clock_ = false;
}

int TGenerator::GenerateComplementaryBernoulli(const RandomVector& x) {
  int bitmask = 0;
  for (size_t i = 0; i < kNumTChannels; ++i) {
    if ((x.variables.u[i >> 1] > bias_) ^ (i & 1)) {
      bitmask |= 1 << i;
    }
  }
  return bitmask;
}

int TGenerator::GenerateIndependentBernoulli(const RandomVector& x) {
  int bitmask = 0;
  for (size_t i = 0; i < kNumTChannels; ++i) {
    if ((x.variables.u[i] > bias_) ^ (i & 1)) {
      bitmask |= 1 << i;
    }
  }
  return bitmask;
}

int TGenerator::GenerateThreeStates(const RandomVector& x) {
  int bitmask = 0;
  float p_none = 0.75f - fabs(bias_ - 0.5f);
  float threshold = p_none + (1.0f - p_none) * (0.25f + (bias_ * 0.5f));
  
  for (size_t i = 0; i < kNumTChannels; ++i) {
    float u = x.variables.u[i >> 1];
    if (u > p_none && ((u > threshold) ^ (i & 1))) {
      bitmask |= 1 << i;
    }
  }
  return bitmask;
}

int TGenerator::GenerateDrums(const RandomVector& x) {
  ++drum_pattern_step_;
  if (drum_pattern_step_ >= kDrumPatternSize) {
    drum_pattern_step_ = 0;
    float u = x.variables.u[0] * 2.0f * fabs(bias_ - 0.5f);
    drum_pattern_index_ = static_cast<int32_t>(kNumDrumPatterns * u);
    if (bias_ <= 0.5f) {
      drum_pattern_index_ -= drum_pattern_index_ % 2;
    }
  }
  return drum_patterns[drum_pattern_index_][drum_pattern_step_];
}

int TGenerator::GenerateMarkov(const RandomVector& x) {
  int bitmask = 0;
  float b = 1.5f * bias_ - 0.5f;
  markov_history_[markov_history_ptr_] = 0;
  const int32_t p = markov_history_ptr_;
  for (size_t i = 0; i < kNumTChannels; ++i) {
    int32_t mask = 1 << i;
    // 4 rules:
    // * We favor repeating what we played 8 ticks ago.
    // * We do not favor pulses appearing on both channels.
    // * We favor sparse patterns (no consecutive hits).
    // * We favor patterns in which one channel "echoes" what the other
    //   channel played 4 ticks before.
    bool periodic = markov_history_[(p + 8) % kMarkovHistorySize] & mask;
    bool simultaneous = markov_history_[(p + 8) % kMarkovHistorySize] & ~mask;
    bool dense = markov_history_[(p + 1) % kMarkovHistorySize] & mask;
    bool alternate = markov_history_[(p + 4) % kMarkovHistorySize] & ~mask;

    float logit = -1.5f;
    logit += streak_counter_[i] > 24 ? 10.0f : 0.0f;
    logit += 8.0f * fabs(b) * (periodic ? b : -b);
    logit -= 2.0f * (simultaneous ? b : -b);
    logit -= 1.0f * (dense ? b : 0.0f);
    logit += 1.0f * (alternate ? b : 0.0f);
    CONSTRAIN(logit, -10.0f, 10.0f);
    float probability = lut_logit[static_cast<int>(logit * 12.8f + 128.0f)];
    bool state = x.variables.u[i] < probability;
    
    if (sequence_.deja_vu() >= x.variables.p) {
      state = markov_history_[(p + sequence_.length()) % kMarkovHistorySize] & mask;
    }
    if (state) {
      bitmask |= mask;
      streak_counter_[i] = 0;
    } else {
      ++streak_counter_[i];
    }
  }
  markov_history_[p] |= bitmask;
  markov_history_ptr_ = (p + kMarkovHistorySize - 1) % kMarkovHistorySize;
  return bitmask;
}

void TGenerator::ScheduleOutputPulses(const RandomVector& x, int bitmask) {
  for (size_t i = 0; i < kNumTChannels; ++i) {
    slave_ramp_[i].Init(
        bitmask & 1,
        RandomPulseWidth(i, x.variables.pulse_width[i]),
        0.5f);
    bitmask >>= 1;
  }
}

void TGenerator::ConfigureSlaveRamps(const RandomVector& x) {
  switch (model_) {
    // Generate a bitmask that will describe which outputs are active
    // at this clock tick. Use this bitmask to actually schedule pulses on the
    // outputs.
    case T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI:
      ScheduleOutputPulses(x, GenerateComplementaryBernoulli(x));
      break;
    
    case T_GENERATOR_MODEL_INDEPENDENT_BERNOULLI:
      ScheduleOutputPulses(x, GenerateIndependentBernoulli(x));
      break;

    case T_GENERATOR_MODEL_THREE_STATES:
      ScheduleOutputPulses(x, GenerateThreeStates(x));
      break;
    
    case T_GENERATOR_MODEL_DRUMS:
      ScheduleOutputPulses(x, GenerateDrums(x));
      break;
    
    case T_GENERATOR_MODEL_MARKOV:
      ScheduleOutputPulses(x, GenerateMarkov(x));
      break;
    
    case T_GENERATOR_MODEL_CLUSTERS:
    case T_GENERATOR_MODEL_DIVIDER:
      --divider_pattern_length_;
      if (divider_pattern_length_ <= 0) {
        DividerPattern pattern;
        if (model_ == T_GENERATOR_MODEL_DIVIDER) {
          pattern = bias_quantizer_.Lookup(
              fixed_divider_patterns,
              bias_,
              kNumDividerPatterns);
        } else {
          float strength = fabs(bias_ - 0.5f) * 2.0f;
          float u = x.variables.u[0];
          u *= (u + strength * strength * (1.0f - u));
          u *= strength;
          pattern = divider_patterns[static_cast<size_t>(
              u * kNumDividerPatterns)];
          if (bias_ < 0.5f) {
            for (size_t i = 0; i < kNumTChannels / 2; ++i) {
              swap(pattern.ratios[i], pattern.ratios[kNumTChannels - 1 - i]);
            }
          }
        }
        for (size_t i = 0; i < kNumTChannels; ++i) {
          slave_ramp_[i].Init(
              pattern.length,
              pattern.ratios[i],
              RandomPulseWidth(i, x.variables.pulse_width[i]));
        }
        divider_pattern_length_ = pattern.length;
      }
      break;
  }
}

void TGenerator::Process(
    bool use_external_clock,
    const GateFlags* external_clock,
    Ramps ramps,
    bool* gate,
    size_t size) {
  float internal_frequency;
  if (use_external_clock) {
    if (!use_external_clock_) {
      ramp_extractor_.Reset();
    }
    
    Ratio ratio = rate_quantizer_.Lookup(
        input_divider_ratios, 
        1.05f * rate_ / 96.0f + 0.5f,
        kNumInputDividerRatios);
    if (range_ == T_GENERATOR_RANGE_0_25X) {
      ratio.q *= 4;
    } else if (range_ == T_GENERATOR_RANGE_4X) {
      ratio.p *= 4;
    }
    ratio.Simplify<2>();
    bool reset_observed = ramp_extractor_.Process(
        ratio, true, external_clock, ramps.external, size);
    if (reset_observed) {
      if (model_ == T_GENERATOR_MODEL_DRUMS) {
        drum_pattern_step_ = kDrumPatternSize;
        RandomVector random_vector;
        sequence_.NextVector(
            random_vector.x,
            sizeof(random_vector.x) / sizeof(float));
        ConfigureSlaveRamps(random_vector);
      } else if (model_ == T_GENERATOR_MODEL_CLUSTERS ||
                model_ == T_GENERATOR_MODEL_DIVIDER) {
        divider_pattern_length_ = 0;
      }
    }
    internal_frequency = 0.0f;
  } else {
    float rate = 2.0f;
    if (range_ == T_GENERATOR_RANGE_4X) {
      rate = 8.0f;
    } else if (range_ == T_GENERATOR_RANGE_0_25X) {
      rate = 0.5f;
    }
    internal_frequency = rate * one_hertz_ * SemitonesToRatio(rate_);
  }
  
  use_external_clock_ = use_external_clock;
  
  while (size--) {
    float frequency = use_external_clock
        ? *ramps.external - previous_external_ramp_value_
        : internal_frequency;
    frequency += frequency < 0.0f ? 1.0f : 0.0f;

    float jittery_frequency = frequency * jitter_multiplier_;
    master_phase_ += jittery_frequency;
    phase_difference_ += frequency - jittery_frequency;
    
    if (master_phase_ > 1.0f) {
      master_phase_ -= 1.0f;
      
      RandomVector random_vector;
      sequence_.NextVector(
          random_vector.x,
          sizeof(random_vector.x) / sizeof(float));
      
      float jitter_amount = jitter_ * jitter_ * jitter_ * jitter_ * 36.0f;
      float x = FastBetaDistributionSample(random_vector.variables.jitter);
      float multiplier = SemitonesToRatio((x * 2.0f - 1.0f) * jitter_amount);
      
      // This step is crucial in making sure that the jittered clock does not
      // deviate too much from the master clock. The larger the phase difference
      // difference between the two, the more likely the jittery clock will
      // speed up or down to catch up with the straight clock.
      multiplier *= phase_difference_ > 0.0f
            ? 1.0f + phase_difference_
            : 1.0f / (1.0f - phase_difference_);
      
      jitter_multiplier_ = multiplier;
      ConfigureSlaveRamps(random_vector);
    }
    
    if (internal_frequency) {
      *ramps.external = master_phase_;
    }
    
    previous_external_ramp_value_ = *ramps.external;
    ramps.external++;
    *ramps.master++ = master_phase_;
    for (size_t j = 0; j < kNumTChannels; ++j) {
      slave_ramp_[j].Process(
          frequency * jitter_multiplier_,
          ramps.slave[j],
          gate);
      ramps.slave[j]++;
      gate++;
    }
  }
}

}  // namespace marbles
