/**
  \class CIntgPanel
  \brief Integrator, reverse of Differentatior
  
  This class is a panel that is the reverse of a Differentiator: it's an
  integrator, that is, it accumulates differences to reconstruct the
  original image. It's name is derived from the electronic equivalent
  with resistors, capacitors and coils.
  
  Because of round-off errors, DCT (de)compression, bias, etc. (aka "noise")
  the integration is not perfect. So every once in a while a 'real' YUV
  image is needed to refresh the image.
 */  


#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>

#include "IntgPanel.h"
#ifdef HAVE_INTELCPU
#include "video_asm.h"
#endif

/**
  \fn CIntgPanel::CIntgPanel(CCamPanel *yuv_panel, CCamPanel *diff_panel, const char *name = "intg.yuv", const char *desc = "YUV Integrator")
  \brief Constructor
  \param yuv_panel The panel with unprocessed, 'real' YUV images.
  \param diff_panel The panel with the differential images.
  \param name The name of this panel; default "intg.yuv"
  \param desc The description of this panel; default "YUV integrator"
  
 */
CIntgPanel::CIntgPanel(CCamPanel *yuv_panel, CCamPanel *diff_panel, const char *name, const char *desc)
	: CCamPanel(name, desc, YUV420)
{
   pYUVPanel = yuv_panel;
   pDiffPanel = diff_panel;
   if (pYUVPanel == NULL || pDiffPanel == NULL)
     return;
   if (pYUVPanel->GetPanelType() != YUV420 ||
       pDiffPanel->GetPanelType() != YUV420) {
     pYUVPanel = NULL;
     pDiffPanel = NULL;
     return;
   }
 
   if (pDiffPanel && pYUVPanel) {
     // Initial size
     SetSize(pDiffPanel->GetImageSize());

     // Register usage
     ConnectUsage(pDiffPanel);
     ConnectUsage(pYUVPanel);
     // Make sure updates in DiffPanel arrive here; we consider DiffPanel as our 'primary' parent
     ConnectResizes(pDiffPanel);
   }

   // Initialize refresh counters; get fresh image ever 100 updates
   RefreshCountdown = 0;
   Refreshes = 100;
}



// private

void CIntgPanel::Calculate(int n, void *dst, void *src)
{
   if (dst == NULL || src == NULL)
     return;

#ifdef HAVE_INTELCPU
#if HAVE_MMX
   calc_intg128_smx(n, dst, src);
#else        
   calc_intg128(n, dst, src);
#endif   
#else
   // Non-assembly version
   int i;
   uchar *d, *s;
   
   d = (uchar *)dst;
   s = (uchar *)src;
   
   for (i = 0; i < n; i++) {
      *d += ((*s - 128) << 1);
      d++;
      s++;
   }
#endif
}

// protected slots

void CIntgPanel::UpdatePanel()
{
   void *dsty, *dstu, *dstv;
   void *srcy, *srcu, *srcv;
   int pixels;

   dsty = ImgY.bits();
   dstu = ImgU.bits();
   dstv = ImgV.bits();

   pixels = image_w * image_h;   
   if (RefreshCountdown == 0) {
     srcy = pYUVPanel->GetImage(0).bits();
     srcu = pYUVPanel->GetImage(1).bits();
     srcv = pYUVPanel->GetImage(2).bits();
     memcpy(dsty, srcy, pixels     );
     memcpy(dstu, srcu, pixels >> 2);
     memcpy(dstv, srcv, pixels >> 2);
     RefreshCountdown = Refreshes;
   }
   else {
     srcy = pDiffPanel->GetImage(0).bits();
     srcu = pDiffPanel->GetImage(1).bits();
     srcv = pDiffPanel->GetImage(2).bits();
     Calculate(pixels,      dsty, srcy);
     Calculate(pixels >> 2, dstu, srcu);
     Calculate(pixels >> 2, dstv, srcv);
     RefreshCountdown--;
   }

   emit Updated();
}


// public

/**
  \fn void CIntgPanel::SetRefresh(int n)
  \brief Set new refresh rate
  \param n New refresh rate
  
  Every once in a while, a fresh image is needed because using a differential
  and then going back with an integrator causes all kinds of errors, like
  round off errors, drifts, etc. Every N frames, a 'real' image will be
  requested instead of a diff. This function sets this refresh rate.
 */
void CIntgPanel::SetRefresh(int n)
{
   if (n > 0)
     Refreshes = n;
   if (RefreshCountdown > Refreshes)
     RefreshCountdown = Refreshes;
}

