/**********************************************************************

   Audacity - A Digital Audio Editor
   Copyright 1999-2009 Audacity Team
   License: GPL v2 or later - see LICENSE.txt

   Dan Horgan

******************************************************************//**

\file CommandTargets
\brief Contains classes for 'command output targets' - objects which can
receive output from a command. For instance, a progress target might pass the
information to a GUI ProgressDialog. Using abstract targets means the command
objects needn't be concerned with what happens to the information.

Note: currently, reusing target objects is not generally safe - perhaps they
should be reference-counted.

\class CommandMessageTargetDecorator
\brief CommandMessageTargetDecorator is a CommandOutputTarget that forwards
its work on to another one.  Typically we derive from it to modify some 
functionality and forward the rest.

\class BriefCommandMessageTarget
\brief BriefCommandMessageTarget is a CommandOutputTarget that provides
status in a briefer listing 

\class LispyCommandMessageTarget
\brief LispyCommandMessageTarget is a CommandOutputTarget that provides status 
in a lispy style.

\class MessageDialogTarget
\brief MessageDialogTarget is a CommandOutputTarget that sends its status
to the LongMessageDialog.

\class CommandOutputTargets
\brief CommandOutputTargets a mix of three output classes to output 
progress indication, status messages and errors.

\class BriefCommandOutputTargets
\brief BriefCommandOutputTargets is a CommandOutputTargets that  replaces the 
status message target with the BriefCommandMessageTarget version.

\class LispifiedCommandOutputTargets
\brief LispifiedCommandOutputTargets is a CommandOutputTargets that  replaces the 
status message target with the LispyCommandMessageTarget version.

\class ProgressToMessageTarget
\brief ProgressToMessageTarget formats the percentage complete text as a message
and sends it to that message target.

*//*******************************************************************/

#ifndef __COMMANDTARGETS__
#define __COMMANDTARGETS__

#include <memory>
#include <vector>
#include <wx/string.h>
#include <wx/thread.h>

class wxStatusBar;

/// Interface for objects that can receive command progress information
class CommandProgressTarget /* not final */
{
public:
   virtual ~CommandProgressTarget() {}
   virtual void Update(double completed) = 0;
};

/// Interface for objects that can receive (string) messages from a command
class AUDACITY_DLL_API CommandMessageTarget /* not final */
{
public:
   CommandMessageTarget() {mCounts.push_back(0);}
   virtual ~CommandMessageTarget() { Flush();}
   virtual void Update(const wxString &message) = 0;
   virtual void StartArray();
   virtual void EndArray();
   virtual void StartStruct();
   virtual void EndStruct();
   virtual void AddItem(const wxString &value , const wxString &name = {} );
   virtual void AddBool(const bool value      , const wxString &name = {} );
   virtual void AddItem(const double value    , const wxString &name = {} );
   virtual void StartField( const wxString &name = {} );
   virtual void EndField( );
   virtual void Flush();
   wxString Escaped( const wxString & str);
   std::vector<int> mCounts;
};

class CommandMessageTargetDecorator : public CommandMessageTarget
{
public:
   CommandMessageTargetDecorator( CommandMessageTarget & target): mTarget(target) {}
   ~CommandMessageTargetDecorator() override { }
   void Update(const wxString &message) override { mTarget.Update( message );}
   void StartArray() override { mTarget.StartArray();}
   void EndArray() override { mTarget.EndArray();}
   void StartStruct() override { mTarget.StartStruct();}
   void EndStruct() override { mTarget.EndStruct();}
   void AddItem(const wxString &value , const wxString &name = {} ) override
      { mTarget.AddItem(value,name);}
   void AddBool(const bool value      , const wxString &name = {} ) override
      { mTarget.AddBool(value,name);}
   void AddItem(const double value    , const wxString &name = {} ) override
      { mTarget.AddItem(value,name);}
   void StartField( const wxString &name = {} ) override
      { mTarget.StartField(name);}
   void EndField( ) override
      { mTarget.EndField();}
   void Flush() override
      { mTarget.Flush();}
   CommandMessageTarget & mTarget;
};

