/* ======================================================================== *\
!
! *
! * 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, 12/2000 <mailto:tbretz@astro.uni-wuerzburg.de>
!   Author(s): Harald Kornmayer, 1/2001
!
!   Copyright: MAGIC Software Development, 2000-2003
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
//  MHillasCalc
//  ===========
//
//  This is a task to calculate the Hillas parameters from each event
//
//
//   Flags
//  --------
//
//  By default all flags are set:
//
//  To switch of the calculation you may use:
//   - Disable(MHillasCalc::kCalcHillas)
//   - Disable(MHillasCalc::kCalcHillasExt)
//   - Disable(MHillasCalc::kCalcNewImagePar)
//   - Disable(MHillasCalc::kCalcImagePar)
//   - Disable(MHillasCalc::kCalcSrcPosCam)
//   - Disable(MHillasCalc::kCalcConc)
//
//  If the calculation of MHillas is switched off a container MHillas
//  in the parameter list is nevertheless necessary for the calculation
//  of some other containers, see below.
//
//  If kCalcHillasSrc is set and no corresponding MSrcPosCam is found
//  in the parameter list an empty container (X=0, Y=0) is created.
//
//
//   Container names
//  -----------------
//
//  The names of the containers to be used can be set with:
//   - SetNameHillas("NewName")
//   - SetNameHillasExt("NewName")
//   - SetNameNewImgPar("NewName")
//   - SetNameImagePar("NewName")
//   - SetNameSrcPosCam("NewName")
//   - SetNameConc("NewName")
//   - SetNameHillasSrc("NewName")
//
//
//   Islands
//  ---------
//
//  You can change the islands for which the caluclations are done by:
//   - SetNumIsland()
//  The default is to use all used pixels (-1)
//
//  fIdxIslands effects the calculations:
//   - kCalcHillas
//   - kCalcHillasExt
//   - kCalcNewImgPar
//
//
//   Example
//  ---------
//
//   MHillasCalc calc0; // calculate all image parameters except source dep.
//   MHillasCalc calc1; // calculate source dependant image parameters for 'Source'
//   MHillasCalc calc2; // calculate source dependant image parameters for 'AntiSource'
//   MHillasCalc calc3; // calculate hillas parameters only for biggest island
//   MHillasCalc calc4; // calculate hillas parameter for 2nd biggest island
//   // setup names of input-/output-containers
//   calc1.SetNameSrcPosCam("Source");
//   calc2.SetNameSrcPosCam("AntiSource");
//   calc1.SetNameHillasSrc("MHillasSource");
//   calc2.SetNameHillasSrc("MHillasAntiSource");
//   calc3.SetNameHillas("MHillas0");
//   calc4.SetNameHillas("MHillas1");
//   // setup calculations to be done
//   calc0.Disable(MHillasCalc::kCalcHillasSrc);
//   calc1.SetFlags(MHillasCalc::kCalcHillasSrc);
//   calc2.SetFlags(MHillasCalc::kCalcHillasSrc);
//   calc3.SetFlags(MHillasCalc::kCalcHillas);
//   calc4.SetFlags(MHillasCalc::kCalcHillas);
//   // choode index of island
//   calc3.SetNumIsland(0);
//   calc4.SetNumIsland(1);
//
//   // setup tasklist
//   MTaskList list;
//   list.Add(&calc0);
//   list.Add(&calc1);
//   list.Add(&calc2);
//   list.Add(&calc3);
//   list.Add(&calc4);
//
//
//   Input/Output Containers
//  -------------------------
//
//    1) MGeomCam          5) MHillas         8) MImagePar
//    2) MCerPhotEvt       6) MHillasSrc      9) MNewImagePar
//    3) MSrcPosCam        7) MHillasExt     10) MConcentration
//    4) fIdxIslands
//
//     Flag           | Input Container | Output
//   -----------------+-----------------+--------
//    kCalcHillas     |  1  2     4     |    5
//    kCalcHillasSrc  |        3  4  5  |    6
//    kCalcHillasExt  |  1  2     4  5  |    7
//    kCalcImagePar   |     2           |    8
//    kCalcNewImgPar  |  1  2     4  5  |    9
//    kCalcConc       |  1  2        5  |   10
//   -----------------+-----------------+--------
//
/////////////////////////////////////////////////////////////////////////////
#include "MHillasCalc.h"

#include <fstream> // StreamPrimitive

#include "MParList.h"

#include "MCerPhotEvt.h"

#include "MHillas.h"
#include "MHillasExt.h"
#include "MHillasSrc.h"
#include "MImagePar.h"
#include "MNewImagePar.h"
#include "MConcentration.h"

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

ClassImp(MHillasCalc);

using namespace std;

const TString MHillasCalc::gsDefName         = "MHillasCalc";
const TString MHillasCalc::gsDefTitle        = "Calculate Hillas and other image parameters";

const TString MHillasCalc::gsNameHillas      = "MHillas";        // default name of the 'MHillas' container
const TString MHillasCalc::gsNameHillasExt   = "MHillasExt";     // default name of the 'MHillasExt' container
const TString MHillasCalc::gsNameNewImagePar = "MNewImagePar";   // default name of the 'MNewImagePar' container
const TString MHillasCalc::gsNameConc        = "MConcentration"; // default name of the 'MConcentration' container
const TString MHillasCalc::gsNameImagePar    = "MImagePar";      // default name of the 'MImagePar' container
const TString MHillasCalc::gsNameHillasSrc   = "MHillasSrc";     // default name of the 'MHillasSrc' container
const TString MHillasCalc::gsNameSrcPosCam   = "MSrcPosCam";     // default name of the 'MSrcPosCam' container

// --------------------------------------------------------------------------
//
// Default constructor.
//
MHillasCalc::MHillasCalc(const char *name, const char *title)
        : fNameHillas(gsNameHillas),       fNameHillasExt(gsNameHillasExt),
          fNameHillasSrc(gsNameHillasSrc), fNameSrcPosCam(gsNameSrcPosCam),
          fNameConc(gsNameConc),           fNameImagePar(gsNameImagePar),
          fNameNewImagePar(gsNameNewImagePar),
          fErrors(6), fFlags(0xff), fIdxIsland(-1)
{
    fName  = name  ? name  : gsDefName.Data();
    fTitle = title ? title : gsDefTitle.Data();
}

// --------------------------------------------------------------------------
//
// Check for in-/output containers, see class description
//
Int_t MHillasCalc::PreProcess(MParList *pList)
{
    if (TestFlags(~kCalcHillasSrc))
    {
        fCerPhotEvt = (MCerPhotEvt*)pList->FindObject(AddSerialNumber("MCerPhotEvt"));
        if (!fCerPhotEvt)
        {
            *fLog << err << "MCerPhotEvt not found... aborting." << endl;
            return kFALSE;
        }
    }

    if (TestFlags(kCalcHillas|kCalcHillasExt|kCalcNewImagePar|kCalcConc))
    {
        fGeomCam = (MGeomCam*)pList->FindObject(AddSerialNumber("MGeomCam"));
        if (!fGeomCam)
        {
            *fLog << err << "MGeomCam (Camera Geometry) missing in Parameter List... aborting." << endl;
            return kFALSE;
        }
    }

    // depend on whether MHillas is an in- or output container
    if (TestFlag(kCalcHillas))
    {
        fHillas = (MHillas*)pList->FindCreateObj("MHillas", AddSerialNumber(fNameHillas));
        if (!fHillas)
            return kFALSE;
    }

    if (TestFlags(kCalcHillasExt|kCalcNewImagePar|kCalcConc|kCalcHillasSrc))
    {
        fHillas = (MHillas*)pList->FindObject(AddSerialNumber(fNameHillas), "MHillas");
        if (!fHillas)
        {
            *fLog << err << fNameHillas << " [MHillas] not found... aborting." << endl;
            return kFALSE;
        }
    }

    // if enabled
    if (TestFlag(kCalcHillasExt))
    {
        fHillasExt = (MHillasExt*)pList->FindCreateObj("MHillasExt", AddSerialNumber(fNameHillasExt));
        if (!fHillasExt)
            return kFALSE;
    }

    // if enabled
    if (TestFlag(kCalcHillasSrc))
    {
        const MSrcPosCam *src = (MSrcPosCam*)pList->FindObject(AddSerialNumber(fNameSrcPosCam), "MSrcPosCam");
        if (!src)
        {
            *fLog << warn << AddSerialNumber(fNameSrcPosCam) << " [MSrcPosCam] not found... creating default container." << endl;
            src = (MSrcPosCam*)pList->FindCreateObj("MSrcPosCam", AddSerialNumber(fNameSrcPosCam));
        }
        if (!src)
            return kFALSE;

        fHillasSrc = (MHillasSrc*)pList->FindCreateObj("MHillasSrc", AddSerialNumber(fNameHillasSrc));
        if (!fHillasSrc)
            return kFALSE;

        fHillasSrc->SetSrcPos(src);
    }

    // if enabled
    if (TestFlag(kCalcNewImagePar))
    {
        fNewImgPar = (MNewImagePar*)pList->FindCreateObj("MNewImagePar", AddSerialNumber(fNameNewImagePar));
        if (!fNewImgPar)
            return kFALSE;
    }

    // if enabled
    if (TestFlag(kCalcImagePar))
    {
        fImagePar = (MImagePar*)pList->FindCreateObj("MImagePar", AddSerialNumber(fNameImagePar));
        if (!fImagePar)
            return kFALSE;
    }

    // if enabled
    if (TestFlag(kCalcConc))
    {
        fConc = (MConcentration*)pList->FindCreateObj("MConcentration", fNameConc);
        if (!fConc)
            return kFALSE;
    }

    fErrors.Reset();

    Print();

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// If you want do complex descisions inside the calculations
// we must move the calculation code inside this function
//
// If the calculation wasn't sucessfull skip this event
//
Int_t MHillasCalc::Process()
{
    if (TestFlag(kCalcHillas))
    {
        const Int_t rc = fHillas->Calc(*fGeomCam, *fCerPhotEvt, fIdxIsland);
        if (rc<0 || rc>4)
        {
            *fLog << err << dbginf << "MHillas::Calc returned unknown error code!" << endl;
            return kFALSE;
        }
        if (rc>0)
        {
            fErrors[rc]++;
            return kCONTINUE;
        }
    }

    if (TestFlag(kCalcHillasSrc))
    {
        const Int_t rc = fHillasSrc->Calc(*fHillas);
        if (rc<0 || rc>2)
        {
            *fLog << err << dbginf << "MHillasSrc::Calc returned unknown error code!" << endl;
            return kFALSE;
        }
        if (rc>0)
        {
            fErrors[rc+4]++;
            return kCONTINUE;
        }
    }
    fErrors[0]++;

    if (TestFlag(kCalcHillasExt))
        fHillasExt->Calc(*fGeomCam, *fCerPhotEvt, *fHillas, fIdxIsland);

    if (TestFlag(kCalcImagePar))
        fImagePar->Calc(*fCerPhotEvt);

    if (TestFlag(kCalcNewImagePar))
        fNewImgPar->Calc(*fGeomCam, *fCerPhotEvt, *fHillas, fIdxIsland);

    if (TestFlag(kCalcConc))
        fConc->Calc(*fGeomCam, *fCerPhotEvt, *fHillas);

    return kTRUE;
}

// --------------------------------------------------------------------------
//
//  This is used to print the output in the PostProcess. Later (having
//  millions of events) we may want to improve the output.
//
void MHillasCalc::PrintSkipped(int i, const char *str) const
{
    *fLog << " " << setw(7) << fErrors[i] << " (";
    *fLog << setw(3) << (int)(100.*fErrors[i]/GetNumExecutions());
    *fLog << "%) Evts skipped: " << str << endl;
}

// --------------------------------------------------------------------------
//
//  Prints some statistics about the hillas calculation. The percentage
//  is calculated with respect to the number of executions of this task.
//
Int_t MHillasCalc::PostProcess()
{
    if (GetNumExecutions()==0)
        return kTRUE;

    if (!TestBit(kCalcHillas) && !TestBit(kCalcHillasSrc))
        return kTRUE;

    *fLog << inf << endl;
    *fLog << GetDescriptor() << " execution statistics:" << endl;
    *fLog << dec << setfill(' ');
    if (TestBit(kCalcHillas))
    {
        PrintSkipped(1, "Less than 3 pixels (before cleaning)");
        PrintSkipped(2, "Calculated Size == 0 (after cleaning)");
        PrintSkipped(3, "Number of used pixels < 3");
        PrintSkipped(4, "CorrXY==0");
    }
    if (TestBit(kCalcHillasSrc))
    {
        PrintSkipped(5, "Dist==0");
        PrintSkipped(6, "Arg2==0");
    }
    *fLog << " " << (int)fErrors[0] << " (" << (int)(100.*fErrors[0]/GetNumExecutions()) << "%) Evts survived Hillas calculation!" << endl;
    *fLog << endl;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Print 'Dataflow'
//
void MHillasCalc::Print(Option_t *o) const
{
    *fLog << inf << GetDescriptor() << " calculating:" << endl;
    if (TestFlag(kCalcHillas))
        *fLog << " - " << fNameHillas << " from MGeomCam, MCerPhotEvt and fIdxIsland=" << fIdxIsland << endl;
    if (TestFlag(kCalcHillasSrc))
        *fLog << " - " << fNameHillasSrc << " from " << fNameSrcPosCam << ", " << fNameHillas << " and fIdxIsland=" << fIdxIsland << endl;
    if (TestFlag(kCalcHillasExt))
        *fLog << " - " << fNameHillasExt << " from MGeomCam, MCerPhotEvt, " << fNameHillas << " and fIdxIsland=" << fIdxIsland << endl;
    if (TestFlag(kCalcImagePar))
        *fLog << " - " << fNameImagePar << " from MCerPhotEvt" << endl;
    if (TestFlag(kCalcNewImagePar))
        *fLog << " - " << fNameNewImagePar << " from MGeomCam, MCerPhotEvt, " << fNameHillas << " and fIdxIsland=" << fIdxIsland << endl;
    if (TestFlag(kCalcConc))
        *fLog << " - " << fNameConc << " from MGeomCam, MCerPhotEvt and " << fNameHillas << endl;
}

// --------------------------------------------------------------------------
//
// Implementation of SavePrimitive. Used to write the call to a constructor
// to a macro. In the original root implementation it is used to write
// gui elements to a macro-file.
//
void MHillasCalc::StreamPrimitive(ofstream &out) const
{
    out << "   MHillasCalc " << GetUniqueName() << "(";
    if (fName!=gsDefName || fTitle!=gsDefTitle)
    {
        out << ", \"" << fName << "\"";
        if (fTitle!=gsDefTitle)
            out << ", \"" << fTitle << "\"";
    }
    out << ");" << endl;

    if (TestFlags(kCalcHillasExt|kCalcNewImagePar|kCalcConc|kCalcHillasSrc))
    {
        if (fNameHillas!=gsNameHillas)
            out << "   " << GetUniqueName() << ".SetNameHillas(\"" << fNameHillas << "\");" << endl;
    }
    if (TestFlag(kCalcHillasSrc) && fNameHillasSrc!=gsNameHillasSrc)
    {
        out << "   " << GetUniqueName() << ".SetNameHillasSrc(\"" << fNameHillasSrc << "\");" << endl;
        out << "   " << GetUniqueName() << ".SetNameSrcPosCam(\"" << fNameSrcPosCam << "\");" << endl;
    }
    if (TestFlag(kCalcHillasExt) && fNameHillasExt!=gsNameHillasExt)
        out << "   " << GetUniqueName() << ".SetNameHillasExt(\"" << fNameHillasExt << "\");" << endl;
    if (TestFlag(kCalcConc) && fNameConc!=gsNameConc)
        out << "   " << GetUniqueName() << ".SetNameConc(\"" << fNameConc << "\");" << endl;
    if (TestFlag(kCalcImagePar) && fNameImagePar!=gsNameImagePar)
        out << "   " << GetUniqueName() << ".SetNameImagePar(\"" << fNameImagePar << "\");" << endl;
    if (TestFlag(kCalcNewImagePar) && fNameNewImagePar!=gsNameNewImagePar)
        out << "   " << GetUniqueName() << ".SetNameNewImagePar(\"" << fNameNewImagePar << "\");" << endl;

    if (!TestFlag(kCalcHillas))
        out << "   " << GetUniqueName() << ".Disable(MHilllasCalc::kCalcHillas);" << endl;
    if (!TestFlag(kCalcHillasExt))
        out << "   " << GetUniqueName() << ".Disable(MHilllasCalc::kCalcHillasExt);" << endl;
    if (!TestFlag(kCalcHillasSrc))
        out << "   " << GetUniqueName() << ".Disable(MHilllasCalc::kCalcHillasSrc);" << endl;
    if (!TestFlag(kCalcNewImagePar))
        out << "   " << GetUniqueName() << ".Disable(MHilllasCalc::kCalcNewImagePar);" << endl;
    if (!TestFlag(kCalcConc))
        out << "   " << GetUniqueName() << ".Disable(MHilllasCalc::kCalcConc);" << endl;
    if (!TestFlag(kCalcImagePar))
        out << "   " << GetUniqueName() << ".Disable(MHilllasCalc::kCalcImagePar);" << endl;

    if (fIdxIsland>=0)
        out << "   " << GetUniqueName() << ".SetNumIsland(" << fIdxIsland << ");" << endl;
}

