#include "SearchInHeader.hh"
//////////////////////////////////////////////////////////
SearchInHeader::
SearchInHeader( bool flag )
{
    Initialize(flag);
}
//////////////////////////////////////////////////////////
SearchInHeader::
SearchInHeader( ElementContainerMatrix *target )
{
    Initialize();
    SetTarget( target );
}//////////////////////////////////////////////////////////
SearchInHeader::
SearchInHeader( ElementContainerArray *target )
{
    Initialize();
    SetTarget( target );
}
//////////////////////////////////////////////////////////
SearchInHeader::
~SearchInHeader()
{
}

//////////////////////////////////////////////////////////
void SearchInHeader::
Initialize(bool flag)
{
    isDebug = flag;

    CommentHead = "SearchInHeader >> ";

    _ResultIndex.clear();
    _TargetECM = NULL;
    _TargetECA = NULL;
    isSearchArray = false;
}

//////////////////////////////////////////////////////////
void SearchInHeader::
SetTarget( ElementContainerMatrix *target ){
    _TargetECM = target;
    _TargetECA = NULL;
    _ResultIndex.clear();
}
//////////////////////////////////////////////////////////
void SearchInHeader::
SetTarget( ElementContainerArray *target ){
    _TargetECM = NULL;
    _TargetECA = target;
    _ResultIndex.clear();
}

