/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Thomas Bretz 1/2008 <mailto:tbretz@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2008
!
!
\* ======================================================================== */
#include "Camera.h"

#include "MLog.h"
#include "MLogManip.h"

#include "MVideo.h"
#include "PixClient.h"

//ClassImp(Camera);

using namespace std;

Camera::Camera(PixClient &client, Int_t nch) : MThread("Camera"), fClient(client), fVideo(0)
{
    fVideo = new MVideo;
    fVideo->Open(nch);

    RunThread();
}

Camera::~Camera()
{
    // Shut down the thread
    CancelThread();

    // Now delete (close) the device
    delete fVideo;
}

void Camera::ExitLoop()
{
    CancelThread();

    // Some information
    //cout << fNumFrame-1 << "frames processed." << endl;
    //cout << fNumSkipped << "frames skipped." << endl;
}

void Camera::ProcessFrame(char *img)
{
    gettimeofday(&fTime, NULL);

    const Int_t W = fVideo->GetWidth();
    const Int_t H = fVideo->GetHeight();

    const Bool_t crop = W!=768 || H!=576;

    // FIXME: This only works for one RGB24
    if (!crop)
    {
        // FIXME: Grey scale assumed!
        const char *end = img + 768*576*depth;
        char *beg = fImg;
        while (img < end)
        {
            *beg++ = *img;
            img += depth;
        }
    }
    else
    {
        const Int_t w = TMath::Min(W, 768);
        const Int_t h = TMath::Min(H, 576);

        memset(fImg, 0, 768*576);
        for (int y=0; y<h; y++)
            for (int x=0; x<w; x++)
                fImg[x+y*768] = ((UInt_t)(UChar_t)img[(x+y*W)*3]+(UInt_t)(UChar_t)img[(x+y*fVideo->GetWidth())*3+1]+(UInt_t)(UChar_t)img[(x+y*fVideo->GetWidth())*3+2])/3;
    }

    fClient.ProcessFrame(fNumFrame-1, (byte*)fImg, &fTime);
}

Int_t Camera::Thread()
{
    fNumSkipped = 0;
    fNumFrame   = 1;

    if (!fVideo->IsOpen())
    {
        gLog << err << "Camera::Thread: ERROR - Device not open." << endl;
        return kFALSE;
    }

    //Print();

    for (int f=0; f<fVideo->GetNumBuffers()-1; f++)
        if (!fVideo->CaptureStart(f))
            return kFALSE;

    Int_t timeouts = 0;

    while (1)
    {
        // If cacellation is requested cancel here
        TThread::CancelPoint();

        // Start to capture into the buffer which has just been processed
        if (!fVideo->CaptureStart(fNumFrame))
            break;

        // If cacellation is requested cancel here
        TThread::CancelPoint();

        // Check and wait until capture into the next buffer is finshed
        char *img = 0;
        switch (fVideo->CaptureWait(++fNumFrame, &img))
        {
        case kTRUE: // Process frame
            ProcessFrame(img);
            timeouts = 0;
            continue;

        case kFALSE: // Waiting failed
            break;

        case kSKIP:  // Skip frame
            fNumFrame--;
            fNumSkipped++;
            if (timeouts++<5)
                continue;

            cout << "ERROR - At least five captured images timed out." << endl;
            break;
        }

        break;
    }

    // Is this necessary?!?
    //for (int i=0; i<frames-1; i++)
    //    video.CaptureWait((f+i+1)%frames);

    cout << fNumFrame-1 << " frames processed." << endl;
    cout << fNumSkipped << " frames skipped." << endl;

    return kTRUE;
}

void Camera::Loop(unsigned long nof)
{
}

void Camera::SetChannel(int chan)
{
    CancelThread();
    fVideo->SetChannel(chan);
    RunThread();
}

