/*
 * Author: Harry van Haaren 2013
 *         harryhaaren@gmail.com
 * 
 *  This program 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.
 *  
 *  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 <http://www.gnu.org/licenses/>.
 */

#include "looperclip.hxx"

#include <stdio.h>
#include "config.hxx"
#include "jack.hxx"
#include "event.hxx"
#include "eventhandler.hxx"
#include "audiobuffer.hxx"

#include "controllerupdater.hxx"
#include "timemanager.hxx"



extern Jack* jack;

LooperClip::LooperClip(int t, int s) :
  Stately(),
  track(t),
  scene(s)
{
  _buffer = new AudioBuffer();
  _buffer->nonRtResize( 4410 );
  init();
}

void LooperClip::init()
{
  _loaded     = false;
  _playing    = false;
  _recording  = false;
  
  _queuePlay  = false;
  _queueStop  = false;
  _queueRecord= false;
  
  if ( _buffer )
  {
    _buffer->init();
  }
  _newBufferInTransit = false;
  
  _playhead   = 0;
  _recordhead = 0;
  
  
}

void LooperClip::save()
{
  // ensure the buffer exists, and is saveable (not recording)
  if ( _loaded && !_recording && !_queueRecord )
  {
    char buffer [50];
    sprintf (buffer, "LC::save() track %i, scene %i", track,scene);
    EventGuiPrint e( buffer );
    writeToGuiRingbuffer( &e );
    
    int frames = _buffer->getAudioFrames();
    EventRequestSaveBuffer e2( track, scene, frames );
    writeToGuiRingbuffer( &e2 );
  }
  else
  {
    // notify of "success" of save if there *is* no state to save
    Stately::success();
  }
}

void LooperClip::reset()
{
  // TODO make the LooperClip reset to initial state
  if ( _loaded )
  {
    char buffer [50];
    sprintf (buffer, "LC::reset() track %i, scene %i", track,scene);
    EventGuiPrint e( buffer );
    writeToGuiRingbuffer( &e );
    
    // set "progress" to zero as there's no clip anymore
    jack->getControllerUpdater()->setTrackSceneProgress(track, scene, 0 );
  }
  else
  {
    //SaveAble::done();
  }
  
  init();
}

/// loads a sample: eg from disk, unloading current sample if necessary
void LooperClip::load( AudioBuffer* ab )
{
  _loaded = true;
  _recording = false;
  _playing    = false;
  
  _queuePlay  = false;
  _queueStop  = false;
  _queueRecord= false;
  
  if ( _buffer )
  {
    EventDeallocateBuffer e( _buffer );
    writeToGuiRingbuffer( &e );
  }
  
  _buffer = ab;
  
  _playhead = 0;
  jack->getControllerUpdater()->setTrackSceneProgress(track, scene, 0 );
  
  // set the endpoint to the buffer's size
  _recordhead = _buffer->getData().size();
  
#ifdef DEBUG_BUFFER
  char buffer [50];
  sprintf (buffer, "LC::load() t %i, s %i, aF %i",track, scene, int(_buffer->getAudioFrames()) );
  EventGuiPrint e( buffer );
  writeToGuiRingbuffer( &e );
#endif
}

void LooperClip::setRequestedBuffer( AudioBuffer* ab )
{
  if ( _buffer )
  {
    size_t size = _buffer->getData().size();
    memcpy( &ab->getData().at(0), &_buffer->getData().at(0), sizeof(float)*size);
    
    ab->setID   ( _buffer->getID()    );
    ab->setBeats( _buffer->getBeats() );
    
    EventDeallocateBuffer e( _buffer );
    writeToGuiRingbuffer( &e );
  }
  
  _buffer = ab;
  
  _newBufferInTransit = false;
}



void LooperClip::recieveSaveBuffer( AudioBuffer* saveBuffer )
{
  if ( saveBuffer->getData().size() >= _buffer->getData().at(0) )
  {
    // copy current contents into save buffer,
    size_t framesBySize = _buffer->getAudioFrames();
    memcpy( &saveBuffer->getData().at(0), &_buffer->getData().at(0), sizeof(float)*framesBySize);
    
    saveBuffer->setID   ( _buffer->getID()    );
    saveBuffer->setBeats( _buffer->getBeats() );
    saveBuffer->setAudioFrames( _buffer->getAudioFrames() );
    
    EventStateSaveBuffer e ( track, scene, saveBuffer );
    writeToGuiRingbuffer( &e );
    
    Stately::success();
  }
  else
  {
    char buffer [50];
    sprintf (buffer, "LC:: %i, %i: can't save, buf too small",track, scene );
    EventGuiPrint e( buffer );
    writeToGuiRingbuffer( &e );
    Stately::error("");
  }
}



void LooperClip::record(int count, float* L, float* R)
{
  // write "count" samples into current buffer.
  if ( _buffer )
  {
    size_t size = _buffer->getData().size();
    
    for(int i = 0; i < count; i++)
    {
      if ( _recordhead < size )
      {
        _buffer->getData().at( _recordhead ) = *L++;
        _recordhead++;
      }
      else
      {
        // break: this is *BAD*, audio data is lost but the buffer isn't here
        // yet to hold new audio data so there's no option. This has not been
        // encountered in actual usage, only during the development process.
        char buffer [50];
        sprintf (buffer, "LooperClip t %i, s %i, Error: out of mem!",track, scene );
        EventGuiPrint e( buffer );
        writeToGuiRingbuffer( &e );
#ifdef BUILD_TESTS
        LUPPP_WARN("%s","buffer has no space");
#endif
        
        break;
      }
    }
  }
  
  _loaded = true;
}

