/*

    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 <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 <wrap_bracket_dialog.hpp>
#include <wrap_in_bracket.hpp>

//#include <wrap_in_bracket_dialog.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
 *
 */



wrap_in_bracket::wrap_in_bracket(immagine* image,string libpath)
  :bist_plugin(image,libpath),
   _has_to_act(false),
   _has_acted(false),
   _merge(false),
   _free_wrap(false),
   _type(WRAP_BRACKET_BOX)
{

}


void wrap_in_bracket::inizialize(){
  _type=make_wrap_molecule_dialog(_merge,_free_wrap);

  if(!_free_wrap){
    _has_to_act=true;
  }

}


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

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

bool wrap_in_bracket::act(int e){
  /**
   *Contiene  coppie <tipo, < gruppo, id > > selezionati, se  si e'
   *selezionato una etichetta gruppo e' uguale a NO_VALID_GROUP
   */



  if(!_has_acted && _has_to_act){

    if(_free_wrap){
      switch(e){
      case FL_PUSH:
        {


          
          if(_box.size()<1){
            std::pair<float,float> bb(Fl::event_x(),Fl::event_y());
            _box.push_back(bb);
            _has_acted=false;
            _has_to_act=true;

          }else{
            int dx=(MainWindow->ritorna_mol_canvas())->x();
            int dy=(MainWindow->ritorna_mol_canvas())->y();
            std::pair<float,float> bb(Fl::event_x(),Fl::event_y());
            _box.push_back(bb);

            float w=_box[1].first  - _box[0].first;
            float h=_box[1].second - _box[0].second;
            
            switch (_type){
            case WRAP_BRACKET_BOX:
              insert_box_bra(NULL,_box[0].first-dx ,_box[0].second-dy,w,h);
              break;
            case WRAP_BRACKET_PARENTHESES:
              insert_parent_bra(NULL,_box[0].first-dx,_box[0].second-dy,w,h);
              break;
            case WRAP_BRACKET_CURLY:
              insert_curly_bra(NULL,_box[0].first-dx,_box[0].second-dy,w,h);
              break;
            }
            _has_to_act=true;
            _has_acted=true;
          }
          
        }
      }
    }else{
      
      vector< pair < int, pair<int,int> > >* elem=r_elem_selected();
      
      if(elem->size()>0){
        float lx=1E9;
        float ly=1E9;
        
        float rx=-1E9;
        float ry=-1E9;
        
        float w=-1;
        float h=-1;
        
        
        vector< pair < int, pair<int,int> > >::iterator el_b=elem->begin();
        vector< pair < int, pair<int,int> > >::iterator el_e=elem->end();
        
        while(el_b!=el_e){
          float tmp_px=1E9;
          float tmp_py=1E9;
          
          float tmp_w=-1E9;
          float tmp_h=-1E9;
          
          switch((*el_b).first){
          case ATOMO:
            {
              gruppo* the_grp=_the_image->find_group_id(((*el_b).second).first);
              tmp_px=the_grp->phys_posx();
              tmp_py=the_grp->phys_posy();
              tmp_w=the_grp->phys_w();
              tmp_h=the_grp->phys_h();
            }
            break;
          case PROC_BEZIER:
          case PROC_ARC:
            {
              gruppo* the_grp=_the_image->find_group_id(((*el_b).second).first);
              procedura* proc=the_grp->find_proc_id(((*el_b).second).second);
              tmp_px=proc->phys_posx();
              tmp_py=proc->phys_posy();
              tmp_w=proc->phys_w();
              tmp_h=proc->phys_h();
            }
            break;
          case PROC_ARROW:
            {
              gruppo* the_grp=_the_image->find_group_id(((*el_b).second).first);
              proc_arrow* proc=dynamic_cast<proc_arrow*>(the_grp->find_proc_id(((*el_b).second).second));
              std::pair<float,float> ld;
              std::pair<float,float> ru;
              
              proc->get_phys_bounding_box(ld,ru);
              
              tmp_w=ru.first - ld.first;
              tmp_h=ru.second - ld.second;
              tmp_px=ld.first;
              tmp_py=ld.second;
              
              /*
                
                float b_px=proc->phys_posx();
                float b_py=proc->phys_posy();
                
                float e_px=proc->phys_eposx();
                float e_py=proc->phys_eposy();
                
                if(b_px < e_px){
                tmp_px=b_px;
                }else{
                tmp_px=e_px;
                }
                
                if(b_py < e_py){
                tmp_py=b_py;
                }else{
                tmp_py=e_py;
                }
                
                tmp_w=abs(proc->phys_w());
                tmp_h=abs(proc->phys_h());
              */
              
              
            }
            break;
          case ETICHETTA:
            {
              etichetta* laet=_the_image->ritorna_etich(((*el_b).second).second);
              tmp_px=laet->phys_x();
              tmp_py=laet->phys_y();
              tmp_w=laet->phys_w();
              tmp_h=laet->phys_h();
            }
            break;
          }
          
          if(tmp_px < lx){
            lx=tmp_px;
          }
          
          if(tmp_w + tmp_px > rx){
            rx=tmp_w + tmp_px;
          }
          
          if(tmp_py < ly){
            ly=tmp_py;
          }
          
          if(tmp_h + tmp_py > ry){
            ry=tmp_h + tmp_py;
          }
          
          el_b++;
        }
        
        
        w=rx-lx;
        h=ry-ly;
        
        
        gruppo* add_to_this=NULL;
        
        
        if(_merge){
          vector <gruppo>* the_groups=r_groups();
          for(unsigned int i=0;i<the_groups->size();i++){
            if( (*the_groups)[i].id_gruppo()  ==  ((*elem)[0].second).first){
              add_to_this=&((*the_groups)[i]);
            }
          }
        }
        
        switch (_type){
        case WRAP_BRACKET_BOX:
          insert_box_bra(add_to_this,lx,ly,w,h);
          break;
        case WRAP_BRACKET_PARENTHESES:
          insert_parent_bra(add_to_this,lx,ly,w,h);
          break;
        case WRAP_BRACKET_CURLY:
          insert_curly_bra(add_to_this,lx,ly,w,h);
          break;
        }
      }
      _has_to_act=false;      
      _has_acted=true;
    }
  }
  return _has_acted;
}


