#include <global.hpp>
#include <config.h>
#include <config_path.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <dlfcn.h>

#include <deque>

#include <cstdio>
#include <expat.h>


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


#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.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_File_Chooser.H>
#include <FL/Fl_Color_Chooser.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Pixmap.H>
#include <FL/Fl_Tabs.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Help_Dialog.H>
#include <FL/Fl_Float_Input.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Input_Choice.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Tree.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 <immagine_cml.hpp>
#include <immagine_mol.hpp>
//#include <immagine_babel.hpp>

#include <mol_canvas.hpp>

#include <finestra_pr.hpp>

#include <prefs.hpp>

#include <pref_dialog.hpp>
#include <lib_dialog.hpp>

#include <chain_prop.hpp>
#include <editor.hpp>

#include <util.hpp>

extern finestra_pr* MainWindow;

extern Preferences  __pref;


extern Fl_Pixmap img_save_file;

#include <plot_window.hpp>


using namespace spectra_plot;


void spectra_plot::save_as_native_file_cb(Fl_Widget* w, void* v){
  char* file_n=fl_file_chooser(_("Save Spectra As..."), "*.bist", NULL, 0);
  plot_window* pwin=static_cast<plot_window*>(v);

  if(pwin!=0){
    if(file_n!=NULL){
      string name=file_n;
      name=name.substr(name.find_last_of("/")+1);
      struct stat attrb;
      stat(file_n,&attrb);
      
      int write=1;
      
      if(errno!=ENOENT){
        write=ask_overwrite_file(name);
      }
    
      if(write){
        immagine   im=pwin->_plot;
        im.w_immagine(string(file_n));
      }
    }
  }
}


    
plot_window::plot_window(signal_type  type_plot)
  :Fl_Double_Window(def_w,def_h,title),
   _canvas(static_cast<int>(rintf(def_w*def_gap_w/100.0)),
           static_cast<int>(rintf(def_h*def_gap_h/100.0)),
           def_w - static_cast<int>(rintf(def_w*2*def_gap_w/100.0)),
           def_h - static_cast<int>(rintf(def_h*2*def_gap_h/100.0))),
   _save_file_b(PADD_BUTT+LARG_BUTTON_TOP,def_h-ALT_BUTT_TOP-PADD_BUTT,
                LARG_BUTTON_TOP,ALT_BUTT_TOP),
   _type_plot(type_plot)

{

  resizable(this);
  _canvas.add_immagine(ritorna_immagine());
  add(_canvas);

  _save_file_b.image(img_save_file);
  _save_file_b.callback(spectra_plot::save_as_native_file_cb);
  _save_file_b.user_data(this);
  _save_file_b.tooltip(_("Save as"));

  add(_save_file_b);


  end();

  set_modal();
  
  if(_type_plot==IR){
    _max_y=max_transmittance;
    _max_x=max_wave_no;
  }

}
    
plot_window::~plot_window(){
  
}

void plot_window::draw(){
  
  _plot.reset_all();
  draw_axis();
  draw_points();
  draw_groups();
  Fl_Double_Window::draw();
  
}

Fl_Scroll*  plot_window::ritorna_scroll(){
  return _placeholder;
}

immagine*   plot_window::ritorna_immagine(){
  return &_plot;
}

mol_canvas* plot_window::ritorna_mol_canvas(){
  return &_canvas;
}


