// -----------------------------------------------------------------------------
// File SEARCH_ENGINE_API.CPP
//
// (c) by Koziev Elijah
//
// Content:
// SOLARIX Intellectronix Project   http://www.solarix.ru
// SEARCH ENGINE Core
//
// Win32 DLL интерфейс к поисковой машине.
// Old-fashioned Win32 DLL interface to the search engine.
//
// 24.01.2006 - API переделан, введен хэндл для создаваемого объекта ГМ
//
// 24.01.2006 - API fuctions take wchar_t strings.
//
// 29.05.2006 - в вызовах API введен параметр 'HFAIND' - хэндл созданного
//              поискового движка.
//
// 06.08.2006 - изменен API - callback функция "начало обработки каталога" в
//              качестве аргумента получает UNICODE имя каталога
//
// 20.08.2006 - добавлена sol_Translate_To_Noun для приведения слов к форме
//              существительных.
//
// 25.08.2006 - добавлена процедура sol_Project_Word для получения всех
//              проекций слова на лексикон, а также группа процедур для работы
//              с получившимся списком координат проекций. 
//
// 30.08.2006 - добавлены процедуры для синтаксического анализа и работы с 
//              получающейся пачкой решений. 
//
// 08.10.2006 - в версию Pro добавлены процедуры API для работы с RegexEx -
//              расширенными регулярными выражениями.
//
// 15.10.2006 - введен низкоуровневый API для работы с индексатором.
//
// 21.11.2006 - подключен стеммер для нормализации.
//
// 25.11.2006 - изменена sol_GetError - возвращает wchar_t строку описания
//              ошибки. 
//
// 08.05.2007 - sol_SetCallback_xxx возвращают код ошибки.
//
// 12.07.2007 - введены процедуры sol_GenerateXml и sol_NoResults.
//
// 08.06.2008 - если вызвать sol_ReadIni с указанием пустой строки в
//              качестве имени файла, то будет выполнена инициализация
//              движка по умолчанию. 
// 17.07.2008 - перевод API call convention на stdcall
// 01.08.2008 - все возвращаемые bool'ы переделаны на int'ы
// -----------------------------------------------------------------------------
//
// CD->01.03.2005
// LC->10.07.2010
// --------------


#include <lem/config.h>

#if defined FAIND_IENGINES || defined DLL_EXPORTS

#include <lem/conversions.h>
#include <lem/startup.h>
#include <lem/ustreams.h>
#include <lem/unicode.h>
#include <lem/logfile.h>
#include <lem/solarix/version.h>
#include <lem/solarix/dictionary.h>
#include <lem/solarix/sg_autom.h>
#include <lem/solarix/la_autom.h>
#include <lem/solarix/se_indexer.h>
#include <lem/solarix/search_engine.h>
#include <lem/solarix/DocumentTagsCache.h>
#include <lem/solarix/sentencebroker.h>
#include <lem/solarix/faind_internal.h>


using namespace lem;
using namespace lem::Char_Stream;
using namespace Solarix;
using namespace Solarix::Search_Engine;


#if LEM_DEBUGGING==1

// ********************************************
// Debug tools: writing messages to logfile.
// ********************************************
static void WriteLog( HFAIND hEngine, const char *Message )
{
 if( lem::LogFile::IsOpen() )
  lem::LogFile::logfile->printf( "DEBUG: %s\n", Message );
 return;
}

static void WriteLogW( HFAIND hEngine, const wchar_t *Message, const wchar_t *Str2 )
{
 if( lem::LogFile::IsOpen() )
  {
   if( Str2 )
    lem::LogFile::logfile->printf( "DEBUG: %us %us\n", Message, Str2 );
   else
    lem::LogFile::logfile->printf( "DEBUG: %us\n", Message );
  }
  
 return;
}

#define LOG(hEngine,Message)      WriteLog(hEngine,Message)
#define LOGW(hEngine,Message,Str) WriteLogW(hEngine,Message,Str)

#else

#define LOG(hEngine,Message) {}
#define LOGW(hEngine,Message,Str) {}

#endif


#define ENGINE ((Faind_Engine*)hEngine)
#define HCMD ((Solarix::Faind_Executor*)hCmd)
#define HPARAMS ((Solarix::Search_Engine::QueryParams*)hParams)
#define HXML ((Solarix::XmlText*)hXml)


