/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MatchingCriteria.h"
#include "ObjectList.h"
#include "BufrDecoder.h"
#include "MatchingInfo.h"

MatchingCriteria::MatchingCriteria(const MvRequest& viewReq) :
    matchingRequest_("MATCHING_REQUEST")
{
    // Build a matching criteria based on the information provided
    // by a view request, subrequest OVERLAY_CONTROL
    // Go through the request, extract all param which begin
    // with the words "OVERLAY_" and insert them in the matching request
    MvRequest overlayReq = viewReq.getSubrequest("OVERLAY_CONTROL");
    if (overlayReq == nullptr)
        overlayReq = ObjectList::CreateDefaultRequest("OVERLAY_CONTROL");

    overlayReq = ObjectList::ExpandRequest(overlayReq, EXPAND_DEFAULTS);
    int count = overlayReq.countParameters();
    for (int i = 0; i < count; i++) {
        const char* param = overlayReq.getParameter(i);
        if (strncmp(param, "OVERLAY_", 8) == 0) {
            int n = overlayReq.countValues(param);
            for (int j = 0; j < n; j++) {
                const char* value = nullptr;
                overlayReq.getValue(value, param, j);
                matchingRequest_.addValue(param + 8, value);
            }
        }
    }

    //-- Observation grouping parameters --//

    //-- grouping time can be taken from metadata (section1,msg) or data (section4,obs)
    const char* timeFrom = overlayReq("OBS_GROUPING_TIME_FROM");
    if (timeFrom)
        ObsGroupControl::setUseObsTime(strcmp(timeFrom, "DATA") == 0);

    int obsGroup = (int)overlayReq("OBS_GROUPING_PERIOD");
    if (obsGroup)
        ObsGroupControl::setGroupingPeriod(obsGroup);

    if ((const char*)overlayReq("OBS_GROUPING_PERIOD_START_MIN")) {
        int obsGroupStart = (int)overlayReq("OBS_GROUPING_PERIOD_START_MIN");
        ObsGroupControl::setGroupingPeriodStart(obsGroupStart);
    }
}

MatchingCriteria::~MatchingCriteria() = default;

// Match
// Compare two matching informations.
// In case the two input arguments are considered a pair, also checks if
// they are given in the correct order, i.e., the first input argument is
// expected to be the "first" companion (e.g. pair u/v the u is the first
// companion)
bool MatchingCriteria::IsPair(const MatchingInfo& arg1, const MatchingInfo& arg2, bool& swap) const
{
    MvRequest req1 = arg1.matchingRequest_;
    MvRequest req2 = arg2.matchingRequest_;
    std::string companionExpected = (const char*)req1("MY_COMPANION");
    std::string parameter = (const char*)req2("PARAM");

    // The elements are companions
    // Check if they are given in the correct order
    if (companionExpected == parameter) {
        if ((const char*)req1("FIRST_COMPANION") &&
            (int)req1("FIRST_COMPANION") == 1)
            swap = false;
        else
            swap = true;

        return true;
    }

    return false;
}

bool MatchingCriteria::Match(const MatchingInfo& lhs, const MatchingInfo& rhs) const
{
    // Check Overlay_Mode
    Cached mode = matchingRequest_("MODE", 0);
    if (mode == Cached("ALWAYS_OVERLAY"))
        return true;
    else if (mode == Cached("NEVER_OVERLAY"))
        return false;

    int count = matchingRequest_.countParameters();
    Cached ON = "ON";
    bool flagOn = false;
    for (int i = 0; i < count; i++) {
        Cached param = matchingRequest_.getParameter(i);

        if (matchingRequest_(param) == ON) {
            // This approach does not take into account date-time tolerance
            // and vector fields
            flagOn = true;
            Cached lhsValue = lhs.matchingRequest_(param, 0);
            Cached rhsValue = rhs.matchingRequest_(param, 0);

            if (!lhsValue || !rhsValue) {
                // What shall we do?
                //(breaks XSect!) return false; //F05-07-2001, implemented 010709/vk
            }
            else if (lhsValue != rhsValue) {
                return false;
            }
        }
    }

    if (flagOn)
        return true;
    else
        return false;  // Never overlay
}

#if 0

MatchingInfo 
MatchingCriteria::merge(const MatchingInfo& lhs,const MatchingInfo& rhs)
{
	
}

bool 
MapView::Match( MatchingInfo& dataInfo,
	        MatchingInfo& nodeInfo )
{
	bool    levelMatch = false, 
		timeMatch  = false, 
		paramMatch = false;

	if ( levelChecking_ == true )
		levelMatch = LevelMatching (dataInfo.Level(), nodeInfo.Level());

	if ( timeChecking_ == true )
		timeMatch = TimeMatching (dataInfo.Date(), nodeInfo.Date());
	if ( paramChecking_ == true )
		paramMatch = ParamMatching (dataInfo.Param(), nodeInfo.Param());
	
	return (timeMatch || paramMatch || levelMatch);

}

bool
MapView::TimeMatching(const MvDate& dataDate, const MvDate& nodeDate)
{
	if (dataDate != nodeDate)
		return false;

	if ((dataDate - timeTolerance_) > nodeDate || 
	    (dataDate + timeTolerance_) < nodeDate)
		return false;

	return true;
}

bool
MapView::LevelMatching(const int dataLevel, const int nodeLevel)
{

	// Compares level according to some criteria
	return (dataLevel == nodeLevel ?  true : false); 
}

bool
MapView::ParamMatching(const int  dataParam, const int nodeParam)
{

	// Compares the two parameters - are they equal ?
	if (dataParam == nodeParam)
		return true;

	// Use the GribTable2 object

	// If the parameters are not equal,
	// they may be a pair of components of a vector field
	
	// First, convert parameter number from long to char
	char strParamNum[10];
	sprintf(strParamNum, "%ld", dataParam); 

	// Then, retrieve from PlotModTable the parameter number
	// corresponding to the companion 
	// That is U/V, V/D, etc.
	const char* value = MvRequest::EnquireTable 
		            ("vector_field", strParamNum, "companion_number");

	if ((value != 0) && atol(value) == nodeParam)  // found other vector field
		return true;
			
	return false;

}
#endif
