// -*- mode: c++ -*- 
/* 

    GIFT, a flexible content based image retrieval system.
    Copyright (C) 1998, 1999, 2000 CUI, University of Geneva

    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 2 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, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
#include "libMRML/include/mrml_const.h" // for parsing
#include "libGIFTQuHtDig/include/CQHtDig.h"    // the header for this class
#include "libGIFTAcHtDig/include/CAcHtDig.h"   // the header for its accessor
#include "libMRML/include/CXMLElementBuilder.h" //building XML elements from a string

// for pipes
#include <errno.h>  // error number (man errno)
#include <string.h> // error string
#include <cstdio>   // c standard i/o (enspricht stdio.h)
#include <assert.h> // 

#include <unistd.h>

#include "libMRML/include/safePopen.h" 
/** 

    There is no special initialisation to do,
    so this function is empty

 */

void CQHtDig::init(){
  mCurrentPath.clear();
};

/**
 *
 * default constructor SHOULD NOT BE CALLED
 * 
 */
CQHtDig::CQHtDig(){
  assert(0);
};
/**
 * constructor
 * see CQuery
 */
CQHtDig::CQHtDig(CAccessorAdminCollection& inAccessorAdminCollection,
			 CAlgorithm&          inAlgorithm):
  CQuery(inAccessorAdminCollection,
	 inAlgorithm){
  {

    // mproxy has been filled in a reasonable way 
    // by CQuery::CQuery
    mAccessor=mAccessorAdmin->openAccessor("htdig");

    init();

    assert(mAccessor);
  }
};
    
/**
 *
 * destructor: at present empty
 * 
 */
CQHtDig::~CQHtDig(){

  cout << "destroying this "
       << __FILE__
       << __LINE__
       << flush
       << endl;

  //i thought i will need this, but at present I do not have this impression
  //it does not hurt, so we leave it in
};

/**  */
bool CQHtDig::setAlgorithm(CAlgorithm & inAlgorithm){
  if(mAlgorithm && mAlgorithm->getCollectionID()==inAlgorithm.getCollectionID()){
    
    return true;
    
  }else{
    //close the old collection, if exsisting
    if(mAccessorAdmin)
      mAccessorAdmin->closeAccessor("htdig");
    //
    mAccessorAdmin=&mAccessorAdminCollection->getProxy(inAlgorithm.getCollectionID());
    mAccessor=mAccessorAdmin->openAccessor("htdig");
    
    assert(mAccessor);
    //
    return (CQuery::setAlgorithm(inAlgorithm) && mAccessor);
  }
};

/**
 *
 *
 */
CIDRelevanceLevelPairList* CQHtDig::fastQuery(const CXMLElement& ,
					      int ,
					      double){
  
  assert(!"This should never be called");
}

/**
 *
 *
 */