#define CATCH_ALL(ret) catch( const lem::E_BaseException& x ) \
  { \
   ENGINE->error = UFString(x.what()); \
   return ret; \
  } \
 catch( const std::exception& x ) \
  { \
   ENGINE->error = x.what(); \
   return ret; \
  } \
 catch(...) \
  { \
   ENGINE->error = L"Unhandled error"; \
   return ret; \
  }



static int LoadDefaultConfig( HFAIND hEngine, bool ram_cataloguer )
{
 lem::FString ini(
 "[general]\n"
 "work_dir = \"%app%\\\\Faind\"\n"
 "work_dir_linux = \"~/Faind\"\n"
 "raw_reader_kb = \"%work%\\\\raw_reader_knowledgebase\"\n"
 "allow_plugins = true\n"
 "plugins_path = \"%exe%\\\\plugins\"\n"
 "multimedia_files = \"aif;au;cda;med;mid;midi;mod;mpa;mp3;ogg;ra;rmi;rmx;rv;s3m;sfx;sid;snd;spc;voc;vvs;wav;wma;xm;asf;avi;divx;mpeg;mpg;mpg4;mov;rm;vob;wmv;xvid;mp2;mp1;ani;bmp;cdr;clp;cgm;cur;dib;djvu;drw;dxf;emf;eps;gif;ico;icl;iff;img;jpg;jpeg;lbm;ldf;lwf;pbm;pcd;pcx;pgm;pic;pct;png;ppm;psd;psp;qtif;ras;rgb;rle;sfw;tga;tif;tiff;wmf;wpg\"\n"
 "save_files = true\n"

 "[ui]\n"
 "lang = \"\"\n"
 "session_codepage = \"\"\n"
 "tty_codepage = \"\"\n"

 "[defaults]\n"
 "minbound = 1.0\n"
 "skip_word = 0.01\n"
 "recurse = true\n"
 "allow_raw = false\n"
 "lexems_margin = 8\n"
 "maxhitcount = 100\n"

 "[echo]\n"
 "show_context = true\n"
 "verbose = true\n"
 "show_query = false\n"
 "logfile = \"\"\n"
 "internal_browser = true\n"

 "[dictionary]\n"
 "file = \"dictionary.xml\"\n"
 "allow_rooting = false\n"
 "preload = false\n"
 "languages = *\n"
 "topics = \"%work%\\\\Topics\" \n"
 "allow_stopwords = true\n"

 "[internet]\n"
 "proxy = \"auto\"\n"
 "proxy_bypass = \"<local>\"\n"
 "passive_ftp = true\n"
 "org_domain = true\n"
 "href = false\n"
 "store_download = false\n"
 "download_dir = \"%work%\\\\Download\"\n"
 "start_httpd = false\n"
 "httpd_ip = \"127.0.0.1\"\n"
 "httpd_port = 10973\n" 

 "[index]\n"
 "storage = \"%work%\\\\Index\"\n"
 "use = true\n"
 "db_host = \"127.0.0.1\"\n"
 "db = \"faind\"\n"
 "db_login = \"faind\"\n"
 "db_psw = \"faind\"\n"
 "monitor_folders = false\n"
 "allow_clucene_wr = true\n"
 "allow_mysql_wr = true\n" );

 if( ram_cataloguer )
  ini += "ram_cataloguer = true\n";
 
 
 lem::MemReadStream memini( ini.c_str() );
 int ret = ENGINE->parser->Read_Ini( memini ) ? 0 : -1;


 if( ret==0 )
  ENGINE->PostINI();
  

 return ret;
}




// ****************************************************************
// Начальная инициализация движка - словари загружаются отдельно,
// явным вызовом специальной процедуры sol_LoadDictionary, либо
// автоматически поисковым движком перед поиском/индексированием
// с морфологией.
//
// Может вернуть NULL при неудачной попытке создать объект.
// ****************************************************************
FAIND_API(HFAIND) sol_CreateSearchEngine(void)
{
 try
  {
   lem::Init();
   Faind_Engine *e = new Faind_Engine();

   if( e->ok )
    return e;

   delete e;
   return NULL;
  }
 catch(...)
  {
   return NULL;
  }
}




