/*

    Bist: a chemical drawing tool
    Copyright (C) 2008 Valerio Benfante

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <config_path.h>
#include <global.hpp>

#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <cairo_t_singleton.hpp>
#include <glib.h>


#include <wordexp.h>
#include <sys/stat.h>
#include <dirent.h>
#include <cerrno>
//portability problem?
#include <pthread.h>

#include <cstdio>


#include <FL/Fl.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Pixmap.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Toggle_Button.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Help_Dialog.H>
#include <FL/Fl_Check_Button.H>
//#include <FL/Fl_PNG_Image.H>

#include <interfacce.hpp>
#include <legame.hpp>
#include <etichetta.hpp>
#include <multiline_label.hpp>
#include <multifont_label.hpp>
#include <paragraph_text.hpp>
#include <atomo.hpp>
#include <procedura.hpp>
#include <gruppo.hpp>
#include <immagine.hpp>
#include <bist_plugin.hpp>
#include <mol_canvas.hpp>
#include <finestra_pr.hpp>
#include <editor.hpp>

#include <prefs.hpp>

#include <set_conf.hpp>
#include <command_line.hpp>
#include <util.hpp>
#include <2D_vector.hpp>

#include <attach_ring.hpp>

extern finestra_pr* MainWindow;

extern Preferences  __pref;

extern bool __close;




/**
 *Un plugin deve avere :
 *
 *una funzione  bool (*need_group)()  che ritorna true  se il  plugin ha
 *bisogno di conoscre quali atomi sono selezionati;
 *
 *una funzione  bool (*need_leg)()  che ritorna true  se il  plugin ha
 *bisogno di conoscere quali legami sono selezionati;
 *
 *una funzione  bool (*act)()  che computa quello  per cui il  plugin e'
 *stato scritto
 *
 */



attach_ring::attach_ring(immagine* image,string libpath)
  :bist_plugin(image,libpath),
   _has_to_act(true),
   _has_acted(false)
  
{

   


}


void attach_ring::inizialize(){
  const char* numsize=fl_input("number of size?");
  if(numsize!=NULL){
    _vertices=strtol(numsize,NULL,0);
  }else{
    _vertices=5;
  }
}


bool attach_ring::need_atom(){
  return false;
}

bool attach_ring::need_leg(){
  return false;
}

bool attach_ring::act(int e){


  switch(e){
  case FL_PUSH:
    {

      bool exists=true;
      _the_image->prova_click(Fl::event_x(), 
                              Fl::event_y(),
                              exists,
                              true);
      
      if(Fl::event_button()==FL_RIGHT_MOUSE){
        attach(true);
      }else{
        attach(false);
      }
    }
    break;
  case  FL_KEYUP:
    switch(Fl::event_key()){
    default:
      _has_to_act=false;
      break;
    }//end case FL_KEYUP:
    
  }//end switch(e)



  return _has_to_act;
}


attach_ring::~attach_ring(){
  
  //std::cerr << __FUNCTION__  <<endl;
}

void attach_ring::register_plugin(){

}

 
bool attach_ring::time_to_act(){
  return _has_to_act;
}
    

string attach_ring::libpath(){
  return _lib;
}