wrap_in_bracket::~wrap_in_bracket(){

  //  cout << "wrap_in_bracket distruzione!!! " << _the_image <<endl;
}

void wrap_in_bracket::register_plugin(){

}


bool wrap_in_bracket::time_to_act(){
  bool res=false;

  if(_free_wrap){
    if(_has_acted){
      res=false;
    }else{
      _has_to_act=true;
      res=true;
    }
  }else{
    if(_has_to_act){
      res=true;
    }else if(_has_acted){
      res=false;
    }
  }

  return res;

}


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

void wrap_in_bracket::insert_box_bra(gruppo* the_group, float px, float py,
				     float w, float h){
  float arr_w=__pref.get_arr_w();
  float arr_h=__pref.get_arr_h();
  float arr_gap=__pref.get_arr_gap();

  float offset=__pref.getBond_fixedlength()/3.;
  float h_line_width=w/10;

  if(w<__pref.getBond_fixedlength()){
    h_line_width=w/4;
  }

  gruppo* to_add=NULL;

  if(the_group==NULL){
    to_add=new gruppo();
  }else{
    to_add=the_group;
  }

  float thikness_thr=(10*h)/(900+h);

  for(float thick=0;thick<thikness_thr;thick++){

    /*

                        -------+
                               |
                               |
                               

    */
    proc_arrow hor(0,
                   offset + px+w-h_line_width, 
                   py - offset - thick,
                   offset+1 + px+w + thick,
                   py - offset - thick,
                   0, 0,0,
                   0,0,0,
                   2,0, ARR_NO_PUNT,
                   arr_w,arr_h,arr_gap);


    proc_arrow ver(0,offset + px+w+thick  , 
                   py - offset - thick,
                   offset +px+w + thick,
                   py+h + offset +thick,
                   0, 0,0,
                   0,0,0,
                   2,0, ARR_NO_PUNT,
                   arr_w,arr_h,arr_gap);

    proc_arrow hor2(0,offset + px+w-h_line_width, 
                    py+h + offset + thick,
                    offset +1+ px+w +thick,
                    py+h + offset + thick,
                    0, 0,0,
                    0,0,0,
                    2,0, ARR_NO_PUNT,
                    arr_w,arr_h,arr_gap);



    proc_arrow hor_m(0,
                     -offset-1 + px -thick, 
                     py - offset - thick,
                     -offset  + px + h_line_width,
                     py - offset - thick,
                     0, 0,0,
                     0,0,0,
                     2,0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);


    proc_arrow ver_m(0,
                     -offset + px - thick,
                     py - offset - thick,
                     -offset +px - thick,
                     py+h + offset + thick,
                     0, 0,0,
                     0,0,0,
                     2,0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);

    proc_arrow hor2_m(0,
                      -offset -1 + px - thick, 
                      py+h + offset + thick,
                      -offset  + px + h_line_width,
                      py+h +  offset + thick,
                      0, 0,0,
                      0,0,0,
                      2,0, ARR_NO_PUNT,
                      arr_w,arr_h,arr_gap);




    to_add->aggiungi_procedura(&hor);
    to_add->aggiungi_procedura(&ver);
    to_add->aggiungi_procedura(&hor2);
    to_add->aggiungi_procedura(&hor_m);
    to_add->aggiungi_procedura(&ver_m);
    to_add->aggiungi_procedura(&hor2_m);


  }

  if(the_group==NULL){
    _the_image->aggiungi_gruppo(*to_add);
    delete to_add;
  }


}