FAIND_API(HFAIND) sol_CreatePortableSearcher( int flags )
{
 try
  {
   lem::Init();
   Faind_Engine *e = new Faind_Engine();

   e->ram_cataloguer=true;
   LoadDefaultConfig(e,true);

   if( e->ok )
    return e;

   delete e;
   return NULL;
  }
 catch(...)
  {
   return NULL;
  }
}


FAIND_API(int) sol_DeleteSearchEngine( HFAIND hEngine )
{
 try
  {
   delete ENGINE;
   return 1;
  }
 catch(...)
  {
   return 0;
  }
}


FAIND_API(int) sol_DeleteGrammarEngine( HFAIND hEngine )
{
 return sol_DeleteSearchEngine(hEngine);
}



// ************************************************
// Read the configuration from ini file
// Return:
//        0  - ok
//        -1 - error
// ************************************************
FAIND_API(int) sol_ReadIniA( HFAIND hEngine, const char *ini_filename )
{
 if( hEngine==NULL || !ENGINE->ok || ENGINE->ini_loaded )
  return -1;

 if( ini_filename==NULL || !*ini_filename )
  return LoadDefaultConfig( ENGINE, ENGINE->ram_cataloguer);

 return sol_ReadIniW( ENGINE, to_unicode(ini_filename).c_str() );
}


FAIND_API(int) sol_ReadIniW( HFAIND hEngine, const wchar_t *ini_filename )
{
 if( hEngine==NULL || !ENGINE->ok || ENGINE->ini_loaded )
  return -1;

 if( ini_filename==NULL || !*ini_filename )
  return LoadDefaultConfig(ENGINE,ENGINE->ram_cataloguer);

 // Загружаем настройки по умолчанию из ini-файла
 lem::Path ini_name( ini_filename );
 int ret = ENGINE->parser->Read_Ini( ini_name ) ? 0 : -1;

 if( ret==0 )
  ENGINE->PostINI();

 return ret;
} 




FAIND_API(int) sol_LoadDictionaryW( HFAIND hEngine, const wchar_t *Filename )
{
 if( lem::lem_is_empty(Filename) || hEngine==NULL )
  return 0;

 int ok=0;

 try
  {
   ENGINE->parser->generals->dictionary_filename = lem::Path(Filename);
   ENGINE->parser->LoadDictionary(false);
   ok = ENGINE->parser->template_crawler->CheckDictionary() ? 1 : 0;
  }
 catch(...)
  {
  } 

 return ok;
}


FAIND_API(int) sol_LoadDictionaryA( HFAIND hEngine, const char *Filename )
{
 return sol_LoadDictionaryW( ENGINE, to_unicode(Filename).c_str() );
}


// *********************************************************************
// Инициализация грамматического движка, в частности - загрузка словаря
// из указанного в ini-файле (sol_ReadIni) источника.
// *********************************************************************
FAIND_API(int) sol_LoadGrammarEngine( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return -1;

 try
  {
   ENGINE->parser->LoadDictionary(false);
   ENGINE->dict = lem::Ptr<Solarix::Dictionary>( &ENGINE->parser->GetDictionary(), null_deleter() );
   ENGINE->seeker = lem::Ptr<Solarix::LD_Seeker>( &ENGINE->parser->GetSeeker(), null_deleter() );
   ENGINE->sg = &ENGINE->parser->GetDictionary().GetSynGram();

   #if LEM_DEBUGGING==1 
   int ne = ENGINE->dict->GetSynGram().GetnEntry();
   int nf = ENGINE->dict->GetSynGram().Count_Forms();

    #if defined SOLARIX_PRO && !defined SOL_NO_NGRAMS
    int nn = ENGINE->dict->GetNgrams()->TotalCount();
    #endif

   #endif


   #if defined SOLARIX_PRO && !defined SOL_NO_AA
   ENGINE->fuzzy = ENGINE->parser->template_crawler->GetFuzzyComparator();
   ENGINE->tpu = ENGINE->parser->template_crawler->GetSyntaxAnalyzer();
   #endif 
  }
 catch(...) 
  {
   return -1;
  } 

 return 0;
} 




// ************************************************************************
// Начинаем трассировать активность поисковика в указанный текстовый файл
// ************************************************************************
FAIND_API(int) sol_OpenLogFileA( HFAIND hEngine, const char *logfilename )
{
 if( hEngine==NULL || !logfilename || !*logfilename || !ENGINE->ok )
  return -1;

 return sol_OpenLogFileW( hEngine, to_unicode(logfilename).c_str() );
}

