/* ======================================================================== *\
!
! *
! * 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): Oscar Blanch    12/2001 <mailto:blanch@ifae.es>
!   Author(s): Thomas Bretz    08/2002 <mailto:tbretz@astro.uni.wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2003
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
//  MBlindPixelCalc
//
//  This is the specific image cleaning for a list of pixels. This task
//  sets the pixels listed in fPixelsIdx to unused so they should not be
//  used for analysis (eg calculation of hillas parameters).
//
//  You can use MBlindPixelCalc::SetUseInterpolation to replaced the
//  blind pixels by the average of its neighbors instead of unmapping
//  them. If you want to include the central pixel use
//  MBlindPixelCalc::SetUseCentralPixel.
//
//  You have three options:
//   1) Call SetUseBlindPixels():
//      This will take an existing MBlindPixels container filled from
//      elsewhere (eg. MCT1ReadPreProc) and use this pixels as blind
//      pixels.
//   2) Call SetPixels():
//      This will setup an array with pixel numbers. These pixels are used
//      as blind pixels.
//   3) Neither 1) nor 2)
//      This options tries to identify the starfield from the
//      MMcRunHeader container and tries to identifies it. If it is known
//      (eg. Crab) the fixed build in pixel numbers are used as blind
//      pixels.
//
//  If neither an array of pixels is given (or its size is 0) and
//  MMcRunHeader couldn't be found the task removes itself from the
//  tasklist.
//
//  Implemented star fields:
//   - Crab: 400, 401, 402, 437, 438, 439
//
//  Input Containers:
//   MCerPhotEvt
//   [MBlindPixels]
//
//  Output Containers:
//   MCerPhotEvt
//   MBlindPixels
//
/////////////////////////////////////////////////////////////////////////////
#include "MBlindPixelCalc.h"

#include <fstream>

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

#include "MParList.h"

#include "MGeomPix.h"
#include "MGeomCam.h"
#include "MCerPhotPix.h"
#include "MCerPhotEvt.h"
#include "MPedestalPix.h"
#include "MPedestalCam.h"
#include "MBlindPixels.h"

#include "MMcRunHeader.hxx"

ClassImp(MBlindPixelCalc);

using namespace std;

static const TString gsDefName  = "MBlindPixelCalc";
static const TString gsDefTitle = "Find hot spots (star, broken pixels, etc)";

// --------------------------------------------------------------------------
//
// Default constructor.
//
MBlindPixelCalc::MBlindPixelCalc(const char *name, const char *title)
    : fFlags(0)
{
    fName  = name  ? name  : gsDefName.Data();
    fTitle = title ? title : gsDefTitle.Data();
}

// --------------------------------------------------------------------------
//
//  - Try to find or create MBlindPixels in parameter list.
//  - get the MCerPhotEvt from the parlist (abort if missing)
//  - if no pixels are given by the user try to determin the starfield
//    from the monte carlo run header.
//
Int_t MBlindPixelCalc::PreProcess (MParList *pList)
{
    if (TESTBIT(fFlags, kUseBlindPixels))
        fPixels = (MBlindPixels*)pList->FindObject(AddSerialNumber("MBlindPixels"));
    else
        fPixels = (MBlindPixels*)pList->FindCreateObj(AddSerialNumber("MBlindPixels"));
    if (!fPixels)
	return kFALSE; 
    
    fEvt = (MCerPhotEvt*)pList->FindObject(AddSerialNumber("MCerPhotEvt"));
    if (!fEvt)
    {
        *fLog << err << dbginf << "MCerPhotEvt not found... aborting." << endl;
        return kFALSE;
    }
    fPed = (MPedestalCam*)pList->FindObject(AddSerialNumber("MPedestalCam"));
    if (!fEvt)
    {
        *fLog << err << dbginf << "MPedestalCam not found... aborting." << endl;
        return kFALSE;
    }

    fGeomCam = (MGeomCam*)pList->FindObject(AddSerialNumber("MGeomCam"));
    if (!fGeomCam)
        *fLog << warn << dbginf << "No camera geometry available... can't use interpolation." << endl;

    if (TESTBIT(fFlags, kUseBlindPixels))
        return kTRUE;

    const UShort_t size = fPixelsIdx.GetSize();

    if (size == 0)
    {
        if (!pList->FindObject("MMcRunHeader"))
        {
            *fLog << warn << "Warning - Neither blind pixels are given nor a MMcRunHeader was found... removing MBlindPixelCalc from list." << endl;
            return kSKIP;
        }
        return kTRUE;
    }

    // Set as blind pixels the global blind pixels, which are given
    // through the macros

    UShort_t numids = fPixelsIdx.GetSize();

    for(Int_t i = 0; i<numids; i++)
	fPixels->SetPixelBlind(fPixelsIdx[i]);

    return kTRUE;
}

// --------------------------------------------------------------------------
//
//  Replaces each pixel (signal, signal error, pedestal, pedestal rms)
//  by the average of its surrounding pixels.
//  If TESTBIT(fFlags, kUseCentralPixel) is set the central pixel is also
//  included.
//
//  NT: Informations about the interpolated pedestals are added. 
//      When the option Interpolated is called, the blind pixel with the new
//      values of signal and fluttuation is included in the calculation of
//      the Image Parameters.
//
void MBlindPixelCalc::Interpolate() const
{
    const UShort_t entries = fGeomCam->GetNumPixels();

    //
    // Create arrays
    //
    Double_t *nphot  = new Double_t[entries];
    Double_t *perr   = new Double_t[entries];
    Double_t *ped    = new Double_t[entries];
    Double_t *pedrms = new Double_t[entries];
 
    //
    // Loop over all pixels
    //
    for (UShort_t i=0; i<entries; i++)
    {
        //
        // Check whether pixel with idx i is blind
        //
        if (!fPixels->IsBlind(i))
            continue;

        //
        // Get a pointer to this pixel. If it is not yet existing
        // create a new entry for this pixel in MCerPhotEvt
        //
        const MCerPhotPix *pix = fEvt->GetPixById(i);
        if (!pix)
            pix = fEvt->AddPixel(i, 0, 0);

        //
        // Get the corresponding geometry and pedestal
        //
        const MGeomPix &gpix = (*fGeomCam)[i];
        const MPedestalPix &ppix = (*fPed)[i];

        // Do Not-Use-Central-Pixel
        const Bool_t nucp = !TESTBIT(fFlags, kUseCentralPixel);

        Int_t num = nucp ? 0 : 1;

        nphot[i]  = nucp ? 0 : pix->GetNumPhotons();
        ped[i]    = nucp ? 0 : ppix.GetPedestal();
        perr[i]   = nucp ? 0 : Sqr(pix->GetErrorPhot());
        pedrms[i] = nucp ? 0 : Sqr(ppix.GetPedestalRms());

        //
	// The values are rescaled to the small pixels area for the right comparison
        //
        const Double_t ratio = fGeomCam->GetPixRatio(i);

        nphot[i]  *= ratio;
	ped[i]    *= ratio;
        perr[i]   *= Sqr(ratio);
	pedrms[i] *= Sqr(pedrms[i]);

        //
        // Loop over all its neighbors
        //
        const Int_t n = gpix.GetNumNeighbors();
        for (int j=0; j<n; j++)
        {
            const UShort_t nidx = gpix.GetNeighbor(j);

            //
            // Do not use blind neighbors
            //
            if (fPixels->IsBlind(nidx))
                continue;

            //
            // Check whether the neighbor has a signal stored
            //
            const MCerPhotPix *evtpix = fEvt->GetPixById(nidx);
            if (!evtpix)
                continue;

            //
            // Get the geometry for the neighbor
            //
            const Double_t nratio = fGeomCam->GetPixRatio(nidx);
            MPedestalPix &nppix = (*fPed)[nidx];

            //
	    //The error is calculated as the quadratic sum of the errors
	    //
            ped[i]    += nratio*nppix.GetPedestal();
            nphot[i]  += nratio*evtpix->GetNumPhotons();
            perr[i]   += Sqr(nratio*evtpix->GetErrorPhot());
	    pedrms[i] += Sqr(nratio*nppix.GetPedestalRms());

            num++;
        }

        //
	// Now the mean is calculated and the values rescaled back to the pixel area
        //
	nphot[i] /= num*ratio;
        ped[i]   /= num*ratio;
        perr[i]   = TMath::Sqrt(perr[i]/num)*ratio;
        pedrms[i] = TMath::Sqrt(pedrms[i]/num)*ratio;

    }

    //
    // Now the new pixel values are calculated and can be replaced in
    // the corresponding containers
    //
    for (UShort_t i=0; i<entries; i++)
    {
        //
        // Do not use blind neighbors
        //
        if (!fPixels->IsBlind(i))
            continue;

        //
        // It must exist, we have created it in the loop before.
        //
        fEvt->GetPixById(i)->Set(nphot[i], perr[i]);
        (*fPed)[i].Set(ped[i], pedrms[i]);
    }

    //
    // Delete the intermediat arrays
    //
    delete nphot;
    delete perr;
    delete ped;
    delete pedrms;
}

// --------------------------------------------------------------------------
//
//  Removes all blind pixels from the analysis by setting their state
//  to unused.
//
void MBlindPixelCalc::Unmap() const
{
    const UShort_t entries = fEvt->GetNumPixels();

    //
    // remove the pixels in fPixelsIdx if they are set to be used,
    // (set them to 'unused' state)
    //
    for (UShort_t i=0; i<entries; i++)
    {
        MCerPhotPix &pix = (*fEvt)[i];

        if (fPixels->IsBlind(pix.GetPixId()))
            pix.SetPixelUnused();
    }
}

// --------------------------------------------------------------------------
//
// Treat the blind pixels
//
Int_t MBlindPixelCalc::Process()
{
    if (TESTBIT(fFlags, kUseInterpolation) && fGeomCam)
        Interpolate();
    else
        Unmap();

    return kTRUE;
}

// --------------------------------------------------------------------------
//
//  - Check whether pixels to disable are available. If pixels are
//    given by the user nothing more is done.
//  - Otherwise try to determin the blind pixels from the starfield
//    given in MMcRunHeader.
//
Bool_t MBlindPixelCalc::ReInit(MParList *pList)
{
    if (TESTBIT(fFlags, kUseBlindPixels))
        return kTRUE;

    //
    // If pixels are given by the user, we are already done
    //
    if (fPixelsIdx.GetSize() > 0)
        return kTRUE;

    //
    // Delete the old array holding the blind pixels for the last file
    //
    fPixels->Clear();

    if (!fGeomCam->InheritsFrom("MGeomCamMagic"))
    {
        *fLog << warn << "MBlindPixelCalc::ReInit: Warning - Starfield only implemented for Magic standard Camera... no action." << endl;
        return kTRUE;
    }

    //
    // Set as blind some particular pixels because of a particular
    // Star Field of View.
    //
    MMcRunHeader *mcrun = (MMcRunHeader*)pList->FindObject("MMcRunHeader");
    if (!mcrun)
    {
        *fLog << warn << "MBlindPixelCalc::ReInit: Warning - No run header available... no action." << endl;
        return kTRUE;
    }

    Int_t rah, ram, ras;
    Int_t ded, dem, des;
    mcrun->GetStarFieldRa(&rah, &ram, &ras);
    mcrun->GetStarFieldDec(&ded, &dem, &des);

    if (rah!=5 || ram!=34 || ras!=32 || ded!=22 || dem!=0 || des!=55)
    {
        *fLog << warn << "Warning - Starfield unknown..." << endl;
        return kTRUE;
    }

    //
    // Case for Crab Nebula FOV
    //
    fPixels->SetPixelBlind(400);
    fPixels->SetPixelBlind(401);
    fPixels->SetPixelBlind(402);
    fPixels->SetPixelBlind(437);
    fPixels->SetPixelBlind(438);
    fPixels->SetPixelBlind(439);

    *fLog << inf;
    *fLog << "FOV is centered at CRAB NEBULA: Setting 6 blind pixels" << endl;
    *fLog << "to avoid bias values of analysis due to CRAB NEBULA:" << endl;
    *fLog << "   Pixels: 400, 401, 402, 437, 438, 439" << endl;

    return kTRUE;
}

void MBlindPixelCalc::StreamPrimitive(ofstream &out) const
{
    out << "   MBlindPixelCalc " << GetUniqueName();
    if (fName!=gsDefName || fTitle!=gsDefTitle)
    {
        out << "(\"" << fName << "\"";
        if (fTitle!=gsDefTitle)
            out << ", \"" << fTitle << "\"";
        out <<")";
    }
    out << ";" << endl;

    if (TESTBIT(fFlags, kUseInterpolation))
        out << "   " << GetUniqueName() << ".SetUseInterpolation();" << endl;
    if (TESTBIT(fFlags, kUseCentralPixel))
        out << "   " << GetUniqueName() << ".SetUseCentralPixel();" << endl;

    if (fPixelsIdx.GetSize()==0)
        return;

    out << "   {" << endl;
    out << "      TArrayS dummy;" << endl;
    for (int i=0; i<fPixelsIdx.GetSize(); i++)
        out << "      dummy[" << i << "]=" << ((TArrayS)fPixelsIdx)[i] << ";" << endl;
    out << "      " << GetUniqueName() << ".SetPixels(dummy);" << endl;
    out << "   }" << endl;
}
