#include <string_tokenizer.hpp>
#include <stdio.h>
#include <iostream>
#include <string>
#include <curl/curl.h>

#include <regex.h>

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Progress.H>
#include <FL/Fl_Hold_Browser.H>



std::string choice_window(std::vector< std::pair< std::string,std::string > > links) {
  Fl_Double_Window* w = new Fl_Double_Window(480, 205, "Choose molecule");
  Fl_Hold_Browser* br= new Fl_Hold_Browser(25, 15, 430, 160,"Choose molecule");
  for(unsigned int i=0;i<links.size();i++){
    br->add(links[i].first.c_str());
  }
  w->end();
  w->set_modal();
  w->show();
  while(w->shown()){
    Fl::wait();
  }

  std::string res=links[0].second;
  if(br->value()>0 &&
     static_cast<unsigned int>(br->value()-1) < links.size()){
    res=links[br->value() -1 ].second;
  }
  delete br;
  delete w;

  return res;
}


class get_molfile{

protected:
  static std::string the_file;
  static std::string nist_host;

  static int my_progress_func(void* w,
			      double t, /* dltotal */
			      double d, /* dlnow */
			      double ultotal,
			      double ulnow)
  {
    
    Fl_Window* win=reinterpret_cast<Fl_Window*>(w);
    cout <<  "progress: dlnow" <<  d << " dltotal" 
	 << t << " "<< d*100.0/t<< "%" << endl;
    dynamic_cast<Fl_Progress*>(win->child(0))->maximum(static_cast<float>(400.0));
    dynamic_cast<Fl_Progress*>(win->child(0))->minimum(0);
    dynamic_cast<Fl_Progress*>(win->child(0))->value(static_cast<float>(d/t*400.0));
    win->redraw();
    Fl::check();
    return 0;
  }


  static int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
  {
    char* ch=static_cast<char*>(buffer);
    for(unsigned int i=0; i< nmemb ;i++){
      for(unsigned int i2=0;i2<size;i2++){
	get_molfile::the_file+=ch[i+i2];
      }
    }
    //  sleep(1);
    return nmemb;
  }
  
  static std::string get_curl_file(std::string the_url,Fl_Window* progressw){
    get_molfile::the_file="";
    CURL *curl;
    CURLcode res;
    curl_global_init(CURL_GLOBAL_ALL);
    
    curl = curl_easy_init();
    if(curl) {
      
      curl_easy_setopt(curl, CURLOPT_URL,
		       the_url.c_str());
      /* Define our callback to get called when there's data to be written */
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
      
      /* Switch on full protocol/debug output */
      curl_easy_setopt(curl, CURLOPT_VERBOSE, 1 );
      curl_easy_setopt(curl,CURLOPT_NOPROGRESS,0);
      curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
      curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progressw);
      
      
      res = curl_easy_perform(curl);
      
      
      /* always cleanup */
      curl_easy_cleanup(curl);
      
      if(CURLE_OK != res) {
	/* we failed */
	//fl_alert("Unable to get file from %s", the_url.c_str());
	return std::string("");
      }
    }
    /*
      cout << "***************************************" << endl;
      
      cout << the_file << endl;
    */
    curl_global_cleanup();
    return get_molfile::the_file;
  }




  static std::string get_2d_struct_file(std::string page,Fl_Window* progressw){
    std::string regexp_mol_file="/cgi/cbook.cgi?str2file=[a-z,0-9]\\+\"";
    regex_t weblink;
    regmatch_t result[1];
    std::string res="";

    regcomp(&weblink, regexp_mol_file.c_str(),REG_ICASE);
    int error=regexec(&weblink, page.c_str(),1, result, 0);
    if(error==0){
      std::string link=nist_host + page.substr(result[0].rm_so,(result[0].rm_eo-1)-result[0].rm_so);
      res=get_curl_file(link,progressw);
    }else{
      char error_re[72];
      regerror (error,&weblink,error_re,72); 
      fl_alert("Error fetching %s", error_re);
    }
    regfree(&weblink);
    
    return res;
  }

  
  static bool more_than_one_choice(std::string page){
    regex_t res_r;
    bool res=true;
    std::string regexp="[0-9]\\+ matching species were found";
    regcomp(&res_r, regexp.c_str(),REG_ICASE);
    int error=regexec(&res_r, page.c_str(),0, NULL, REG_NOTBOL|REG_NOTEOL);
    if(error!=0){
      res=false;
    }
    regfree(&res_r);
    return res;
  }

  static void get_all_choices(std::string page , std::vector< std::pair< std::string,std::string > >* links){
    if(page!=""){
      regex_t res_r;
      std::string regexp="\\(/cgi/cbook.cgi?ID=[0-9,a-z]\\+&amp;Units=SI\"\\)\\(>[^>]\\+\\)";
      regcomp(&res_r, regexp.c_str(),REG_ICASE);
      regmatch_t results[3];
      
      int error=regexec(&res_r, page.c_str(),3, results, REG_NOTBOL|REG_NOTEOL);
      
      if(error==0){
	std::string link=nist_host + page.substr(results[1].rm_so,(results[1].rm_eo-1)-results[1].rm_so);
	std::string name=page.substr(results[2].rm_so,(results[2].rm_eo-1)-results[2].rm_so);
	std::string line=link + std::string("") + name;
	std::pair<std::string,std::string> alink(name,link);
	links->push_back(alink);
	get_all_choices(page.substr(results[0].rm_eo-1),links);
	
      }
      regfree(&res_r);
    }
  }


public:

  
  static std::string get_web_molfile(std::string filename,Fl_Window* progressw, bool addhost=true){
    std::string urlfetch="";
    if(addhost){
      std::string delim=" ";
      string_tokenizer tok(filename , delim);
      std::string filename_enc="";
      if(filename.find(' ')!=std::string::npos){
	while(!tok){
	  filename_enc+=tok.next_token();
	}
      }else{
	filename_enc=filename;
      }
      urlfetch=nist_host + "cgi/cbook.cgi?Name="+ filename_enc + "&Units=SI";
    }else{
      urlfetch=filename;
    }

    std::string molfile="";
    std::string catalog=get_curl_file(urlfetch,progressw);

  
    if(more_than_one_choice(catalog)){
      std::vector< std::pair< std::string,std::string> > list;
      get_all_choices(catalog,&list);
      molfile=get_web_molfile(choice_window(list),progressw,false);
    }else{
      molfile=get_2d_struct_file(catalog,progressw);
    }  
  
  
    return molfile;
  }
  
};

std::string get_molfile::nist_host="http://webbook.nist.gov/";

std::string get_molfile::the_file="";
