#include "D4MatrixCalc.hh"

//////////////////////////////////////////////////////////////////////
D4MatrixCalc::
D4MatrixCalc(){
    _D4Mat1=NULL;
    _D4Mat2=NULL;;
    _D4Mat=NULL;
    MessageTag = "D4MatrixCalc >> ";
    //MASKVALUE = 100000000.0;
    MASKVALUE = UTSUSEMIMASKVALUE32;
}
//////////////////////////////////////////////////////////////////////
D4MatrixCalc::
~D4MatrixCalc(){
    if (_D4Mat1!=NULL) delete _D4Mat1;
    if (_D4Mat2!=NULL) delete _D4Mat2;
    if (_D4Mat!=NULL) delete _D4Mat;

}
//////////////////////////////////////////////////////////////////////
void D4MatrixCalc::
OpenMat1( std::string datapath, std::string paramfile ){
    if (_D4Mat1!=NULL) delete _D4Mat1;
    _D4Mat1 = new UtsusemiD4Matrix();
    _D4Mat1->OpenMat( datapath, paramfile );
    if (!(_D4Mat1->isStatusOK())){
        UtsusemiError( MessageTag+"OpenMat1 > Failure to open D4Matrix "+datapath+"/"+paramfile );
        delete _D4Mat1;
        _D4Mat1=NULL;
    }
}
//////////////////////////////////////////////////////////////////////
void D4MatrixCalc::
OpenMat2( std::string datapath, std::string paramfile ){
    if (_D4Mat2!=NULL) delete _D4Mat2;
    _D4Mat2 = new UtsusemiD4Matrix();
    _D4Mat2->OpenMat( datapath, paramfile );
    if (!(_D4Mat2->isStatusOK())){
        UtsusemiError( MessageTag+"OpenMat2 > Failure to open D4Matrix "+datapath+"/"+paramfile );
        delete _D4Mat2;
        _D4Mat2=NULL;
    }
}