void plot_window::draw_axis(){
  gruppo axis;
  float arr_w=(plot_window::def_gap_w/100*w())/5;
  float arr_h=(plot_window::def_gap_h/100*h())/5;
  float arr_gap=0;
  
  
  float x_ax_st=actual_plot_x();
  float y_ax_st=actual_plot_y();

  float x_ax_end=actual_plot_w();
  float y_ax_end=actual_plot_y();

  float y_ay_end=actual_plot_h();



  proc_arrow X(0,x_ax_st, y_ax_st,
               x_ax_end,y_ax_end,
               50,50,50, //GRAY
               0, 0, 0, 
               1, 0, ARR_NO_PUNT,
               arr_w, arr_h, arr_gap);


  proc_arrow Y(0,x_ax_st, y_ax_st,
               x_ax_st,y_ay_end,
               50,50,50, //GRAY
               0, 0, 0, 
               1, 0, ARR_NO_PUNT,
               arr_w, arr_h, arr_gap);


  axis.aggiungi_procedura(&X);
  axis.aggiungi_procedura(&Y);

  float y_now=0;
  for(int i=0;i<=axys_div;i++){
    int l_tick=1;
    if(i%4==0){
      l_tick*=2;
    }
    proc_arrow tick_y(0,x_ax_st-l_tick, remap_y(normalize_y(y_now)),
                      x_ax_st,remap_y(normalize_y(y_now)),
                      50,50,50, //GRAY
                      0, 0, 0, 
                      l_tick, 0, ARR_NO_PUNT,
                 arr_w, arr_h, arr_gap);

    y_now= i * _max_y/axys_div;
    
    axis.aggiungi_procedura(&tick_y);

  }

  float incr=_max_x/axys_div;

  int ct_tk=0;
  for(float i=0;i<=_max_x;i+=incr, ct_tk++){
    int l_tick=1;
    if(ct_tk%4==0){
      l_tick*=2;
    }
    
    proc_arrow tick_x(0,remap_x(normalize_x(i)),remap_y(normalize_y(0)),
                      remap_x(normalize_x(i)),remap_y(normalize_y(-l_tick)),
                      50,50,50, //GRAY
                      0, 0, 0, 
                      l_tick, 0, ARR_NO_PUNT,
                      arr_w, arr_h, arr_gap);

    
    axis.aggiungi_procedura(&tick_x);

  }


  _plot.aggiungi_gruppo(axis);


  draw_scale_y();
  draw_scale_x();
}


void plot_window::draw_scale_y(){
  if(_type_plot==IR){
    float y_now=0;
    for(int i=1;i<=axys_div;i++){
      int dim_std=(plot_window::def_gap_h/100*h())/5;;
      if(i%4==0){
        dim_std+=2;
      }

      etichetta et;
      et.dim(dim_std);
      ostringstream value_st;
      value_st << y_now;
      et.aggiungi(value_st.str(), ET_STR);
      et.x(actual_plot_x()-et.w()*2);
      et.y(remap_y(normalize_y(y_now) - et.h()/2));
      _plot.add_etich(et);
      y_now= i * _max_y/axys_div;
      
    }
        
  }else{ //TODO: OTHER PLOT

  }

}


void plot_window::draw_scale_x(){
  if(_type_plot==IR){
    float incr=_max_x/axys_div;
    int ct_tk=0;
    for(float i=0;i<=_max_x;i+=incr, ct_tk++){
      int dim_std=(plot_window::def_gap_h/100*h())/5;;
      if(ct_tk%4==0){
        dim_std+=2;
      }
      
      etichetta et;
      et.dim(dim_std);
      ostringstream value_st;
      value_st << _max_x- i;
      et.aggiungi(value_st.str(), ET_STR);
      et.x(remap_x(normalize_x(i) - et.w()/2));
      et.y(remap_y(normalize_y(-et.h())));
      _plot.add_etich(et);


    }
        
  }else{ //TODO: OTHER PLOT

  }

}

void plot_window::add_spike(float x, float y){
  plot_point p;
  p._type=SPIKE;
  p._x=x;
  p._y=y;
  _data.push_back(p);
}

void plot_window::add_group(gruppo to_add, float x, float y){
  to_add.trasla(-to_add.posx(),-to_add.posy());
  to_add.trasla(x,y);
  _groups.push_back(to_add);
}