void attach_ring::attach(bool aromatic){
  
  /**
   *Contiene  coppie <tipo, < gruppo, id > > selezionati, se  si e'
   *selezionato una etichetta gruppo e' uguale a NO_VALID_GROUP
   */
  vector< pair < int, pair<int,int> > >* elem=r_elem_selected();
  vector< pair < int, pair<int,int> > >::iterator eliniz=elem->begin();
  vector< pair < int, pair<int,int> > >::iterator elend=elem->end();
  //vector <gruppo>* gruppi=r_groups();

  if(eliniz!=elend){

    switch((*eliniz).first){
    case ATOMO:
      {
        try{
          gruppo to_add=create_ring(__pref.getBond_fixedlength(), 
                                    _vertices,aromatic);
          atomo* atm=to_add.find_atomo_id(0);

          std::pair<int,int> type_bond_slot(LEGAME_SINGOLO,LEGAME_SINGOLO);
          vector<legame>::iterator fst=atm->primo_leg();
          vector<legame>::iterator last=fst+1;;
          
          type_bond_slot.first=(*fst).tipo_legame();
          type_bond_slot.second=(*last).tipo_legame();

          to_add.trasla(-atm->pos_x(),-atm->pos_y());
          
          gruppo* slot=_the_image->find_group_id(((*eliniz).second).first);
          atomo*  atm_slot=slot->find_atomo_id(((*eliniz).second).second);
          
          to_add.trasla(atm_slot->pos_x(),atm_slot->pos_y());
          to_add.ruota(M_PI,atm_slot->pos_x(),atm_slot->pos_y());

          std::pair<float,float> bisect=calc_angle(atm_slot,to_add);

//           std::cerr << "------\n" << bisect.first << " "
//                     << bisect.second << std::endl;

          
          bisect.first=-bisect.first;
          bisect.second=-bisect.second;

          float rt_ang=bidimensional_vector::angle(bidimensional_vector::x_ax,
                                                   bisect);

          //std::cerr << "rt_ang " << -rad2deg(rt_ang) << std::endl;

          to_add.ruota(-rt_ang,atm_slot->pos_x(),atm_slot->pos_y());
          _the_image->aggiungi_gruppo(to_add);
          
          
          gruppo* to_attach=_the_image->find_group_id((_the_image->ritorna_ultimo_gruppo()).id());

          to_attach->purge_atom(0);

          slot=_the_image->find_group_id(((*eliniz).second).first);
          atm_slot=slot->find_atomo_id(((*eliniz).second).second);

          
          _the_image->crea_legame_nuovo(atm_slot, to_attach->find_atomo_id(1),
                                        type_bond_slot.first);



          slot=_the_image->find_group_id(((*eliniz).second).first);
          atm_slot=slot->find_atomo_id(((*eliniz).second).second);

          atomo lst=*(slot->fin_atom()-1);
          _the_image->crea_legame_nuovo(atm_slot, slot->find_atomo_id(lst.id()),
                                        type_bond_slot.second);
           
        }catch(out_of_range e){
          
        }
      }
    }
    
  }
}


std::pair<float,float> attach_ring::calc_angle(atomo* slot, gruppo ring){
  std::vector<float> x_n;
  std::vector<float> y_n;

  std::vector< std::pair<float,int> > angles_idx_pos;
  std::vector< std::pair<float,int> > angles_idx_neg;

  std::pair<float,float> min_v(1,0);
  std::pair<float,float> max_v(1,0);

  std::pair<float,float> res(0,0);



  gruppo* skel=_the_image->find_group_id(slot->id_gruppo());
  vector<legame>::iterator fst=slot->primo_leg();
  vector<legame>::iterator lst=slot->ultimo_leg();

  while(fst!=lst){
    atomo* ngb=skel->find_atomo_id((*fst).id_atomo());
    x_n.push_back(ngb->pos_x()- slot->pos_x());
    y_n.push_back(ngb->pos_y()- slot->pos_y());
    fst++;
  }

  if(x_n.size()==1){
    std::pair<float,float> vc(x_n[0],y_n[0]);
    return vc;
  }else{

    for(unsigned int i=0;i<x_n.size();i++){
      std::pair<float, int> vec(x_n[i],y_n[i]);
      float ang=bidimensional_vector::angle(bidimensional_vector::x_ax,vec);
      std::pair<float, int> vc(ang,i);
      if(ang<=0){
        angles_idx_neg.push_back(vc);
      }else{
        angles_idx_pos.push_back(vc);
      }
    }

    sort_ang_idx(angles_idx_neg, false);
    sort_ang_idx(angles_idx_pos, false);

    //     std::cerr << "minneg " << std::endl;
    //     for(unsigned int i=0;i<angles_idx_neg.size();i++){
    //       std::cerr << rad2deg(angles_idx_neg[i].first) << " "
    //                 << angles_idx_neg[i].second << " " 
    //                 << x_n[angles_idx_neg[i].second] 
    //                 << "," 
    //                 << y_n[angles_idx_neg[i].second]
    //                 << std::endl;
    //     }
    
    
    //     std::cerr << "minpos " << std::endl;
    //     for(unsigned int i=0;i<angles_idx_pos.size();i++){
    //       std::cerr << rad2deg(angles_idx_pos[i].first) << " "
    //                 << angles_idx_pos[i].second << " "
    //                 << x_n[angles_idx_pos[i].second] 
    //                 << "," 
    //                 << y_n[angles_idx_pos[i].second]
    //                 << std::endl;
    
    //     }

    int max_neg=10*M_PI;
    int min_pos=10*M_PI;

    if(angles_idx_neg.size()>0){
      max_neg=angles_idx_neg.back().second;
    }

    if(angles_idx_pos.size()>0){
      min_pos=angles_idx_pos.back().second;
    }

    if(angles_idx_pos.size()>0 &&
       angles_idx_neg.size()>0){
 
      float bisect=calc_bisect(x_n,
                               y_n,
                               min_pos, max_neg);

      //std::cerr << "res=" <<rad2deg(bisect) << std::endl;
      //std::cerr << "resrad=" << bisect << std::endl;
      //std::cerr <<  min_pos << " " <<  max_neg << std::endl;
      res=bidimensional_vector::rotate(bidimensional_vector::x_ax,bisect);


    }else if (angles_idx_pos.size()>0 &&
              angles_idx_neg.size()==0){
      int max_neg=angles_idx_pos.back().second;
      int min_pos=angles_idx_pos[0].second;
      
      float bisect=calc_bisect(x_n,
                               y_n,
                               min_pos, max_neg);
      res=bidimensional_vector::rotate(bidimensional_vector::x_ax,bisect);
    }else if (angles_idx_pos.size()==0 &&
              angles_idx_neg.size()>0){
      int max_neg=angles_idx_neg.back().second;
      int min_pos=angles_idx_neg[0].second;

      float bisect=calc_bisect(x_n,
                               y_n,
                               min_pos, max_neg);
      res=bidimensional_vector::rotate(bidimensional_vector::x_ax,bisect);
    }
    
  
    return res;
  }
}