//////////////////////////////////////////////////////////
std::vector<UInt4> SearchInHeader::
_Search( ElementContainerArray *eca, std::string key, Int4 val ){
    std::vector<UInt4> ret;
    ret.clear();
    UInt4 num_of_pixel = eca->PutSize();
    for (UInt4 i=0; i<num_of_pixel; i++){
        HeaderBase *H = eca->PutPointer(i)->PutHeaderPointer();
        if ( (H->CheckKey(key))!=0 ){
            if ( val == H->PutInt4(key) ){
                ret.push_back( i );
            }
        }
    }
    return ret;
}
//////////////////////////////////////////////////////////
std::vector<UInt4> SearchInHeader::
_Search( ElementContainerArray *eca, std::string key, Int4 val_lower, Int4 val_upper ){
    std::vector<UInt4> ret;
    ret.clear();

    UInt4 num_of_pixel = eca->PutSize();
    for (UInt4 pixel=0;pixel<num_of_pixel;pixel++){
        HeaderBase* H = eca->PutPointer(pixel)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            Int4 val = H->PutInt4(key);
            if ((val >= val_lower)&&(val <= val_upper)){
                ret.push_back(pixel);
            }
        }
    }
    return ret;
}
//////////////////////////////////////////////////////////
std::vector<UInt4> SearchInHeader::
_Search( ElementContainerArray *eca, std::string key, double val_lower, double val_upper ){
    std::vector<UInt4> ret;
    ret.clear();

    UInt4 num_of_pixel = eca->PutSize();
    for (UInt4 pixel=0;pixel<num_of_pixel;pixel++){
        HeaderBase* H = eca->PutPointer(pixel)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            double val = H->PutDouble(key);
            if ((val >= val_lower)&&(val <= val_upper)){
                ret.push_back(pixel);
            }
        }
    }
    return ret;
}
//////////////////////////////////////////////////////////
std::vector<UInt4> SearchInHeader::
_Search( ElementContainerArray *eca, std::string key, std::string val, bool isStrict ){
    std::vector<UInt4> ret;
    ret.clear();

    UInt4 num_of_pixel = eca->PutSize();
    for (UInt4 pixel=0;pixel<num_of_pixel;pixel++){
        HeaderBase* H = eca->PutPointer(pixel)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            std::string val_h = H->PutString(key);
            if (isStrict){
                if ( val==val_h )
                    ret.push_back(pixel);
            }else{
                std::string::size_type index = val_h.find( val );
                if ( index != std::string::npos )
                    ret.push_back(pixel);
            }
        }
    }
    return ret;
}
//////////////////////////////////////////////////////////
bool SearchInHeader::
Search( std::string key, Int4 val ){

    isSearchArray=false;
    _ResultIndex.clear();

    if (_TargetECA!=NULL){
        std::vector< UInt4 > ret_ec = _Search( _TargetECA, key, val );
        for (UInt4 i=0; i<ret_ec.size(); i++){
            std::vector<UInt4> tmp(1,ret_ec[i]);
            _ResultIndex.push_back( tmp );
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else if (_TargetECM!=NULL){
        UInt4 num_of_eca = _TargetECM->PutSize();

        std::vector< std::vector< std::vector<UInt4> > > mul_ret;
#ifdef MULTH
        UInt4 numth = MULTH;
        omp_set_num_threads(MULTH);
#else
        UInt4 numth = 1;
#endif
        mul_ret.resize(numth);
        for (UInt4 i=0; i<numth; i++){
            mul_ret[i].clear();
        }

#pragma omp parallel for
        for (Int4 i=0; i<(Int4)num_of_eca; i++){
#ifdef MULTH
            UInt4 ThNum = omp_get_thread_num();
#else
            UInt4 ThNum = 0;
#endif
            ElementContainerArray *eca = _TargetECM->PutPointer(i);
            std::vector< UInt4 > ret_ec = _Search( eca, key, val );
            for (UInt4 j=0; j<ret_ec.size(); j++){
                std::vector< UInt4 > tmp( 2, i );
                tmp[1] = ret_ec[j];
                mul_ret[ThNum].push_back( tmp );
            }
        }


        for (UInt4 i=0; i<numth; i++){
            for (UInt4 j=0; j<mul_ret[i].size(); j++){
                std::vector< UInt4 > tmp(2,0);
                tmp[0] = mul_ret[i][j][0];
                tmp[1] = mul_ret[i][j][1];
                _ResultIndex.push_back(tmp);
            }
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else{
        std::cout << "No Target Data" << std::endl;
    }
    return false;
}
//////////////////////////////////////////////////////////
bool SearchInHeader::
Search( std::string key, Int4 val_lower, Int4 val_upper ){

    isSearchArray=false;
    _ResultIndex.clear();

    if (_TargetECA!=NULL){
        std::vector< UInt4 > ret_ec = _Search( _TargetECA, key, val_lower, val_upper );
        for (UInt4 i=0; i<ret_ec.size(); i++){
            std::vector<UInt4> tmp(1,ret_ec[i]);
            _ResultIndex.push_back( tmp );
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else if (_TargetECM!=NULL){
        UInt4 num_of_eca = _TargetECM->PutSize();

        std::vector< std::vector< std::vector<UInt4> > > mul_ret;
#ifdef MULTH
        UInt4 numth = MULTH;
        omp_set_num_threads(MULTH);
#else
        UInt4 numth = 1;
#endif
        mul_ret.resize(numth);
        for (UInt4 i=0; i<numth; i++){
            mul_ret[i].clear();
        }

#pragma omp parallel for
        for (Int4 i=0; i<(Int4)num_of_eca; i++){
#ifdef MULTH
            UInt4 ThNum = omp_get_thread_num();
#else
            UInt4 ThNum = 0;
#endif
            ElementContainerArray *eca = _TargetECM->PutPointer(i);
            std::vector< UInt4 > ret_ec = _Search( eca, key, val_lower, val_upper );
            for (UInt4 j=0; j<ret_ec.size(); j++){
                std::vector< UInt4 > tmp( 2, i );
                tmp[1] = ret_ec[j];
                mul_ret[ThNum].push_back( tmp );
            }
        }

        for (UInt4 i=0; i<numth; i++){
            for (UInt4 j=0; j<mul_ret[i].size(); j++){
                std::vector< UInt4 > tmp(2,0);
                tmp[0] = mul_ret[i][j][0];
                tmp[1] = mul_ret[i][j][1];
                _ResultIndex.push_back(tmp);
            }
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else{
        std::cout << "No Target Data" << std::endl;
    }
    return false;
}
//////////////////////////////////////////////////////////
bool SearchInHeader::
Search( std::string key, double val_lower, double val_upper ){

    isSearchArray=false;
    _ResultIndex.clear();

    if (_TargetECA!=NULL){
    std::vector< UInt4 > ret_ec = _Search( _TargetECA, key, val_lower, val_upper );
    for (UInt4 i=0; i<ret_ec.size(); i++){
        std::vector<UInt4> tmp(1,ret_ec[i]);
        _ResultIndex.push_back( tmp );
    }
    if (_ResultIndex.empty()) return false;
    return true;
    }else if (_TargetECM!=NULL){
        UInt4 num_of_eca = _TargetECM->PutSize();

        std::vector< std::vector< std::vector<UInt4> > > mul_ret;
#ifdef MULTH
        UInt4 numth = MULTH;
        omp_set_num_threads(MULTH);
#else
        UInt4 numth = 1;
#endif
        mul_ret.resize(numth);
        for (UInt4 i=0; i<numth; i++){
            mul_ret[i].clear();
        }

#pragma omp parallel for
        for (Int4 i=0; i<(Int4)num_of_eca; i++){
#ifdef MULTH
            UInt4 ThNum = omp_get_thread_num();
#else
            UInt4 ThNum = 0;
#endif
            ElementContainerArray *eca = _TargetECM->PutPointer(i);
            std::vector< UInt4 > ret_ec = _Search( eca, key, val_lower, val_upper );
            for (UInt4 j=0; j<ret_ec.size(); j++){
                std::vector< UInt4 > tmp( 2, i );
                tmp[1] = ret_ec[j];
                mul_ret[ThNum].push_back( tmp );
            }
        }


        for (UInt4 i=0; i<numth; i++){
            for (UInt4 j=0; j<mul_ret[i].size(); j++){
                std::vector< UInt4 > tmp(2,0);
                tmp[0] = mul_ret[i][j][0];
                tmp[1] = mul_ret[i][j][1];
                _ResultIndex.push_back(tmp);
            }
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else{
        std::cout << "No Target Data" << std::endl;
    }
    return false;
}
//////////////////////////////////////////////////////////
bool SearchInHeader::
Search( std::string key, std::string val, bool isStrict ){
    _ResultIndex.clear();
    isSearchArray=false;

    if (_TargetECA!=NULL){
        std::vector< UInt4 > ret_ec = _Search( _TargetECA, key, val, isStrict );
        for (UInt4 i=0; i<ret_ec.size(); i++){
            std::vector<UInt4> tmp(1,ret_ec[i]);
            _ResultIndex.push_back( tmp );
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else if (_TargetECM!=NULL){
        UInt4 num_of_eca = _TargetECM->PutSize();

        std::vector< std::vector< std::vector<UInt4> > > mul_ret;
#ifdef MULTH
        UInt4 numth = MULTH;
        omp_set_num_threads(MULTH);
#else
        UInt4 numth = 1;
#endif
        mul_ret.resize(numth);
        for (UInt4 i=0; i<numth; i++){
            mul_ret[i].clear();
        }

#pragma omp parallel for
        for (Int4 i=0; i<(Int4)num_of_eca; i++){
#ifdef MULTH
            UInt4 ThNum = omp_get_thread_num();
#else
            UInt4 ThNum = 0;
#endif
            ElementContainerArray *eca = _TargetECM->PutPointer(i);
            std::vector< UInt4 > ret_ec = _Search( eca, key, val, isStrict );
            for (UInt4 j=0; j<ret_ec.size(); j++){
                std::vector< UInt4 > tmp( 2, i );
                tmp[1] = ret_ec[j];
                mul_ret[ThNum].push_back( tmp );
            }
        }

        for (UInt4 i=0; i<numth; i++){
            for (UInt4 j=0; j<mul_ret[i].size(); j++){
                std::vector< UInt4 > tmp(2,0);
                tmp[0] = mul_ret[i][j][0];
                tmp[1] = mul_ret[i][j][1];
                _ResultIndex.push_back(tmp);
            }
        }
        if (_ResultIndex.empty()) return false;
        return true;
    }else{
        std::cout << "No Target Data" << std::endl;
    }
    return false;
}

//////////////////////////////////////////////////////////
bool SearchInHeader::
SearchArray( std::string key, Int4 val )
{
    if (_TargetECM==NULL) return false;

    isSearchArray=true;
    _ResultIndex.clear();

    UInt4 num_of_det = _TargetECM->PutSize();
    for (UInt4 i=0; i<num_of_det; i++){
        HeaderBase* H = _TargetECM->PutPointer(i)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            if (val == H->PutInt4(key)){
                std::vector<UInt4> tmp(1,i);
                _ResultIndex.push_back( tmp );
            }
        }
    }
    if (_ResultIndex.empty()) return false;
    return true;
}

//////////////////////////////////////////////////////////
bool SearchInHeader::
SearchArray( std::string key, Int4 val_lower, Int4 val_upper )
{
    if (_TargetECM==NULL) return false;

    isSearchArray=true;
    _ResultIndex.clear();

    UInt4 num_of_det = _TargetECM->PutSize();
    for (UInt4 i=0; i<num_of_det; i++){
        HeaderBase* H = _TargetECM->PutPointer(i)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            Int4 val = H->PutInt4(key);
            if ((val>=val_lower)&&(val<=val_upper)){
                std::vector<UInt4> tmp(1,i);
                _ResultIndex.push_back( tmp );
            }
        }
    }
    if (_ResultIndex.empty()) return false;
    return true;
}

//////////////////////////////////////////////////////////
bool SearchInHeader::
SearchArray( std::string key, Double val_lower, Double val_upper )
{
    if (_TargetECM==NULL) return false;

    isSearchArray=true;
    _ResultIndex.clear();

    UInt4 num_of_det = _TargetECM->PutSize();
    for (UInt4 i=0; i<num_of_det; i++){
        HeaderBase* H = _TargetECM->PutPointer(i)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            Double val = H->PutDouble(key);
            if ((val>=val_lower)&&(val<=val_upper)){
                std::vector<UInt4> tmp(1,i);
                _ResultIndex.push_back( tmp );
            }
        }
    }
    if (_ResultIndex.empty()) return false;
    return true;
}

//////////////////////////////////////////////////////////
bool SearchInHeader::
SearchArray( std::string key, std::string str_val, bool isStrict )
{
    if (_TargetECM==NULL) return false;

    isSearchArray=true;
    _ResultIndex.clear();

    UInt4 num_of_det = _TargetECM->PutSize();
    for (UInt4 i=0; i<num_of_det; i++){
        HeaderBase* H = _TargetECM->PutPointer(i)->PutHeaderPointer();
        if ((H->CheckKey(key))!=0){
            std::string val = H->PutString(key);
            if (isStrict){
                if (str_val == val){
                    std::vector<UInt4> tmp(1,i);
                    _ResultIndex.push_back( tmp );
                }
            }else{
                std::string::size_type index = val.find( str_val );
                if ( index != std::string::npos ){
                    std::vector<UInt4> tmp(1,i);
                    _ResultIndex.push_back( tmp );
                }
            }
        }
    }
    if (_ResultIndex.empty()) return false;
    return true;
}

//////////////////////////////////////////////////////////
ElementContainerArray SearchInHeader::
PutResultAsArray()
{
    ElementContainerArray ret;
    if (_ResultIndex.empty()){
        std::cout << CommentHead + "Result is empty. " << std::endl;
        return ret;
    }
    if (isSearchArray){
        std::cout << CommentHead + "Latest search was for Array. " << std::endl;
        return ret;
    }

    if (_TargetECM!=NULL){
        for (UInt4 i=0; i<_ResultIndex.size(); i++)
            ret.Add( _TargetECM->PutPointer( _ResultIndex[i][0] )->Put( _ResultIndex[i][1] ) );
    }else if (_TargetECA!=NULL){
        for (UInt4 i=0; i<_ResultIndex.size(); i++)
            ret.Add( _TargetECA->Put( _ResultIndex[i][0] ) );
    }else{
        std::cout << CommentHead + "Target is empty. " << std::endl;
    }

    return ret;
}
//////////////////////////////////////////////////////////
ElementContainerMatrix SearchInHeader::
PutResultSearchArray()
{
    ElementContainerMatrix ret;
    if (_ResultIndex.empty()){
        std::cout << CommentHead + "Result is empty. " << std::endl;
        return ret;
    }
    if (isSearchArray){
        if (_TargetECM!=NULL){
            for (UInt4 i=0; i<_ResultIndex.size(); i++)
                ret.Add( _TargetECM->Put( _ResultIndex[i][0] ) );
        }else{
            std::cout << CommentHead + "Target is empty. " << std::endl;
        }
    }else{
        std::cout << CommentHead + "Latest search was not for Array. " << std::endl;
        return ret;
    }

    return ret;
}
//////////////////////////////////////////////////////////
std::vector<UInt4> SearchInHeader::
PutResultIndex( UInt4 index){
    std::vector<UInt4> ret;
    ret.clear();
    if ( (index!=0)&&(index!=1) ){
        std::cout << CommentHead + "argument is invalid (index= "<< index << ")" << std::endl;
        return ret;
    }
    if (_ResultIndex.empty()){
        std::cout << CommentHead + "Result is empty. " << std::endl;
        return ret;
    }
    ret.resize( _ResultIndex.size() );
    for (UInt4 i=0; i<_ResultIndex.size(); i++){
        ret[i] = _ResultIndex[i][index];
    }
    return ret;
}

//////////////////////////////////////////////////////////
void SearchInHeader::
ShowResults()
{
    std::string msg="";
    StringTools st;
    if (_ResultIndex.empty()){
        msg += "Empty\n";
    } else {
        msg += "------------------------------\n";
        for (UInt4 i=0; i<_ResultIndex.size(); i++){
            msg += "( ";
            for (UInt4 j=0; j<_ResultIndex[i].size(); j++){
                msg += st.UInt4ToString( _ResultIndex[i][j] );
                if (j< _ResultIndex[i].size()-1) msg += " , ";
            }
            msg += " )\n";
        }
    }

    std::cout << msg << std::endl;
}

//////////////////////////////////////////////////////////
void SearchInHeader::
ChangeDebugFlag( bool flag)
{
    isDebug = flag;
}

//////////////////////////////////////////////////////////
std::vector<Double> SearchInHeader::
FindLimitElementDoubleVector( std::string key, UInt4 index )
{
    double maxv,minv;
    maxv = minv = 0.0;
    std::vector<double> ret;
    ret.clear();

    if (_TargetECM!=NULL){
        for (UInt4 i=0; i<_TargetECM->PutSize(); i++){
            for (UInt4 j=0; j<_TargetECM->PutPointer(i)->PutSize(); j++){
                HeaderBase *H = _TargetECM->PutPointer(i)->PutPointer(j)->PutHeaderPointer();
                if ((H->CheckKey(key))!=0){
                    std::vector<double> val = H->PutDoubleVector(key);
                    if (val[index]>maxv) { maxv = val[index]; }
                    if (val[index]<minv) { minv = val[index]; }
                }
            }
        }
    } else if (_TargetECA!=NULL){
        for (UInt4 i=0; i<_TargetECA->PutSize(); i++){
            HeaderBase *H = _TargetECM->PutPointer(i)->PutHeaderPointer();
            if ((H->CheckKey(key))!=0){
                std::vector<double> val = H->PutDoubleVector(key);
                if (val[index]>maxv) { maxv = val[index]; }
                if (val[index]<minv) { minv = val[index]; }
            }
        }
    } else {
        std::cout << CommentHead + "Target is empty!!" << std::endl;
        return ret;
    }

    ret.push_back(minv);
    ret.push_back(maxv);
    return ret;
}