void wrap_in_bracket::insert_parent_bra(gruppo* the_group,
					float px,float py,
					float w,float h){

  float arr_w=__pref.get_arr_w();
  float arr_h=__pref.get_arr_h();
  float arr_gap=__pref.get_arr_gap();
  //float h_line_width=w/10;
  float offset=__pref.getBond_fixedlength()/3.;
  float depth=h/5;



  if(h<__pref.getBond_fixedlength()){
    if(!similar_to(h,static_cast<float>(0),static_cast<float>(0.1))){
      depth=h/2;
    }else{
      depth=h/4;
    }
  }

  int num_bez=1+(h/__pref.getBond_fixedlength());



  int off_bez_thick=0;
  int off_bez_thick_incr=1;



  gruppo* to_add=NULL;

  if(the_group==NULL){
    to_add=new gruppo();
  }else{
    to_add=the_group;
  }



  for(int i=0;i<num_bez;i++,off_bez_thick+=off_bez_thick_incr){
    proc_bezier par1(0,px-offset,py-offset,
                     px-depth-offset-off_bez_thick, py,
                     px-depth-offset-off_bez_thick, py+h,
                     px-offset, py+h+offset,
                     0,0,0 ,
                     0,0,0,
                     1, 0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);

    proc_bezier par2(0,px+w+offset,py-offset,
                     px+w+depth+offset+off_bez_thick, py,
                     px+w+depth+offset+off_bez_thick, py+h,
                     px+w+offset, py+h+offset,
                     0,0,0 ,
                     0,0,0,
                     1, 0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);


    to_add->aggiungi_procedura(&par1);
    to_add->aggiungi_procedura(&par2);
  }

  if(the_group==NULL){
    _the_image->aggiungi_gruppo(*to_add);
    delete to_add;
  }


}


void wrap_in_bracket::insert_curly_bra(gruppo* the_group,
				       float px,float py,
				       float w,float h){
  float arr_w=__pref.get_arr_w();
  float arr_h=__pref.get_arr_h();
  float arr_gap=__pref.get_arr_gap();
  float offset=__pref.getBond_fixedlength()/3.;
  float depth=h/5;

  if(h<__pref.getBond_fixedlength()){
    if(!similar_to(h,static_cast<float>(0),static_cast<float>(0.1))){
      depth=h/2;
    }else{
      depth=5;
    }
  }

  float thikness_slope=0.2f;

  float thikness_thr=(21*h)/(160+h);

  gruppo* to_add=NULL;

  if(the_group==NULL){
    to_add=new gruppo();
  }else{
    to_add=the_group;
  }



  for(float dyn_offset=0;dyn_offset<thikness_thr;dyn_offset++){

    proc_bezier sig1(0,px-offset,py-offset,
                     px-depth-offset-dyn_offset,
                     py-offset+dyn_offset*thikness_slope,
                     px-offset-dyn_offset, py+h/2+dyn_offset*(thikness_slope/2.0),
                     px-depth-offset, py+h/2,
                     0,0,0,
                     0,0,0,
                     2, 0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);


    proc_bezier sig2(0,px-depth-offset, py+h/2,
                     px-offset-dyn_offset,
                     py+h/2-dyn_offset*thikness_slope,
                     px-depth-offset-dyn_offset,
                     py+h+offset-dyn_offset*(thikness_slope/2.0),
                     px-offset, py+h+offset,
                     0,0,0,
                     0,0,0,
                     2, 0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);

    proc_bezier sig3(0,px+w+offset,
                     py-offset,
                     px+w+depth+offset+dyn_offset,
                     py-offset+dyn_offset*thikness_slope,
                     px+w+offset+dyn_offset,
                     py+h/2-dyn_offset*(thikness_slope/2.0),
                     px+w+depth+offset, py+h/2,
                     0,0,0 ,
                     0,0,0,
                     2, 0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);

    proc_bezier sig4(0,px+w+depth+offset, py+h/2,
                     px+w+offset+dyn_offset,
                     py+h/2+dyn_offset*thikness_slope,
                     px+w+depth+offset+dyn_offset,
                     py+h+offset-dyn_offset*(thikness_slope/2.0),
                     px+w+offset,
                     py+h+offset,
                     0,0,0,
                     0,0,0,
                     2, 0, ARR_NO_PUNT,
                     arr_w,arr_h,arr_gap);


    to_add->aggiungi_procedura(&sig1);
    to_add->aggiungi_procedura(&sig2);
    to_add->aggiungi_procedura(&sig3);
    to_add->aggiungi_procedura(&sig4);

  }

  if(the_group==NULL){
    _the_image->aggiungi_gruppo(*to_add);
    delete to_add;
  }


}

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




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

extern "C" void destroy_plugin(bist_plugin* j){
  //cout << "distruzione plugin: " << j <<  endl;
  delete j;
  //cout << "riuscita" << endl;
}