float attach_ring::calc_bisect(std::vector<float> x_n,
                               std::vector<float> y_n,
                               float min_pos, float max_neg){
 
  float res=0;
  std::pair<float,float> min(x_n[min_pos],y_n[min_pos]);
  std::pair<float,float> max(x_n[max_neg],y_n[max_neg]);
  
  //std::cerr << "min_povec:" << x_n[min_pos] << "," << y_n[min_pos] << std::endl;
  //std::cerr << "max_negvec:" << x_n[max_neg] << "," << y_n[max_neg] << std::endl;
  
  float bisect=bidimensional_vector::angle(min,max)/2;
  float angle_max_neg=bidimensional_vector::angle(max,
                                                  bidimensional_vector::x_ax);
  //std::cerr << "bisect=" << rad2deg(bisect) <<std::endl;
  //std::cerr << "bisectrad" << bisect <<std::endl;
  //DEBUG_TO_CERR(max_neg);
  
  //   if(angle_max_neg<=M_PI/2){
  //     std::cerr <<  "min_pos<=M_PI/2"
  //               << " " << angle_max_neg << " " << rad2deg(angle_max_neg) << std::endl;
  //     bisect=angle_max_neg+bisect;
  
  //   }else{
  //     std::cerr <<  "min_pos>M_PI/2"
  //               << " " << angle_max_neg << " " << rad2deg(angle_max_neg) << std::endl;
  //     bisect=angle_max_neg+bisect;
  //   }

  bisect=angle_max_neg+bisect;
  res=bisect;
  return res;
}


void attach_ring::sort_ang_idx(std::vector< std::pair<float,int> >& vc, bool asc){
  if(vc.size()>0){
    bool swap=true;

    while(swap){
      swap=false;
      for(unsigned int i=0;i< vc.size()-1; i++){
        if(asc && (vc[i].first > vc[i+1].first) ){
          std::pair<float, int> tmp=vc[i+1];
          vc[i+1]=vc[i];
          vc[i]=tmp;
          swap=true;
        }else if(!asc && (vc[i].first < vc[i+1].first) ){
          std::pair<float, int> tmp=vc[i+1];
          vc[i+1]=vc[i];
          vc[i]=tmp;
          swap=true;
        }
      }
    }
  }
}

std::string attach_ring::menu_path(){
  return "rings/";
}


/**************fine metodi di classe**********************************/




extern "C" bist_plugin* create_plugin(immagine* imm, string libpath){
  return new attach_ring(imm, libpath);
}

extern "C" void destroy_plugin(bist_plugin* j){
  delete j;
}