unsigned long LooperClip::recordSpaceAvailable()
{
  if ( _buffer )
    return _buffer->getData().size() - _recordhead;
  
  return 0;
}

size_t LooperClip::audioBufferSize()
{
  if ( _buffer )
  {
    return _buffer->getData().size();
  }
  return 0;
}

void LooperClip::setBeats(int beats)
{
  if ( _buffer )
  {
    _buffer->setBeats( beats );
  }
}

int LooperClip::getBeats()
{
  if ( _buffer )
    return _buffer->getBeats();
  
  return 0;
}

long LooperClip::getBufferLenght()
{
  return _recordhead;
}

void LooperClip::bar()
{
  bool change = false;
  GridLogic::State s = GridLogic::STATE_EMPTY;
  
  // first update the buffer, as time has passed
  if ( _recording )
  {
    // FIXME: assumes 4 beats in a bar
    _buffer->setBeats( _buffer->getBeats() + 4 );
    _buffer->setAudioFrames( jack->getTimeManager()->getFpb() * _buffer->getBeats() );
  }
  
  if ( _playhead > 0.9 * _recordhead )
  {
    _playhead = 0.f;
  }
  
  if ( _queuePlay && _loaded )
  {
    //LUPPP_NOTE("QPLay + loaded" );
    _playing = true;
    s = GridLogic::STATE_PLAYING;
    _recording = false;
    _queuePlay = false;
    change = true;
    
    _playhead = 0;
  }
  else if ( _queueStop && _loaded )
  {
    _playing   = false;
    s = GridLogic::STATE_STOPPED;
    _recording = false;
    _queueStop = false;
    change = true;
    // set "progress" to zero, as we're stopped!
    jack->getControllerUpdater()->setTrackSceneProgress(track, scene, 0 );
  }
  else if ( _queueRecord )
  {
    _recording   = true;
    s = GridLogic::STATE_RECORDING;
    _playing     = false;
    _queueRecord = false;
    change = true;
    
    if ( _buffer )
    {
      _buffer->setBeats( 0 );
    }
    
    _recordhead = 0;
  }
  else if ( _queuePlay )
  {
    // clip was queued, but there's nothing loaded
    _queuePlay = false;
    change = true;
  }
  
  if ( change )
  {
    jack->getControllerUpdater()->setSceneState(track, scene, s );
  }
}

void LooperClip::neutralize()
{
  _queuePlay = false;
  _queueRecord = false;
  _queueStop = false;
}

bool LooperClip::somethingQueued()
{
  if ( _queuePlay || _queueStop || _queueRecord )
  {
    return true;
  }
  return false;
}

void LooperClip::queuePlay(bool qP)
{
  _queuePlay   = true;
  _queueStop   = false;
  _queueRecord = false;
}

void LooperClip::queueStop()
{
  // comment
  if ( _loaded )
  {
    _queueStop   = true;
    _queuePlay   = false;
  }
}

void LooperClip::queueRecord()
{
  _queueRecord = true;
  _queuePlay   = false;
  _queueStop   = false;
}

GridLogic::State LooperClip::getState()
{
  GridLogic::State s = GridLogic::STATE_EMPTY;
  
  if ( _loaded       )
    s = GridLogic::STATE_STOPPED;
  if ( _playing      )
    s = GridLogic::STATE_PLAYING;
  if ( _recording    )
    s = GridLogic::STATE_RECORDING;
  if ( _queuePlay    )
    s = GridLogic::STATE_PLAY_QUEUED;
  if ( _queueStop    )
    s = GridLogic::STATE_STOP_QUEUED;
  if ( _queueRecord  )
    s = GridLogic::STATE_RECORD_QUEUED;
  
  return s;
}

bool LooperClip::playing()
{
  return _playing;
}

bool LooperClip::getQueueStop()
{
  return _queueStop;
}

bool LooperClip::getQueuePlay()
{
  return _queuePlay;
}

bool LooperClip::getLoaded()
{
  return _loaded;
}

bool LooperClip::recording()
{
  return _recording;
}

void LooperClip::newBufferInTransit(bool n)
{
  _newBufferInTransit = n;
}

bool LooperClip::newBufferInTransit()
{
  return _newBufferInTransit;
}

float LooperClip::getSample(float playSpeed)
{
  if ( _buffer && _buffer->getData().size() > 0 )
  {
    if ( _playhead >= _recordhead ||
         _playhead >= _buffer->getData().size() ||
         _playhead < 0  )
    {
      _playhead = 0;
      
      EventGuiPrint e( "LooperClip resetting _playhead" );
      //writeToGuiRingbuffer( &e );
    }
    
    std::vector<float>& v = _buffer->getData();
    float tmp = v.at(_playhead);
    _playhead += playSpeed;
    
    return tmp;
  }
  
  return 0.f;
}

float LooperClip::getProgress()
{
  if ( _buffer && _playing )
  {
    float p = float(_playhead) / _recordhead;
    //printf("LooperClip progress %f\n", p );
    return p;
  }
  return 0.f;
}

#ifdef BUILD_TESTS
void LooperClip::setState( bool load, bool play, bool rec, bool qPlay, bool qStop, bool qRec )
{
  _loaded       = load;
  _playing      = play;
  _recording    = rec;
    
  _queuePlay    = qPlay;
  _queueStop    = qStop;
  _queueRecord  = qRec;
}
#endif
