///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// File:           ThreadedMIDIOutput.cpp                                    //
//                                                                           //
// Functionality:  Thread safe MIDI out support.                             //
//                                                                           //
// Author:         Rolf Meyerhoff (rm@matrix44.de)                           //
//                                                                           //
// Copyright:      (C) 2009 by Rolf Meyerhoff. All rights reserved.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// License:                                                                  //
//                                                                           //
// This 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.                                           //
//                                                                           //
// This  library 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 this library;  if not,  write to the Free Software Foundation, //
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.              //
///////////////////////////////////////////////////////////////////////////////
#include "reaper_cc121.h"

//TODO: This code has been taken unaltered from the original SDK and may need
//      some attention if this extensions actually meets a mac compiler.

#ifndef _WIN32 // let OS X use this threading step

#include "../../WDL/mutex.h"
#include "../../WDL/ptrlist.h"

class threadedMIDIOutput : public midi_Output
{
public:
  threadedMIDIOutput(midi_Output *out) 
  { 
    m_output=out;
    m_quit=false;
    DWORD id;
    m_hThread=CreateThread(NULL,0,threadProc,this,0,&id);
  }
  virtual ~threadedMIDIOutput() 
  {
    if (m_hThread)
    {
      m_quit=true;
      WaitForSingleObject(m_hThread,INFINITE);
      CloseHandle(m_hThread);
      m_hThread=0;
      Sleep(30);
    }

    delete m_output;
    m_empty.Empty(true);
    m_full.Empty(true);
  }

  virtual void SendMsg(MIDI_event_t *msg, int frame_offset) // frame_offset can be <0 for "instant" if supported
  {
    if (!msg) return;

    WDL_HeapBuf *b=NULL;
    if (m_empty.GetSize())
    {
      m_mutex.Enter();
      b=m_empty.Get(m_empty.GetSize()-1);
      m_empty.Delete(m_empty.GetSize()-1);
      m_mutex.Leave();
    }
    if (!b && m_empty.GetSize()+m_full.GetSize()<500)
      b=new WDL_HeapBuf(256);

    if (b)
    {
      int sz=msg->size;
      if (sz<3)sz=3;
      int len = msg->midi_message + sz - (unsigned char *)msg;
      memcpy(b->Resize(len,false),msg,len);
      m_mutex.Enter();
      m_full.Add(b);
      m_mutex.Leave();
    }
  }

  virtual void Send(unsigned char status, unsigned char d1, unsigned char d2, int frame_offset) // frame_offset can be <0 for "instant" if supported
  {
    MIDI_event_t evt={0,3,status,d1,d2};
    SendMsg(&evt,frame_offset);
  }

  ///////////

  static DWORD WINAPI threadProc(LPVOID p)
  {
    WDL_HeapBuf *lastbuf=NULL;
    threadedMIDIOutput *_this=(threadedMIDIOutput*)p;
    unsigned int scnt=0;
    for (;;)
    {
      if (_this->m_full.GetSize()||lastbuf)
      {
        _this->m_mutex.Enter();
        if (lastbuf) _this->m_empty.Add(lastbuf);
        lastbuf=_this->m_full.Get(0);
        _this->m_full.Delete(0);
        _this->m_mutex.Leave();

        if (lastbuf) _this->m_output->SendMsg((MIDI_event_t*)lastbuf->Get(),-1);
        scnt=0;
      }
      else 
      {
        Sleep(1);
        if (_this->m_quit&&scnt++>3) break; //only quit once all messages have been sent
      }
    }
    delete lastbuf;
    return 0;
  }

  WDL_Mutex m_mutex;
  WDL_PtrList<WDL_HeapBuf> m_full,m_empty;

  HANDLE m_hThread;
  bool m_quit;
  midi_Output *m_output;
};




midi_Output *CreateThreadedMIDIOutput(midi_Output *output)
{
  if (!output) return output;
  return new threadedMIDIOutput(output);
}

#else

// windows doesnt need it since we have threaded midi outputs now
midi_Output *CreateThreadedMIDIOutput(midi_Output *output)
{
  return output;
}

#endif

///////////////////////////////// End of File /////////////////////////////////
