#include "hausdorfm.h"

//makro NORMA definuje cerny bod
//#define NORMA ==0
#define NORMA <=150
//#define METRIKA sqrt((double)pow(delta_x,2)+pow(delta_y,2))

//MOZNOSTI VYPOCTU VZDALENOSTI-METRIKA:
    //delta_x+delta_y;//menhattan
    //sqrt((double)pow(delta_x,2)+pow(delta_y,2));//euklid
    //(delta_x > delta_y) ? delta_x : delta_y;//cheesboard

//modifikace:
#define METRIKA sqrt(this->w1*((double)pow(delta_x,2)+pow(delta_y,2))+this->w2* pow((this->img_ref->get(x1,y1)-img->get(x2,y2)),2));//euklid



hausdorfm::hausdorfm(my_image * image,bool mod)
{
this->img_ref=NULL;
this->img=NULL;

this->w1=1;
this->w2=1;

this->location.x=0;
this->location.y=0;

this->registruj_mod=mod;


this->img_ref=new my_image(image);

if(this->registruj_mod){
    this->body_img_ref=this->registruj(this->img_ref);
    std::cout<< "registrovano "<<this->body_img_ref.size()<<" bodu REF obrazku\n";
}

//show_image(image);
}


hausdorfm::~hausdorfm(){
    if(this->img_ref!=NULL)
        delete this->img_ref;


    this->del_image();
}

void hausdorfm::del_image(){
    if(this->img!=NULL)
        delete this->img;
}

float hausdorfm::Find_Nearest_Point(BOD stred,my_image * img){
    BOD ret;
    uchar value;
    value=img->get(stred);
    if(value NORMA)
        return 0;

    float min=999,old;
    int max_l=999;
    for(int i=1;i<max_l;i++){
        old=this->find_nearest_point(stred,i,img);
        if(old<min){
            min=old;
            max_l=ceil(min);
        }
    }

    //std::cout<<"minimum je"<<min<<"\n";
    return min;
}

float hausdorfm::find_nearest_point(BOD stred,int l,my_image * img){
    int i_start=stred.x-l;
    int i_max=stred.x+l;
    if(i_start<0)
        i_start=0;
    if(i_start>=i_max)
        i_start=i_max;
    if(i_max>=img->Width())
        i_max=img->Width();

    int j_start=stred.y-l;
    int j_max=stred.y+l;
    if(j_start<0)
        j_start=0;
    if(j_start>=j_max)
        j_start=j_max;
    if(j_max>=img->Height())
        j_max=img->Height();


    float min=999;
    for(int j=j_start;j<=j_max;j++){
            min=this->find_nearest_point(stred,i_start,j,min,img);
            min=this->find_nearest_point(stred,i_max,j,min,img);
    }

    for(int i=i_start;i<=i_max;i++){
          min=this->find_nearest_point(stred,i,j_start,min,img);
          min=this->find_nearest_point(stred,i,j_max,min,img);
    }

    return min;
}

float hausdorfm::find_nearest_point(BOD stred, int x, int y,float old_min,my_image *img){
    if(x<0 || x>=img->Width() || y<0 || y>=img->Height()){
        return old_min;
    }

     uchar val=img->get(x,y);
    float min=old_min;

    if(val NORMA){
        float distanc=this->find_distant(stred.x,stred.y,img,x,y);
        if(distanc<min){
            min=distanc;
            //std::cout<<"nalezljsem nove minimum na "<<min<<"\n";
        }
    }

    return min;
}



float hausdorfm::find_distant(int x1,int y1, my_image *img ,int x2,int y2) {
    float vzdalenost=0;
    int delta_x=abs(x1-x2);
    int delta_y=abs(y1-y2);

    uchar a,b;
    a=this->img_ref->get(x2,y2);
    b=this->act_img_point_value;

    //vzdalenost=METRIKA ;
    vzdalenost=sqrt(this->w1*((double)pow(delta_x,2)+pow(delta_y,2))+this->w2* pow(a-b,2));//euklid
    return vzdalenost;
}