class LispyCommandMessageTarget : public CommandMessageTargetDecorator /* not final */
{
public:
   LispyCommandMessageTarget( CommandMessageTarget & target): CommandMessageTargetDecorator(target) {};
   virtual void StartArray() override;
   virtual void EndArray() override;
   virtual void StartStruct() override;
   virtual void EndStruct() override;
   virtual void AddItem(const wxString &value , const wxString &name = {} )override;
   virtual void AddBool(const bool value      , const wxString &name = {} )override;
   virtual void AddItem(const double value    , const wxString &name = {} )override;
   virtual void StartField( const wxString &name = {} )override;
   virtual void EndField( ) override;
};

class BriefCommandMessageTarget : public CommandMessageTargetDecorator /* not final */
{
public:
   BriefCommandMessageTarget( CommandMessageTarget & target): CommandMessageTargetDecorator(target) {};
   virtual void StartArray() override;
   virtual void EndArray() override;
   virtual void StartStruct() override;
   virtual void EndStruct() override;
   virtual void AddItem(const wxString &value , const wxString &name = {} )override;
   virtual void AddBool(const bool value      , const wxString &name = {} )override;
   virtual void AddItem(const double value    , const wxString &name = {} )override;
   virtual void StartField( const wxString &name = {} )override;
   virtual void EndField( ) override;
};

/// Used to ignore a command's progress updates
class NullProgressTarget final : public CommandProgressTarget
{
public:
   virtual ~NullProgressTarget() {}
   void Update(double WXUNUSED(completed)) override {}
};

#if 0

//#include "ProgressDialog.h" // Member variable

/// Sends command progress information to a ProgressDialog
class GUIProgressTarget final : public CommandProgressTarget
{
private:
   ProgressDialog &mProgress;
public:
   GUIProgressTarget(ProgressDialog &pd)
      : mProgress(pd)
   {}
   virtual ~GUIProgressTarget() {}
   void Update(double completed) override
   {
      mProgress.Update(completed);
   }
};
#endif


///
class ProgressToMessageTarget final : public CommandProgressTarget
{
private:
   std::unique_ptr<CommandMessageTarget> mTarget;
public:
   ProgressToMessageTarget(std::unique_ptr<CommandMessageTarget> &&target)
      : mTarget(std::move(target))
   { }
   virtual ~ProgressToMessageTarget()
   {
   }
   void Update(double completed) override
   {
      mTarget->Update(wxString::Format(wxT("%.2f%%"), completed*100));
   }
};

/// Used to ignore a command's message updates
class NullMessageTarget final : public CommandMessageTarget
{
public:
   virtual ~NullMessageTarget() {}
   void Update(const wxString &) override {}
};

/// Displays messages from a command in an AudacityMessageBox
class AUDACITY_DLL_API MessageBoxTarget final : public CommandMessageTarget
{
public:
   virtual ~MessageBoxTarget() {}
   void Update(const wxString &message) override;
};

/// Displays messages from a command in a wxStatusBar
class AUDACITY_DLL_API StatusBarTarget final : public CommandMessageTarget
{
private:
   wxStatusBar &mStatus;
public:
   StatusBarTarget(wxStatusBar &sb)
      : mStatus(sb)
   {}
   void Update(const wxString &message) override;
};

/// Constructs a response (to be sent back to a script)
class ResponseTarget final : public CommandMessageTarget
{
private:
   wxSemaphore mSemaphore;
   wxString mBuffer;
public:
   ResponseTarget()
      : mSemaphore(0, 1),
        mBuffer(wxEmptyString)
   { 
      // Cater for handling long responses quickly.
      mBuffer.Alloc(40000);
   }
   virtual ~ResponseTarget()
   {
   }
   void Update(const wxString &message) override
   {
      mBuffer += message;
   }
   virtual void Flush() override
   {
      mSemaphore.Post();
   }
   wxString GetResponse()
   {
      mSemaphore.Wait();
      return mBuffer;
   }
};

/// Sends messages to two message targets at once
class CombinedMessageTarget final : public CommandMessageTarget
{
private:
   std::unique_ptr<CommandMessageTarget> m1, m2;
public:
   CombinedMessageTarget(std::unique_ptr<CommandMessageTarget> &&t1,
                         std::unique_ptr<CommandMessageTarget> &&t2)
      : m1(std::move(t1)), m2(std::move(t2))
   {
      wxASSERT(m1);
      wxASSERT(m2);
   }
   ~CombinedMessageTarget()
   {
   }
   void Update(const wxString &message) override
   {
      m1->Update(message);
      m2->Update(message);
   }
};


