/// QasTools: Desktop toolset for the Linux sound system ALSA.
/// \copyright See COPYING file.

#include "painter_thread_shared.hpp"
#include "image_set_meta.hpp"
#include "image_set_state.hpp"
#include "paint_job.hpp"
#include "painter.hpp"
#include "painter_simple.hpp"
#include <iostream>

namespace dpe
{

Painter_Thread_Shared::Painter_Thread_Shared ()
: _painter_def ( std::make_shared< dpe::Painter_Simple > () )
{
}

Painter_Thread_Shared::~Painter_Thread_Shared () = default;

void
Painter_Thread_Shared::install_painter (
    const std::shared_ptr< dpe::Painter > & painter_n )
{
  _painters.push_back ( painter_n );
}

void
Painter_Thread_Shared::enqueue_job ( std::unique_ptr< dpe::Paint_Job > pjob_n )
{
  {
    _queue_mutex.lock ();
    _queue.push_back ( std::move ( pjob_n ) );
    _queue_mutex.unlock ();
  }
  _queue_cond.wakeOne ();
}

bool
Painter_Thread_Shared::is_idle () const
{
  bool res = false;
  {
    _queue_mutex.lock ();
    res = ( _queue.empty () && ( _jobs_rendering == 0 ) );
    _queue_mutex.unlock ();
  }
  return res;
}

bool
Painter_Thread_Shared::process_job ()
{
  // Fetch job
  std::unique_ptr< dpe::Paint_Job > pjob;
  {
    _queue_mutex.lock ();
    while ( _queue.empty () ) {
      _queue_cond.wait ( &_queue_mutex );
    }
    pjob = std::move ( _queue.front () );
    _queue.pop_front ();
    if ( pjob ) {
      ++_jobs_rendering;
    }
    _queue_mutex.unlock ();
  }

  // Check if this is a valid job
  if ( !pjob ) {
    return false;
  }

  // Paint job
  {
    std::shared_ptr< dpe::Painter > painter;
    // Search painter
    {
      const unsigned int group_type ( pjob->meta->group_type );
      unsigned int group_variant ( pjob->meta->group_variant );
      while ( true ) {
        for ( const auto & pcur : _painters ) {
          if ( ( pcur->group_type () == group_type ) &&
               ( pcur->group_variant () == group_variant ) ) {
            painter = pcur;
            break;
          }
        }
        if ( painter ) {
          break;
        }
        if ( group_variant == 0 ) {
          // Group variant is 0 but still no painter found
          break;
        }
        // Search again with default variant
        group_variant = 0;
      }
    }
    // Use default painter on demand
    if ( !painter ) {
      painter = _painter_def;
    }
    // Paint using the detected painter
    painter->check_and_paint ( pjob.get () );
  }

  // Job finished
  {
    pjob->state->one_done ();
    {
      _queue_mutex.lock ();
      --_jobs_rendering;
      _queue_mutex.unlock ();
    }
  }

  // Return success
  return true;
}

void
Painter_Thread_Shared::abort_threads ( std::size_t num_n )
{
  {
    _queue_mutex.lock ();
    for ( std::size_t ii = 0; ii < num_n; ++ii ) {
      _queue.push_back ( std::unique_ptr< dpe::Paint_Job > () );
    }
    _queue_mutex.unlock ();
  }
  _queue_cond.wakeAll ();
}

} // namespace dpe