FAIND_API(int) sol_OpenLogFileW( HFAIND hEngine, const wchar_t *logfilename )
{
 if( hEngine==NULL || !logfilename || !*logfilename || !ENGINE->ok )
  return -1;

 if( lem::LogFile::IsOpen() )
  // Already open.
  return -2;

 try
  {
   lem::LogFile::Open(lem::Path(logfilename));
   return 0;
  }
 catch(...)
  {
   return -1;
  }
}


FAIND_API(void) sol_CloseLogFile( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return;

 lem::LogFile::Close();
 return;
}




FAIND_API(void) sol_GenerateHtml( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return;

 ENGINE->Generate_Html();
 return;
}

FAIND_API(void) sol_GenerateXml( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return;

 ENGINE->Generate_Xml();
 return;
}


// Result page is not needed.
FAIND_API(void) sol_NoResults( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return;

 ENGINE->Dont_Generate_Results();
 return;
}



FAIND_API(void) sol_StripHtml( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return;

 ENGINE->Strip_Html();
 return;
}



// ********************************************************************
//
// Execute the command query_str. 
//
// 'text' is optional text to be search in (NULL when searching in
// disk files). 
//
// Return:
//  Executor descriptor or NULL is error occured.
// ********************************************************************

static HFAINDCMD ParseQuery(
                            HFAIND hEngine,
                            const wchar_t *query,
                            const Solarix::Search_Engine::QueryParams &params,
                            Type_Dataset dataset,
                            bool is_sql
                           )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok )
  return NULL;

 Faind_Executor *executor = NULL;

 LOGW(hEngine,L"Entering sol_ParseQuery",query);

 try
  {
   executor = new Faind_Executor;

   executor->cmd = is_sql ? ENGINE->parser->ParseSqlQuery(query,params) : ENGINE->parser->Parse(query,params);

/*
   if( text!=NULL )
    {
     ENGINE->parser->Set_Src_Stream(text);
    }
*/

   executor->cmd->crawler->engine_command->echo.verbose=false;

   if( dataset!=No_Dataset )
    {
     // Настраиваем исполнитель для сохранения результатов во внутренний буфер
     switch( ENGINE->dataset_flag )
     { 
      case Xml_Dataset:
       {
        // UTF-8
//        OUFormatter *stream = new OUFormatter( &executor->dump, false );	 
//      stream->SetOutCP( &lem::UI::get_UI().GetSessionCp() );

        UTF8_Reader *utf8 = new UTF8_Reader( lem::Ptr<Stream>(&executor->dump,null_deleter()) );
        OUFormatter *stream = new OUFormatter( utf8, true );	 
        stream->SetOutCP( &lem::UI::get_UI().GetSessionCp() );
        utf8->SetEncoding( &lem::UI::get_UI().GetSessionCp() );

        Collect<FString> tunes;
        Base_DataSet_Printer *dataset = new Xml_DataSet_Printer( &*ENGINE->parser->generals, ENGINE->parser->GetResources(), stream, tunes ); 
        executor->cmd->crawler->engine_command->action.res_dataset = dataset;
        break;
       }      

      case Html_Dataset:
       {
        UTF8_Reader *utf8 = new UTF8_Reader( lem::Ptr<Stream>(&executor->dump,null_deleter()) );
        OUFormatter *stream = new OUFormatter( utf8, true );	 
        stream->SetOutCP( &lem::UI::get_UI().GetSessionCp() );
        utf8->SetEncoding( &lem::UI::get_UI().GetSessionCp() );

//        OUFormatter *stream = new OUFormatter( &executor->dump, false );	 

        Collect<FString> tunes;
        tunes.push_back( "htmlres_2" );

        if( !ENGINE->html_header_footer )
         tunes.push_back( "html_body" );
 
        Base_DataSet_Printer *dataset = new Html_DataSet_Printer( &*ENGINE->parser->generals, ENGINE->parser->GetResources(), stream, tunes ); 
        executor->cmd->crawler->engine_command->action.res_dataset = dataset;
        break;
       }

      default:
       LEM_STOPIT; 
     }
    } 
  }
 CATCH_ALL(NULL)

 LOG(hEngine,"Leaving sol_ParseQuery");
 return executor;
}