void plot_window::draw_groups(){
  if(_type_plot==IR){
    for(unsigned i=0;i<_groups.size();i++){
      gruppo cp=_groups[i];
      float sc=0.9;
      float max=plot_window::def_gap_w/100*_canvas.w();

      if(cp.w()>0){
        if(cp.w()<max){
          sc=1.1;
          while(cp.w()<max &&
                cp.h()<max){
            cp.scale(sc);
          }
        }else{
          while(cp.w()>max &&
                cp.h()>max){
            cp.scale(sc);
          }
        }

        float old_x=_groups[i].posx();
        float old_y=_groups[i].posy();
        cp.trasla(-cp.posx(),-cp.posy());
        cp.trasla(remap_x(normalize_x(old_x,IR),IR),
                  remap_y(normalize_y(old_y)));
       
      }else{
        float old_x=_groups[i].posx();
        float old_y=_groups[i].posy();
        cp.trasla(-cp.posx(),-cp.posy());
        cp.trasla(remap_x(normalize_x(old_x+(plot_window::def_gap_w/100*w()),IR),IR),
                  remap_y(normalize_y(old_y)));
      }

      _plot.aggiungi_gruppo(cp);
    }
  }
}



void plot_window::draw_points(){
  if(_type_plot==IR){
    draw_points_IR();
  }
}


void plot_window::draw_points_IR(){
  gruppo grp;
  for(unsigned int i=0;i<_data.size();i++){
    float xpt=remap_x(normalize_x(_data[i]._x,IR),IR);
    float ypt=remap_y(normalize_y(_data[i]._y));
    float height=ypt-1;
    if(_data[i]._type==SPIKE){
      height=remap_y(normalize_y(0));
    }

    proc_arrow pt(0,xpt,ypt,
                  xpt,height,
                  0,0,0, //BLACK
                  0, 0, 0, 
                  1, 0, ARR_NO_PUNT,
                  1, 1, 0);
    grp.aggiungi_procedura(&pt);
  }

  _plot.aggiungi_gruppo(grp);
}

float plot_window::remap_y(float old){
  float res=actual_plot_y();

  if(_type_plot!=IR){
    res+=  actual_plot_h() - old; 
  }else{
    res+=old;
  }

  return res;
}


float plot_window::de_remap_y(float old){
  float res=-actual_plot_y();

  if(_type_plot!=IR){
    res-=  actual_plot_h() + old; 
  }else{
    res-=old;
  }

  return res;
}


float plot_window::remap_x(float old, signal_type t){
  float res=actual_plot_x();
  if(t==IR){
    res += old;
  }else{
    res+= old;
  }
  return res;
}


float plot_window::de_remap_x(float old){
  float res=-actual_plot_x();
  res-=old;
  return res;
}


float plot_window::normalize_y(float old){
  float res=0;
  if(_type_plot==IR){
    res=old * (actual_plot_h() - actual_plot_y())/_max_y;
  }
  return res;
}


float plot_window::de_normalize_y(float old){
  float res=0;
  if(_type_plot==IR){
    res=(old * _max_y)/ (actual_plot_h());
  }
  return res;
}


float plot_window::normalize_x(float old,signal_type t){
  float res=0;
  if(t==IR){
    res=(_max_x -old) * (actual_plot_w() - actual_plot_x())/_max_x;
  }else{
    res=old * (actual_plot_w() - actual_plot_x())/_max_x;
  }
  return res;
}


float plot_window::de_normalize_x(float old){
  float res=0;
  if(_type_plot==IR){
    res=(old * _max_x)/ (actual_plot_w() - actual_plot_x());
  }
  return res;
}


float plot_window::actual_plot_x(){
  return _canvas.x() + plot_window::def_gap_w/100*_canvas.w();
}
    
float plot_window::actual_plot_y(){

  return _canvas.y() + plot_window::def_gap_h/100*_canvas.h();
}

float plot_window::actual_plot_w(){
  return _canvas.w() - actual_plot_x();//2 * plot_window::def_gap_w/100*_canvas.w();
}

float plot_window::actual_plot_h(){
  return _canvas.h() - actual_plot_y();//2 * plot_window::def_gap_h/100*_canvas.h();
}


const int plot_window::def_w=800;
const int plot_window::def_h=600;

const float plot_window::max_transmittance=100;
const float plot_window::max_wave_no=4000;


const float plot_window::def_gap_w=5;
const float plot_window::def_gap_h=5;

const char* plot_window::title=_("Spectra prediction");

const float plot_window::axys_div=20;

const float plot_window::max_group_w=50;