//////////////////////////////////////////////////////////////////////
void D4MatrixCalc::
CalcBetweenTwo( std::string ope, std::string data_dir, std::string paramfile ){
    CalcBetweenTwo( ope, 1.0, 1.0, data_dir, paramfile );
}
//////////////////////////////////////////////////////////////////////
void D4MatrixCalc::
CalcBetweenTwo( std::string ope, Double coef1, Double coef2, std::string data_dir, std::string paramfile ){
    if ((_D4Mat2==NULL)||(_D4Mat1==NULL)){
        UtsusemiError( MessageTag+"CalcBetweenTwo >  Two D4Matrixs are not Opened." );
        return;
    }

    StringTools st;

    UInt4 num_bin_files = (UInt4)(_D4Mat1->fs_list.size());
    if (num_bin_files!=_D4Mat2->fs_list.size()){
        UtsusemiError( MessageTag+"CalcBetweenTwo > Bin files for two D4Matrixs are not equal." );
        return;
    }

    if (_D4Mat!=NULL) delete _D4Mat;
    _D4Mat = new UtsusemiD4Matrix();
    for (UInt4 i=0;i<(_D4Mat1->NumBin.size());i++) _D4Mat->NumBin.push_back( _D4Mat1->NumBin[i] );
    for (UInt4 i=0;i<(_D4Mat1->range_list.size());i++){
        std::vector<Double> v;
        v.clear();
        for (UInt4 j=0;j<(_D4Mat1->range_list[i].size());j++) v.push_back( _D4Mat1->range_list[i][j] );
        _D4Mat->range_list.push_back( v );
    }
    for (UInt4 i=0;i<4;i++){
        _D4Mat->ax_titles.push_back( _D4Mat1->ax_titles[i] );
        _D4Mat->ax_units.push_back( _D4Mat1->ax_units[i] );
    }
    for (UInt4 i=0;i<(_D4Mat1->file_components.size());i++) _D4Mat->file_components.push_back( _D4Mat1->file_components[i] );
    for (UInt4 i=0;i<(_D4Mat1->index_of_blocks.size());i++) _D4Mat->index_of_blocks.push_back( _D4Mat1->index_of_blocks[i] );

    std::string data_name_base( paramfile );
    std::string d4mat_param_file = "";
    std::string d4mat_data_dir = "";

    std::string::size_type ind = paramfile.find_last_of(".xml");
    if (ind == std::string::npos) {
        d4mat_param_file = paramfile+".xml";
    }else{
        d4mat_param_file = paramfile;
        data_name_base.erase( ind-3 );
    }
    if (data_dir.substr( data_dir.size()-1 )!=std::string("/")){
        d4mat_data_dir = data_dir+"/";
    }else{
        d4mat_data_dir = std::string( data_dir );
    }

    for (UInt4 i=0;i<(_D4Mat1->name_of_blocks.size());i++){
        std::string org_name = _D4Mat1->name_of_blocks[i];
        UInt4 org_size = (UInt4)(org_name.size());
        std::string fname = data_name_base + org_name.substr( org_size-10 );
        UtsusemiMessage( MessageTag+"CalcBetweenTwo > OutPut bin file name="+fname );
        _D4Mat->name_of_blocks.push_back(fname);
    }

    Int4 ret = _D4Mat->SaveParamXml( d4mat_data_dir, d4mat_param_file );
    if (ret<0){
        UtsusemiError( MessageTag+"CalcBetweenTwo > Failure to SaveParamXml." );
        return;
    }

    const UInt4 blocksize=100000;

    for (UInt4 i=0;i<num_bin_files;i++){
        UInt4 curr_posi = 0;
        FILE *fp;
        std::string bin_file_path = d4mat_data_dir + _D4Mat->name_of_blocks[i];
        if (( fp=fopen(bin_file_path.c_str(),"wb"))==NULL){
            UtsusemiError( MessageTag+"CalcBetweenTwo > Cannot open file ="+bin_file_path );
            return;
        }
        UtsusemiMessage( MessageTag+"CalcBetweenTwo > open "+bin_file_path );
        while(true){

            float dat1[ blocksize*3 ];
            float dat2[ blocksize*3 ];
            float res[ blocksize*3 ];
            //std::cout << "@@@ current position=" << curr_posi << std::endl;
            Int4 r1 = _D4Mat1->GetPartOfD4Mat( i, dat1, curr_posi, blocksize );
            if (r1<0){
                UtsusemiError( MessageTag+"CalcBetweenTwo >  Failure to GetPartOfD4Mat of first data retval (<0)" );
                break;
            }
            Int4 r2 = _D4Mat2->GetPartOfD4Mat( i, dat2, curr_posi, blocksize );
            if (r2<0){
                UtsusemiError( MessageTag+"CalcBetweenTwo >  Failure to GetPartOfD4Mat of second data retval (<0)" );
                break;
            }
            if (r1!=r2){
                UtsusemiError( MessageTag+"CalcBetweenTwo >  Failure to GetPartOfD4Mat of data r1="+st.Int4ToString(r1)+", r2="+st.Int4ToString(r2) );
                break;
            }

            if ((r1==0)||(r2==0)) break;

            for (UInt4 j=0;j<(UInt4)r1;j++){
                float num1 = dat1[j*3];
                float num2 = dat2[j*3];
                if ((dat1[j*3+2]<1.0)||(dat2[j*3+2]<1.0)){
                    res[j*3] = float(MASKVALUE);
                    res[j*3+1] = float(0.0);
                    res[j*3+2] = float(0.0);
                }else{
                    if (ope=="+") res[j*3] = float(coef1)*num1/dat1[j*3+2] + float(coef2)*num2/dat2[j*3+2];
                    if (ope=="-") res[j*3] = float(coef1)*num1/dat1[j*3+2] - float(coef2)*num2/dat2[j*3+2];
                    res[j*3+1] = float(coef1)*dat1[j*3+1] + float(coef1)*dat2[j*3+1];
                    res[j*3+2] = 1.0;
                }

            }

            UInt4 write_size = sizeof(res);
            if (r1!=blocksize){
                write_size = r1*sizeof(float)*3;
            }

            if ((int)std::fwrite( res, write_size, 1, fp ) !=1){
                //usleep(50000);
                std::this_thread::sleep_for(std::chrono::milliseconds(50));
                if ((int)std::fwrite( res, write_size, 1, fp ) !=1){
                    UtsusemiError( MessageTag+"CalcBetweenTwo > Failure to write" );
                    return;
                }
            }

            if (r1==blocksize){
                curr_posi += blocksize;
            }else{
                //std::cout << "Last block size=" << r1 << std::endl;
                break;
            }
        }
        fclose(fp);
        UtsusemiMessage( MessageTag+"CalcBetweenTwo > close file : "+bin_file_path );
    }
}