FAIND_API(HFAINDCMD) sol_ParseQuery( HFAIND hEngine, const wchar_t *query )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok )
  return NULL;

 Solarix::Search_Engine::QueryParams empty_params;
 return ParseQuery( hEngine, query, empty_params, ENGINE->dataset_flag, false );
}


FAIND_API(HFAINDCMD) sol_ParseSqlQuery( HFAIND hEngine, const wchar_t *query )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok )
  return NULL;

 Solarix::Search_Engine::QueryParams empty_params;
 return ParseQuery( hEngine, query, empty_params, ENGINE->dataset_flag, true );
}


FAIND_API(int) sol_ExecuteQuery( HFAIND hEngine, HFAINDCMD hCmd )
{
 if( hEngine==NULL || hCmd==NULL || !ENGINE->ok )
  return -1;

 LOGW(hEngine,L"Entering sol_ExecuteQuery",L"");

 int retval=-1;

 try
  {
   ENGINE->error.clear();

   // Теперь выполняем команду.
   HCMD->cmd->Do();

   if( lem::LogFile::IsOpen() )
    {
     // Печатаем результаты исполнения команды в лог-файл
     HCMD->cmd->PrintResults( *lem::LogFile::logfile );
    }

   HCMD->result = new UFString(HCMD->dump.string().c_str());
   retval = 0;
  }      
 CATCH_ALL(-1)

 LOG(hEngine,"Leaving sol_ExecuteQuery");
 return retval;
}



// *****************************************************************************
// Парсинг и исполнение команды за один вызов. Удобно для выполнения команд DDL,
// например для удаления индекса.
// *****************************************************************************
FAIND_API(int) sol_Execute( HFAIND hEngine, const wchar_t *query )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok )
  return -1;

 Solarix::Search_Engine::QueryParams empty_params;
 HFAINDCMD hCmd = ParseQuery( ENGINE, query, empty_params, No_Dataset, false );
 if( hCmd==NULL )
  return -1;

 int res = sol_ExecuteQuery( hEngine, hCmd );
 sol_DeleteQuery(hCmd);
 return res;
}


FAIND_API(int) sol_ExecuteSql( HFAIND hEngine, const wchar_t *query )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok )
  return -1;

 Solarix::Search_Engine::QueryParams empty_params;
 HFAINDCMD hCmd = ParseQuery( ENGINE, query, empty_params, No_Dataset, true );
 if( hCmd==NULL )
  return -1;

 int res = sol_ExecuteQuery( hEngine, hCmd );
 sol_DeleteQuery(hCmd);
 return res;
}


FAIND_API(int) sol_ExecuteWithParams( HFAIND hEngine, const wchar_t *query, HFAINDPARAMS hParams )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok || hParams==NULL )
  return -1;

 HFAINDCMD hCmd = ParseQuery( hEngine, query, *HPARAMS, No_Dataset, false );
 if( hCmd==NULL )
  return -1;

 int res = sol_ExecuteQuery( hEngine, hCmd );
 sol_DeleteQuery(hCmd);
 return res;
}

FAIND_API(int) sol_ExecuteSqlWithParams( HFAIND hEngine, const wchar_t *query, HFAINDPARAMS hParams )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok || hParams==NULL )
  return -1;

 HFAINDCMD hCmd = ParseQuery( ENGINE, query, *HPARAMS, No_Dataset, true );
 if( hCmd==NULL )
  return -1;

 int res = sol_ExecuteQuery( hEngine, hCmd );
 sol_DeleteQuery(hCmd);
 return res;
}

// ************************************************************
// Вызывается после sol_ExecuteQuery для определения размера
// буфера результатов.
// ************************************************************
FAIND_API(int) sol_GetResultLen( HFAINDCMD hCmd )
{
 if( hCmd==NULL )
  return -1;

 return HCMD->GetResultLen();
}


// ************************************************************
// Вызывается после sol_ExecuteQuery для копирования строки
// результатов в пользовательский буфер.
// ************************************************************
FAIND_API(void) sol_GetResult( HFAINDCMD hCmd, char *buffer )
{
 if( hCmd==NULL || buffer==NULL )
  return;

 *buffer=0;

 try
  {
   HCMD->GetResult(buffer);
  }
 catch(...) 
  {
  }
}