CXMLElement* CQHtDig::query(const CXMLElement& inQuery){

  /*
    AS FOR ANY CALL TO QUERY
   */
  for(list<CXMLElement*>::const_iterator i=inQuery.child_list_begin();
      i!=inQuery.child_list_end();
      i++){// iterating over all the children of the inQuery element.

     cout << "I:The name of this tree element: "
	  << endl
	  << (*i)->getName()
	  << endl;
     if((*i)->getName()==mrml_const::cui_text_query){
       // if the current child is a user-relevance-element
       if((*i)->stringReadAttribute(mrml_const::query_string).first){

	 string lQueryString=(*i)->stringReadAttribute(mrml_const::query_string).second;

	 for(string::iterator j=lQueryString.begin();
	     j!=lQueryString.end();
	     j++){
	   if(*j=='"'){
	     *j=' ';
	   }
	 }
	 
	 cout << __FILE__ << ":" << __LINE__ <<": Our query string is " << lQueryString << endl;

	 // these strings hold the important parameters
	 string lHtSearchLocation;
	 string lOption;
	 string lCGIStyleQuery;
	 string lNoQuoteQuery;

	 {
	   // cast from general accessor to htdig
#ifdef GOT_DYNAMIC_CAST_TO_WORK
	   CAcHtDig* lAccessor=dynamic_cast<CAcHtDig*>(mAccessor);
#else
	   CAcHtDig* lAccessor=(CAcHtDig*)(mAccessor);
#endif
	   if(lAccessor && bool(*lAccessor)){
	      lHtSearchLocation=lAccessor->getHtSearchLocation();
	      lOption=  string("-c")+lAccessor->getConfigFileLocation();
	      lCGIStyleQuery= string("words=\"")+lQueryString+"\"";	 
	      lNoQuoteQuery= string("words=")+lQueryString+"";	 
	     
	   }else{

	     cerr << __FILE__ << ":" << __LINE__ << ": Misconfiguration!" << endl;

	     CXMLElement* lReturnValue(new CXMLElement(mrml_const::error,0));
	     if(lAccessor){
	       lReturnValue->addAttribute(mrml_const::message,
					  string("Misconfiguration of htdig plugin:")
					  +string("Config file:")
					  +lAccessor->getConfigFileLocation()
					  +string("\n")
					  +string("HtSearch executable:")
					  +lAccessor->getHtSearchLocation());
	     }else{
	       lReturnValue->addAttribute(mrml_const::message,
					  string("CQHtDig.cc:187: Probable programmming error in htdig plugin: dynamic cast yields zero!")
					  );
	     }
	     return  lReturnValue;
	   }
	 }

	 CXMLElementBuilder lBuilder;

	   // to put it bluntly, using popen is a security leak
	   // unless you're veryvery careful. This is why we 
	   // created a safer version of popen, safePopen, that is
	   // based on execve instead of popen
#ifdef _USE_POPEN
	 // we open pipe to htdig
	 // htdig writes query result as XML to the pipe
	 // we can read from the pipe like from an input file

	 // this string will hold the string we send to the shell to call htdig
	 string lCallString;
	 cout << __FILE__ << ":" << __LINE__ << ": The program we run is " << lCallString << endl;
	 lCallString=lHtSearchLocation+" "+lOption+" "+lCGIStyleQuery;
	 FILE* lInPipe=popen(lCallString.c_str(), "r");
	 if(lInPipe != NULL){
	   int lCounter(0);
	   
	   puts("Input pipe successfully opened.");
	   string lQueryResult;
	   int lCharacter= fgetc(lInPipe);
	   // skip till the "<" of "<?xml"
	   while((EOF != lCharacter)
		 && ('<' != lCharacter)){
	     lCharacter= fgetc(lInPipe);
	   }
	   cout << "till here" << endl;
	   //lQueryResult.push_back('<');
	   while(EOF != lCharacter){
	     // testen, ob das gelesene Zeichen dem
	     // entspricht, was wir erwarten
	     lQueryResult.push_back(char(lCharacter));
	     putchar(lCharacter);
	     
	     // neues Zeichen lesen
	     lCharacter=fgetc(lInPipe);
	   }
	   return lBuilder.stringToElement(lQueryResult);
	 }else{
	   CXMLElement* lReturnValue(new CXMLElement(mrml_const::error,0));
	   lReturnValue->addAttribute(mrml_const::message,"popen:Could not open pipe");
	   return  lReturnValue;
	 }
#else
	 // i.e. use safepopen
	 cout << __FILE__ << ":" << __LINE__ << ": The program we run is " 
	      << lHtSearchLocation << ":" 
	      << lOption << ":" 
	      << lNoQuoteQuery << ":" << endl;
	 
	 // we open SAFE pipe to htdig
	 // htdig writes query result as XML to the pipe
	 // we can read from the pipe like from an input file
	 char** lArgv=new char*[4];
	 lArgv[0]=(char*)lHtSearchLocation.c_str();
	 lArgv[1]=(char*)lOption.c_str();
	 lArgv[2]=(char*)lNoQuoteQuery.c_str();
	 lArgv[3]=0;
	   
	 int lInputPipe=safePopen((char*const*)lArgv);
	 
	 if(lInputPipe){
	   string lQueryResult;
	   char lBuffer[2];
	   lBuffer[1]=char(0);
	   int lRead;
	   bool lXMLHasBegun=false;
	   while((lRead=read(lInputPipe,lBuffer,1))>0){
	     if(lBuffer[0]=='<'){
	       lXMLHasBegun=true;
	     }
	     if(lXMLHasBegun){
	       lQueryResult.push_back(lBuffer[0]);
	     }
	   }
	   cout << "[" << lQueryResult << "]" << endl;
	   if(lRead<0){
	     cout << strerror(errno) << endl;
	   }
	   cout << "END" << endl;
	   delete[] lArgv;
	   return lBuilder.stringToElement(lQueryResult);
	 }else{
	   CXMLElement* lReturnValue(new CXMLElement(mrml_const::error,0));
	   lReturnValue->addAttribute(mrml_const::message,"safePopen:Could not open pipe");
	   return  lReturnValue;
	 }
#endif
       }
     }
  }

  // we only get till here if all else failed...
  CXMLElement* lReturnValue=new CXMLElement(mrml_const::query_result);

  return  lReturnValue;
};

