/* ======================================================================== *\
!
! *
! * 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  07/2001 <mailto:tbretz@uni-sw.gwdg.de>
!
!   Copyright: MAGIC Software Development, 2000-2001
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
//   MFilterList                                                           //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

#include "MFilterList.h"

#include <TString.h>

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

ClassImp(MFilterList);

// --------------------------------------------------------------------------
//
//   Constructor.
//
//   Specify the boolean operation which is used to evaluate the
//   result of this list. If no operation is specified "land" is
//   used.
//
//   Options:
//      and, &   : is a bitwise and
//      or, |    : is a bitwise or
//      xor, ^   : is a bitwise exclusive or
//      land, && : is a logical and
//      lor, ||  : is a logical or
//
MFilterList::MFilterList(const char *type)
{
    fFilterType = kEAnd;

    TString str(type);

    if (str.CompareTo("OR", TString::kIgnoreCase)  || str.CompareTo("|"))
        fFilterType = kEOr;
    if (str.CompareTo("XOR", TString::kIgnoreCase) || str.CompareTo("^"))
        fFilterType = kEXor;
    if (str.CompareTo("LAND", TString::kIgnoreCase) || str.CompareTo("&&"))
        fFilterType = kELAnd;
    if (str.CompareTo("LOR", TString::kIgnoreCase) || str.CompareTo("||"))
        fFilterType = kELOr;
}

// --------------------------------------------------------------------------
//
//   CopyConstructor
//
MFilterList::MFilterList(MFilterList &ts)
{
    fFilters.AddAll(&ts.fFilters);
    fFilterType = ts.fFilterType;
}

// --------------------------------------------------------------------------
//
//  Evaluates and returns the result of the filter list.
//  The expression is evaluated step by step, eg:
//  ((filter[0] # filter[1]) # filter[3]) # filter[4])
//  The '#' stands for the boolean operation which is specified in
//  the constructor.
//
Bool_t MFilterList::IsExpressionTrue() const
{
    TIter Next(&fFilters);

    MFilter *filter=(MFilter*)Next();

    if (!filter)
        return kTRUE;

    Bool_t rc = filter->IsExpressionTrue();

    //
    // loop over all filters
    //
    switch (fFilterType)
    {
    case kEAnd:
        while ((filter=(MFilter*)Next()))
            rc &= filter->IsExpressionTrue();
        break;

    case kEOr:
        while ((filter=(MFilter*)Next()))
            rc |= filter->IsExpressionTrue();
        break;

    case kEXor:
        while ((filter=(MFilter*)Next()))
            rc ^= filter->IsExpressionTrue();
        break;

    case kELAnd:
        while ((filter=(MFilter*)Next()))
            rc = (rc && filter->IsExpressionTrue());
        break;

    case kELOr:
        while ((filter=(MFilter*)Next()))
            rc = (rc || filter->IsExpressionTrue());
        break;
    }
    return rc;
}

// --------------------------------------------------------------------------
//
// If you want to add a new filter to the list call this function with the
// pointer to the filter to be added. 
//
Bool_t MFilterList::AddToList(MFilter *filter)
{
    if (!filter)
        return kTRUE;

    const char *name = filter->GetName();

    // FIXME: We agreed to put the task into list in an ordered way.

    if (fFilters.FindObject(filter))
    {
        *fLog << warn << dbginf << "Filter already existing... skipped." << endl;
        return kTRUE;
    }

    if (fFilters.FindObject(name))
    {
        *fLog << warn << dbginf << "'" << name << "' exists in List already... skipped." << endl;
        return kTRUE;
    }

    *fLog << inf << "Adding " << name << " to " << GetName() << "... " << flush;

    fFilters.Add(filter);

    *fLog << "Done." << endl;

    return kTRUE;
}


// --------------------------------------------------------------------------
//
// PreProcesses all filters in the list
//
Bool_t MFilterList::PreProcess(MParList *pList)
{
    TIter Next(&fFilters);

    MFilter *filter=NULL;

    //
    // loop over all filters
    //
    while ((filter=(MFilter*)Next()))
        if (!filter->PreProcess(pList))
            return kFALSE;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Processes (updates) all filters in the list.
//
Bool_t MFilterList::Process()
{
    TIter Next(&fFilters);

    MFilter *filter=NULL;

    //
    // loop over all filters
    //
    while ((filter=(MFilter*)Next()))
        if (!filter->Process())
            return kFALSE;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// PostProcesses all filters in the list.
//
Bool_t MFilterList::PostProcess()
{
    TIter Next(&fFilters);

    MFilter *filter=NULL;

    //
    // loop over all filters
    //
    while ((filter=(MFilter*)Next()))
        if (!filter->PostProcess())
            return kFALSE;

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// If you want to use a verbose output ("and") instead of a symbolic ("&")
// one the option string must conatin a "v"
//
void MFilterList::Print(Option_t *opt) const
{
    TString str(opt);
    const Bool_t verbose = str.Contains("V", TString::kIgnoreCase);

    *fLog << all << "(";

    TIter Next(&fFilters);

    MFilter *filter=(MFilter*)Next();

    //
    // loop over all filters
    //
    if (!filter)
    {
        *fLog << "<empty>)" << flush;
        return;
    }

    do
    {
        switch (fFilterType)
        {
        case kEAnd:
            *fLog << (verbose?" and ":"&");
            break;

        case kEOr:
            *fLog << (verbose?" or ":"|");
            break;

        case kEXor:
            *fLog << (verbose?" xor ":"^");
            break;

        case kELAnd:
            *fLog << (verbose?" land ":"&&");
            break;

        case kELOr:
            *fLog << (verbose?" lor ":"||");
            break;
        }

        filter->Print();
    } while ((filter=(MFilter*)Next()));

    *fLog << ")" << flush;
}
