/*
    csPerfThread.hpp:

    Copyright (C) 2005 Istvan Varga

    This file is part of Csound.

    The Csound Library 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 2.1 of the License, or (at your option) any later version.

    Csound 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 Csound; if not, write to the Free Software
    Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA
*/

#ifndef CSOUND_CSPERFTHREAD_HPP
#define CSOUND_CSPERFTHREAD_HPP

class CsoundPerformanceThreadMessage;
class CsPerfThread_PerformScore;

#ifdef SWIG
%include <std_string.i>
#else
#include <string>
#endif

/**
 * CsoundPerformanceThread(Csound *)
 * CsoundPerformanceThread(CSOUND *)
 *
 * Performs a score in a separate thread until the end of score is reached,
 * the playback (which is paused by default) is stopped by calling
 * CsoundPerformanceThread::Stop(), or an error occurs.
 * The constructor takes a Csound instance pointer as argument; it assumes
 * that csoundCompile() was called successfully before creating the
 * performance thread. Once the playback is stopped for one of the above
 * mentioned reasons, the performance thread calls csoundCleanup() and
 * returns.

  An example program using the CsoundPerformanceThread class

#include <stdio.h>
#include "csound.hpp"
#include "csPerfThread.hpp"

int32_t main(int32_t argc, char *argv[])
{
 int32_t result=0;
 Csound cs;
 result = cs.Compile(argc,argv);

 if(!result)
 {
   CsoundPerformanceThread perfThread(cs.GetCsound());
   perfThread.Play(); // Starts performance
   while(perfThread.GetStatus() == 0);
                       // nothing to do here...
                       // but you could process input events, graphics etc
   perfThread.Stop();  // Stops performance. In fact, performance should have
                       // already finished, so this is just an example of how
                       //to stop if you need
   perfThread.Join();  // always call Join() after Stop() as a rule of thumb.
 }
 else{
   printf("csoundCompile returned an error\n");
   return 1;
 }

 return 0;
}
*/

#ifdef __SSE__
  #include <xmmintrin.h>
#ifndef _MM_DENORMALS_ZERO_ON
  #define _MM_DENORMALS_ZERO_MASK   0x0040
  #define _MM_DENORMALS_ZERO_ON     0x0040
  #define _MM_DENORMALS_ZERO_OFF    0x0000
  #define _MM_SET_DENORMALS_ZERO_MODE(mode)                                   \
            _mm_setcsr((_mm_getcsr() & ~_MM_DENORMALS_ZERO_MASK) | (mode))
   #define _MM_GET_DENORMALS_ZERO_MODE()                                      \
            (_mm_getcsr() & _MM_DENORMALS_ZERO_MASK)
 #endif
#else
#if !defined(_MM_SET_DENORMALS_ZERO_MODE)
  #define _MM_DENORMALS_ZERO_MASK   0
  #define _MM_DENORMALS_ZERO_ON     0
  #define _MM_DENORMALS_ZERO_OFF    0
  #define _MM_SET_DENORMALS_ZERO_MODE(mode)
#endif
#endif

typedef struct {
    void *cbuf;
    void *sfile;
    void *thread;
    bool running;
    void* condvar;
    void* mutex;
    char *sfname;
    CSOUND *csound;
} recordData_t;

class PUBLIC CsoundPerformanceThread {
 private:
    CSOUND  *csound;
    volatile CsoundPerformanceThreadMessage *firstMessage;
    CsoundPerformanceThreadMessage *lastMessage;
    void    *queueLock;         // this is actually a mutex
    void    *pauseLock;
    void    *flushLock;
    void    *recordLock;
    void    *perfThread;
    int32_t     paused;
    int32_t     status;
    void    *cdata;
    recordData_t recordData;
    int32_t  running;
    void (*processcallback)(void *cdata);
    int32_t  Perform();
    void csPerfThread_constructor(CSOUND *);
    void QueueMessage(CsoundPerformanceThreadMessage *);
 public:
#ifdef SWIGPYTHON
  PyThreadState *_tstate;
  pycallbackdata pydata;
#endif
  
  /**
   * Returns 1 if the performance thread is running, 0 otherwise
   */
  int32_t IsRunning() { return running;}

  /**
  * Returns the process callback as a void pointer
  */
  void *GetProcessCallback() { return (void *)processcallback; }

  /**
   * Sets the process callback.
   */
   void SetProcessCallback(void (*Callback)(void *), void *cbdata){
    processcallback = Callback;
    cdata = cbdata;
   }
    /**
     * Returns the Csound instance pointer.
     */
    CSOUND *GetCsound()
    {
      return csound;
    }
    
    /**
     * Returns the current status, zero if still playing, positive if
     * the end of score was reached or performance was stopped, and
     * negative if an error occured.
     */
    int32_t GetStatus()
    {
      return status;
    }
    
    /**
     * Continues performance if it was paused.
     */
    void Play();
    
    /**
     * Pauses performance (can be continued by calling Play()).
     */
    void Pause();
    
    /**
     * Pauses performance unless it is already paused, in which case
     * it is continued.
     */
    void TogglePause();
    
    /**
     * Stops performance (cannot be continued).
     */
    void Stop();
    
    /**
     * Starts recording the output from Csound. The sample rate and number
     * of channels are taken directly from the running Csound instance.
     */
    void Record(std::string filename, int32_t samplebits = 16, int32_t numbufs = 4);
    
    /**
     * Stops recording and closes audio file.
     */
    void StopRecord();
    
    /**
     * Sends a score event of type 'opcod' (e.g. 'i' for a note event), with
     * 'pcnt' p-fields in array 'p' (p[0] is p1). If absp2mode is non-zero,
     * the start time of the event is measured from the beginning of
     * performance, instead of the default of relative to the current time.
     */
    void ScoreEvent(int32_t absp2mode, char opcod, int32_t pcnt, const MYFLT *p);
    
    /**
     * Sends a score event as a string, similarly to line events (-L).
     */
    void InputMessage(const char *s);
    
    /**
     * Sets the playback time pointer to the specified value (in seconds).
     */
    void SetScoreOffsetSeconds(double timeVal);


    /**
     * Compiles the given orchestra code
     */
    void CompileOrc(const char *code);


     /**
     * Evaluates the given code, calls the `returncb` callback with the 
     * value passed to the `return` opcode in global space. 
     */
    void EvalCode(const char *code, void (*returncb)(MYFLT));


    /**
     * Calls the given callback within the context of the performance thread
     */
    void RequestCallback(void (*func)(CsoundPerformanceThread *));


     /**
     * Waits until all pending messages (pause, send score event, etc.)
     * are actually received by the performance thread.
     */
    void FlushMessageQueue();

     /**
     * Waits until the performance is finished or fails, and returns a
     * positive value if the end of score was reached or Stop() was called,
     * and a negative value if an error occured. Also releases any resources
     * associated with the performance thread object.
     */
    int32_t Join();

  
    // --------
    CsoundPerformanceThread(Csound *);
    CsoundPerformanceThread(Csound &);
    CsoundPerformanceThread(CSOUND *);
    ~CsoundPerformanceThread();
    // --------
    friend class CsoundPerformanceThreadMessage;
    friend class CsPerfThread_PerformScore;
};


#endif  // CSOUND_CSPERFTHREAD_HPP