/** 
\class TargetFactory
\brief TargetFactory makes Command output targets.  By default, we ignore progress 
updates but display all other messages directly
*/
class TargetFactory
{
public:
   static std::unique_ptr<CommandProgressTarget> ProgressDefault()
   {
      return std::make_unique<NullProgressTarget>();
   }

   static std::shared_ptr<CommandMessageTarget> MessageDefault()
   {
      return std::make_shared<MessageBoxTarget>();
   }
};

/// Used to aggregate the various output targets a command may have.
/// Assumes responsibility for pointers passed into it.
/// mProgressTarget is a unique pointer, but mStatusTraget and
/// mErrorTarget are shared ones, because they may both point to the same 
/// output 
class CommandOutputTargets /* not final */
{
public:
   std::unique_ptr<CommandProgressTarget> mProgressTarget;
   std::shared_ptr<CommandMessageTarget> mStatusTarget;
   std::shared_ptr<CommandMessageTarget> mErrorTarget;
public:
   // && is not a reference to a reference, but rather a way to allow reference to a temporary
   // that will be gone or transferred after we have taken it.  It's a reference to an xvalue, 
   // or 'expiring value'.
   CommandOutputTargets(std::unique_ptr<CommandProgressTarget> &&pt = TargetFactory::ProgressDefault(),
                       std::shared_ptr<CommandMessageTarget>  &&st = TargetFactory::MessageDefault(),
                       std::shared_ptr<CommandMessageTarget> &&et = TargetFactory::MessageDefault())
      : mProgressTarget(std::move(pt)), mStatusTarget(st), mErrorTarget(et)
   { }
   ~CommandOutputTargets()
   {
   }
   // Lots of forwarding...
   void Progress(double completed)
   {
      if (mProgressTarget)
         mProgressTarget->Update(completed);
   }
   void Status(const wxString &status, bool bFlush=false)
   {
      if (mStatusTarget){
         mStatusTarget->Update(status);
         if( bFlush )
            mStatusTarget->Flush();
      }
   }
   void Error(const wxString &message)
   {
      if (mErrorTarget)
         mErrorTarget->Update(message);
   }
   void StartArray()
   {
      if (mStatusTarget)
         mStatusTarget->StartArray();
   }
   void EndArray()
   {
      if (mStatusTarget)
         mStatusTarget->EndArray();
   }
   void StartStruct()
   {
      if (mStatusTarget)
         mStatusTarget->StartStruct();
   }
   void EndStruct()
   {
      if (mStatusTarget)
         mStatusTarget->EndStruct();
   }
   void StartField(const wxString &name)
   {
      if (mStatusTarget)
         mStatusTarget->StartField(name);
   }
   void EndField()
   {
      if (mStatusTarget)
         mStatusTarget->EndField();
   }
   void AddItem(const wxString &value , const wxString &name = {} )
   {
      if (mStatusTarget)
         mStatusTarget->AddItem( value, name );
   }
   void AddBool(const bool value      , const wxString &name = {} )
   {
      if (mStatusTarget)
         mStatusTarget->AddItem( value, name );
   }
   void AddItem(const double value    , const wxString &name = {} )
   {
      if (mStatusTarget)
         mStatusTarget->AddItem( value, name );
   }
};

class AUDACITY_DLL_API LispifiedCommandOutputTargets
   : public CommandOutputTargets
{
public :
   LispifiedCommandOutputTargets( CommandOutputTargets & target );
   ~LispifiedCommandOutputTargets();
private:
   CommandOutputTargets * pToRestore;
};

class AUDACITY_DLL_API BriefCommandOutputTargets : public CommandOutputTargets
{
public :
   BriefCommandOutputTargets( CommandOutputTargets & target );
   ~BriefCommandOutputTargets();
private:
   CommandOutputTargets * pToRestore;
};

class InteractiveOutputTargets : public CommandOutputTargets
{
public:
   InteractiveOutputTargets();

};

#endif /* End of include guard: __COMMANDTARGETS__ */