FAIND_API(void) sol_GetResultW( HFAINDCMD hCmd, wchar_t *buffer )
{
 if( hCmd==NULL || buffer==NULL )
  return;

 *buffer=0;

 try
  {
   HCMD->GetResult(buffer);
  }
 catch(...) 
  {
  }
}


FAIND_API(void) sol_DeleteQuery( HFAINDCMD hCmd )
{
 delete HCMD;
 return;
}
 

FAIND_API(int) sol_SetCallback_StartFile( HFAIND hEngine, EngineCallbackProc_StartFile ptrFuction )
{
 if( hEngine==NULL || !ENGINE->ok )
  return -1;

 return ENGINE->Set_Callback_StartFile( ptrFuction ) ? 0 : -1;
}


FAIND_API(int) sol_SetCallback_StartFolder( HFAIND hEngine, EngineCallbackProc_StartFolder ptrFuction )
{
 if( hEngine==NULL || !ENGINE->ok )
  return -1;

 return ENGINE->Set_Callback_StartFolder( ptrFuction ) ? 0 : -1;
}


FAIND_API(int) sol_SetCallback_Success( HFAIND hEngine, EngineCallbackProc_Success ptrFuction )
{
 if( hEngine==NULL || !ENGINE->ok )
  return -1;

 return ENGINE->Set_Callback_Success( ptrFuction ) ? 0 : -1;
}



// ****************************************************
// Количество актуальных индексов
// ****************************************************
FAIND_API(int) sol_GetDomainsListSize( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return -1;

 #if defined FAIND_INDEXER
 Solarix::Search_Engine::Idb_List &idb_list = ENGINE->parser->GetIndexManager().GetDomains();

 // Загружаем список в оперативку, если еще не загружен
 idb_list.Load();

 int len=0;
 for( lem::Container::size_type i=0; i<idb_list.Count(); i++ )
  if( idb_list.IsActual(CastSizeToInt(i)) )
   len += idb_list.GetDomainName(CastSizeToInt(i)).length()+1; // один символ для разделения имен


 len++; // одна позиция для терминатора

 return len;
 #else
 return -1;
 #endif
}


// ***********************************************************************
// Получаем список актуальных индексов в виде одной строки с задаваемым
// символом-разделителем.
// ***********************************************************************
FAIND_API(int) sol_GetDomainsList( HFAIND hEngine, wchar_t Delimiter, wchar_t *Buffer )
{
 if( hEngine==NULL || Buffer==NULL || Delimiter==0 || !ENGINE->ok )
  return -1;

 #if defined FAIND_INDEXER
 Solarix::Search_Engine::Idb_List &idb_list = ENGINE->parser->GetIndexManager().GetDomains();

 // Загружаем список в оперативку, если еще не загружен
 idb_list.Load();

 int n_idx=0;
 int end_pos=0;
 *Buffer=0;

 const int n = CastSizeToInt(idb_list.Count());
 for( int i=0; i<n; i++ )
  if( idb_list.IsActual(i) )
   {
    const UFString &name = idb_list.GetDomainName(i);
    lem_strcpy( Buffer+end_pos, name.c_str() );
    end_pos += name.length(); 
    Buffer[end_pos++] = Delimiter;
    Buffer[end_pos]=0;
    n_idx++;
   }

 if( n_idx>0 )
  // Уберем последний символ-разделитель.
  Buffer[end_pos-1]=0;

 return n_idx;
 #else
 return -1;
 #endif
}


FAIND_API(int) sol_XmlTextLen( HFAINDXML hXml )
{
 if( hXml==NULL )
  return -1;

 LEM_CHECKIT_Z(hXml!=NULL);
 return CastSizeToInt(HXML->GetLen());
}




FAIND_API(int) sol_GetXmlText( HFAINDXML hXml, char *Buffer, int BufferSize )
{
 if( hXml==NULL || Buffer==NULL || BufferSize==0 )
  return -1;

 LEM_CHECKIT_Z(hXml!=NULL);
 return HXML->GetChars(Buffer,BufferSize);
}


FAIND_API(void) sol_DeleteXml( XmlText *hXml )
{
 delete HXML;
}