void hausdorfm::find(my_image *image){
    //show_image(image);
    this->del_image();//smaze posledni nacteny obraz
    this->img=new my_image(image);

    //show_image(image);
    //show_image(this->img_ref);

    if(registruj_mod){
        this->body_img=this->registruj(this->img);
        std::cout<< "registrovano "<<this->body_img_ref.size()<<" bodu REF a "<<this->body_img.size()<<"body obrazku\n";
    }


    this->H_ab();
     std::cout<<"skoncil jsem s vyhledavanim\n"<<std::endl;
   //float vzdalenost=this->h_ab(519,279,this->img,this->img_ref);
   //std::cout<<"vzdalenost je "<<vzdalenost<<"\n";
   // std::cout<<"min vzdalenost je "<<this->location.val<<" na souradnicich "<<this->location.x<<" x " <<this->location.y<<"\n";
}


void hausdorfm::H_ab(){
    float min=9999,temp;
    int half_size_x=this->img->Width()/2;
    int half_size_y=this->img->Height()/2;
    int max_w=this->img_ref->Width()-this->img->Width();
    int max_h=this->img_ref->Height()-this->img->Height();
    this->act_min_posun=min;

    this->body.erase(body.begin(),body.end());


    for(int i=0;i<max_w;i++){
        for(int j=0;j<max_h;j++){
        //    std::cout<<"zjistuji pro posun "<<i<<" x "<<j<<"\n"<<std::endl;
            if(this->registruj_mod)
                temp=this->h_ab_reg(i,j,this->img,this->img_ref);
            else
                temp=this->h_ab(i,j,this->img,this->img_ref);

            if(temp<min){
                min=temp;
                this->act_min_posun=min;
                this->location.x=i;
                this->location.y=j;
                this->location.val=min;
                std::cout<<"nove minimum  "<<this->location.val<<" na souradnicich "<<this->location.x<<" x " <<this->location.y<<"\n";
                //this->body.push_back(BOD(i,j,min));
                this->body.insert(this->body.begin(),BOD(i+half_size_x,j+half_size_y,min));
              //  this->body.insert(this->body.begin(),BOD(i,j,min));

            }
        }
    }//*/

}



float hausdorfm::h_ab(int posun_x, int posun_y, my_image *imageA, my_image *imageB){//posun je posuna ref. obrazu

    int max_w=imageA->Width();
    int max_h=imageA->Height();
    uchar val;
    float max=0,max_act;

    for(int i=0;i<max_w;i++){
        for(int j=0;j<max_h;j++){
            if(this->act_min_posun<max)//zrychleni algoritmu
                return max;


            val=imageA->get(i,j);
            if(val NORMA){//pokud je bod, potom najdi nejmensi vzdalenost
               this->act_img_point_value=val;
               max_act=this->Find_Nearest_Point(BOD(i+posun_x,j+posun_y),imageB );
               if(max_act>max){
                    max=max_act;
               }
                /*if(max<imageB->get(i+posun_x,j+posun_y)){//pokud je nejmensi vzdalenost vetsi nez max, uloz max
                    max=imageB->get(i+posun_x,j+posun_y);*/
            }
        }
    }

    return max;
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////ALGORITMUS S REGISTRACI NORMOVANYCH BODU:///////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



std::vector<BOD> hausdorfm::registruj(my_image *image){
    int max_w=image->Width();
    int max_h=image->Height();
    uchar val;
    std::vector<BOD> ret;

    for(int i=0;i<max_w;i++){
        for(int j=0;j<max_h;j++){
            val=image->get(i,j);
            if(val NORMA){//pokud je bod
                ret.push_back(BOD(i,j,val));
            }
        }
    }
    return ret;
}



float hausdorfm::h_ab_reg(int posun_x, int posun_y, my_image *imageA, my_image *imageB){
    int max_iter=this->body_img.size();

    float max=0,max_act;

    for(int i=0;i<max_iter;i++){
            if(this->act_min_posun<max)//zrychleni algoritmu
                return max;
               this->act_img_point_value=this->body_img[i].val;
               max_act=this->Find_Nearest_Point_reg(BOD(this->body_img[i].x+posun_x,this->body_img[i].y+posun_y),imageB );
               if(max_act>max){
                    max=max_act;
               }
    }

    return max;

}

float hausdorfm::Find_Nearest_Point_reg(BOD stred,my_image * image){
    float min=9999,temp;
    int max_iter=this->body_img_ref.size();

    for(int i=0;i<max_iter;i++){
        temp=this->find_distant(stred.x,stred.y,image,this->body_img_ref[i].x,this->body_img_ref[i].y);
        if(temp<min)
           min=temp;
    }

return min;
}