// *******************************************************
// Возвращается список описаний индексов в формате XML
// *******************************************************
FAIND_API(HFAINDXML) sol_GetDomainsListXml( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return NULL;

 //LOG(hEngine,__FUNCTION__);

 #if defined FAIND_INDEXER
 XmlText *res = new XmlText;
 res->out->printf( "<xml encoding=\"utf8\">\n<indexes>\n" );
 ENGINE->parser->GetIndexManager().GetDomains().PrintList( *res->out, ENGINE->parser->GetResources(), true );
 res->out->printf( "</indexes>\n</xml>\n" );

 LOG(hEngine,"exit");
 return res;
 #else
 return NULL;
 #endif
}


// ***********************************************************************
// Возвращает указатель на строку с комментарием к индексу. Внешний код
// должен сразу же скопировать содержимое строки в свой буфер, так как
// комментарий может измениться в любой момент.
// ***********************************************************************
FAIND_API(const wchar_t*) sol_GetDomainComment(
                                               HFAIND hEngine,
                                               const wchar_t *Domain_Name
                                              )
{
 if( hEngine==NULL || Domain_Name==NULL || *Domain_Name==0 || !ENGINE->ok )
  return NULL;

 #if defined FAIND_INDEXER
// LOG(hEngine,__FUNCTION__);

 Solarix::Search_Engine::Idb_List &idb_list = ENGINE->parser->GetIndexManager().GetDomains();

 // Загружаем список в оперативку, если еще не загружен
 idb_list.Load();

 const int izone = idb_list.Find(Domain_Name);
 if( izone==UNKNOWN )
  return NULL;

 LOG(hEngine,"exit");

 return idb_list.GetUnsafe(izone).GetComment().c_str();
 #else
 return NULL;
 #endif
}


// *************************************************
// Возвращает число актуальных индексов.
// *************************************************
FAIND_API(int) sol_CountDomains( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return -1;

 #if defined FAIND_INDEXER
 Solarix::Search_Engine::Idb_List &idb_list = ENGINE->parser->GetIndexManager().GetDomains();

 // Загружаем список в оперативку, если еще не загружен
 idb_list.Load();

 int count=0;

 const int n = CastSizeToInt(idb_list.Count());
 for( int i=0; i<n; i++ )
  if( idb_list.IsActual(i) )
   count++;

 return count;
 #else
 return -1;
 #endif
}


// *********************************************************************
// Возвращает внутренний id индекса по его имени. Если индекса нет, или
// он удален, то возвращается -1.
// 
// Search for the index by its name.
// Return value:
//              -2   error  
//              -1   index not found
//              >=0  internal index ID
// *********************************************************************
FAIND_API(int) sol_FindDomain( HFAIND hEngine, const wchar_t *Domain_Name )
{
 if( hEngine==NULL || !ENGINE->ok || Domain_Name==NULL || *Domain_Name==0 )
  return -2;

 #if defined FAIND_INDEXER
 Solarix::Search_Engine::Idb_List &idb_list = ENGINE->parser->GetIndexManager().GetDomains();

 // Загружаем список в оперативку, если еще не загружен
 idb_list.Load();

 const int n = CastSizeToInt(idb_list.Count());
 for( int i=0; i<n; i++ )
  if( idb_list.IsActual(i) && idb_list.GetDomainName(i) == Domain_Name )
   return i;

 #endif

 return UNKNOWN;
}


FAIND_API(HFAINDPARAMS) sol_CreateParams( HFAIND hEngine )
{
 if( hEngine==NULL || !ENGINE->ok )
  return NULL;

 return new Solarix::Search_Engine::QueryParams();
}

FAIND_API(int) sol_DeleteParams( HFAINDPARAMS hParams )
{
 if( hParams==NULL )
  return -1;

 try
  {
   delete (Solarix::Search_Engine::QueryParams*)hParams;
  }
 catch(...)
  {
   return -1;
  }

 return 0;
}

FAIND_API(int) sol_AddParam( HFAINDPARAMS hParams, const wchar_t *ParamName, const wchar_t *ParamValue )
{
 if( hParams==NULL )
  return -1;

 Solarix::Search_Engine::QueryParams &p = *(Solarix::Search_Engine::QueryParams*)hParams;

 #if LEM_DEBUGGING==1
 // Проверим на повтор. 
 for( lem::Container::size_type i=0; i<p.size(); i++ )
  if( p[i].first==ParamName )
   return -1;
 #endif

 p.push_back( std::make_pair( UFString(ParamName),UFString(ParamValue) ) );

 return CastSizeToInt(((Solarix::Search_Engine::QueryParams*)hParams)->size());
}



FAIND_API(HFAINDCMD) sol_ParseQueryWithParams(
                                              HFAIND hEngine,
                                              const wchar_t *query,
                                              HFAINDPARAMS hParams
                                             )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok || hParams==NULL )
  return NULL;

 return ParseQuery( hEngine, query, *(Solarix::Search_Engine::QueryParams*)hParams, ENGINE->dataset_flag, false );
}


FAIND_API(HFAINDCMD) sol_ParseSqlQueryWithParams(
                                                 HFAIND hEngine,
                                                 const wchar_t *query,
                                                 HFAINDPARAMS hParams
                                                )
{
 if( hEngine==NULL || query==NULL || !*query || !ENGINE->ok || hParams==NULL )
  return NULL;

 return ParseQuery( hEngine, query, *(Solarix::Search_Engine::QueryParams*)hParams, ENGINE->dataset_flag, false );
}



FAIND_API(int) sol_FindTaggedDocument( HFAIND hEngine, const wchar_t *Filename, const wchar_t *Location )
{
 if( hEngine==NULL || Filename==NULL || !ENGINE->ok )
  return -1;

 int id_doc=-1;

 #if defined SOLARIX_PRO

 Solarix::DocumentTagsCache& tags = ENGINE->parser->GetDocumentTagsCache(); 
 id_doc = tags.FindDocId( Filename, Location );

 #endif

 return id_doc;
}


FAIND_API(int) sol_GetDocumentTag( HFAIND hEngine, int id_doc, const wchar_t *Tag, wchar_t **Value )
{
 if( hEngine==NULL || id_doc==-1 || Tag==NULL || !ENGINE->ok )
  return -1;

 int ret=-1;

 #if defined SOLARIX_PRO

 Solarix::DocumentTagsCache& tags = ENGINE->parser->GetDocumentTagsCache(); 
 lem::UFString tag_value = tags.GetTagForFile( id_doc, Tag );

 *Value = (wchar_t*)malloc( sizeof(wchar_t)*(tag_value.length()+1) );
 lem_strcpy( *Value, tag_value.c_str() );

 ret = tag_value.empty() ? 0 : 1;

 #endif

 return ret;
}


FAIND_API(int) sol_AddTaggedDocument( HFAIND hEngine, const wchar_t *Filename, const wchar_t *Location )
{
 if( hEngine==NULL || Filename==NULL || !ENGINE->ok )
  return -1;

 int id_doc=-1;

 #if defined SOLARIX_PRO

 Solarix::DocumentTagsCache& tags = ENGINE->parser->GetDocumentTagsCache(); 
 id_doc = tags.AddDoc( Filename, Location );

 #endif

 return id_doc;
}



FAIND_API(int) sol_SetDocumentTag( HFAIND hEngine, int id_doc, const wchar_t *Tag, const wchar_t *Value )
{
 if( hEngine==NULL || id_doc==-1 || Tag==NULL || !ENGINE->ok )
  return -1;

 int ret=-1;

 #if defined SOLARIX_PRO

 Solarix::DocumentTagsCache& tags = ENGINE->parser->GetDocumentTagsCache(); 

 if( Value==NULL )
  {
   tags.RemoveTagForFile( id_doc, Tag );
   ret = 1;
  }
 else
  {
   tags.SetTagForFile( id_doc, Tag, Value );
   ret = 1;
  }

 #endif

 return ret;
}


// **********************************************************************************
// Для портабельного поисковика (sol_CreatePortableSearcher) - подцепляем индекс в
// указанной папке для последующего поиска.
//
// При последующем поиске обычными командами движка нужно указывать в качестве имени
// индекса именно папку с индексом.
// **********************************************************************************
FAIND_API(int) sol_AttachPortableIndex( HFAIND hEngine, const wchar_t *IndexFolder )
{
 if( hEngine==NULL || !ENGINE->ok || IndexFolder==NULL )
  return -1;

 int ret=-1;

 #if defined FAIND_IDBLIST_RAM

 lem::UFString cmd = lem::format_str( L"-index dir \"%ls\" -index create_domain \"%ls\"", IndexFolder, IndexFolder );
 ret = sol_Execute( hEngine, cmd.c_str() );

 #endif

 return ret;
}



#endif

