#define STRICT
#define WIN32_EXTRA_LEAN
#define XVT_INCL_NATIVE

#include <controls.h>
#include <execp.h> 
#include <golem.h> 
#include <image.h>
#include <msksheet.h> 
#include <prefix.h>
#include <relation.h>
#include <urldefid.h>
#include <utility.h>

#ifdef WIN32
#include <mapi.h>
#include <shellapi.h>
#endif

#include "bagn006.h"

///////////////////////////////////////////////////////////////////////////////
// GotoURL 
// Liberamente tradotto da Windows Developer Journal August 1997
///////////////////////////////////////////////////////////////////////////////

HIDDEN unsigned int mail2icon()
{ 
  unsigned int icon = xvt_sys_load_icon(".eml");
  if (icon == 0)
    icon = xvt_sys_load_icon(".msg");
  if (icon == 0)
    icon = xvt_sys_load_icon(".mmf");
  return icon;  
}

bool goto_url(const char* url)
{
  TFilename app(url);
  app.lower();  
  const TString ext(app.ext());
  
  TConfig link(CONFIG_USER, "Link");
  app = link.get(ext);
  if (app.not_empty())    
  {
    app << " \"" << url << '"';
    long error = xvt_sys_execute(app, FALSE, FALSE);  //verificare
    if (error > 32)
      return TRUE;
    else
      link.set(ext, "");
  }

  return xvt_sys_goto_url(url, "open") != 0;
}

bool edit_url(const char* url)
{
  bool ok = xvt_sys_goto_url(url, "edit") != 0;
  if (!ok)
    ok = goto_url(url);
  return ok;
}

bool print_url(const char* url)
{
  bool ok = xvt_sys_goto_url(url, "print") != 0;
  if (!ok)
    ok = goto_url(url);
  return ok;
}  

///////////////////////////////////////////////////////////
// Maschera GOLEM
///////////////////////////////////////////////////////////

class TGolem_mask : public TMask
{ 
protected:
  static bool file_handler(TMask_field& f, KEY k);
  static bool link_handler(TMask_field& f, KEY k);
  static bool main_link_handler(TMask_field& f, KEY k);
  static bool ole_handler(TMask_field& f, KEY k);
  static bool sheet_notify(TSheet_field& s, int r, KEY k);

public:
  TGolem_mask();
  virtual ~TGolem_mask() { }
};

bool TGolem_mask::file_handler(TMask_field& f, KEY k)
{           
  TMask& m = f.mask();

  if (k == K_F9)
  {     
    FILE_SPEC fs;
    if (!f.empty())
    { 
      const TFilename n = f.get();
      xvt_fsys_convert_str_to_dir((char*)(const char*)n.path(), &fs.dir);
    }
    else
      xvt_fsys_convert_str_to_dir(".", &fs.dir);
    strcpy(fs.type, "");
    strcpy(fs.name, "*.*");
    strcpy(fs.creator, "GOLEM");
    
    DIRECTORY dir; xvt_fsys_get_dir(&dir);
    FL_STATUS ok = xvt_dm_post_file_open(&fs, "Selezionare il file ...");
    xvt_fsys_set_dir(&dir);
    
    if (ok == FL_OK)
    {
      TFilename file;
      xvt_fsys_convert_dir_to_str(&fs.dir, file.get_buffer(), file.size());
      file.add(fs.name); 
      m.set(S_FILE, file);
      k = K_TAB;
    }
    f.set_focus();
  }

  if (k == K_TAB && f.focusdirty())
  {   
    TFilename file = m.get(S_FILE);
    if (file.exist())
    {
      char ext[_MAX_EXT];
      char name[_MAX_FNAME];
      xvt_fsys_parse_pathname(file, NULL, NULL, name, ext, NULL);  //verificare
      file = name; file.ext(ext);
      m.enable(S_LINK);
      m.disable(F_NEWREC);
      m.enable(F_LINK);
      m.enable(F_PRINT);
    }  
    else
    {        
      const bool full = file.not_empty();
      m.set(S_LINK, full ? "X" : "");
      m.disable(S_LINK);
      m.enable(F_NEWREC, full);
      m.enable(F_LINK, full);
      m.disable(F_PRINT);
      file.cut(0);
    }  
    m.set(S_ALIAS, file);
  }
  
  return TRUE;
}

bool TGolem_mask::link_handler(TMask_field& f, KEY k)
{    
  if (k == K_SPACE)
  {     
    TMask& m = f.mask();
    TFilename url = m.get(S_FILE);
    short id = f.dlg();
    if (id == F_NEWREC && url.not_empty())
    {                  
      if (!url.exist())
      {
        TFilename name;
        char ext[_MAX_EXT];
        xvt_fsys_parse_pathname(url, NULL, NULL, name.get_buffer(), ext, NULL);
        if (name.not_empty() && *ext)
        {    
          name.ext(ext);
          url.tempdir();
          url.add(name);
          ofstream out(url);         // Crea un file vuoto
        }  
        else
          url.cut(0);
        m.set(S_FILE, url, TRUE);    // ... memorizza il suo nome ... 
        m.reset(S_LINK);             // Non puo' essere un collegamento
      }  
      else
        id = F_LINK;
    }

    if (url.not_empty())
    {
      bool open = TRUE;
      if (id == DLG_PRINT)
        open = !print_url(url);
      if (open)
        goto_url(url);
    }    
  }
  return TRUE;
}

bool TGolem_mask::main_link_handler(TMask_field& f, KEY k)
{    
  if (k == K_SPACE)
  {     
    TSheet_field& sheet = f.mask().sfield(F_SHEET);
    if (sheet.items() > 0)
    {
      sheet.check_row(sheet.selected());
      TMask& sm = sheet.sheet_mask();
      TMask_field& link = sm.field(f.dlg());
      link_handler(link, K_SPACE);
    }  
  }
  return TRUE;
}

bool TGolem_mask::sheet_notify(TSheet_field& s, int r, KEY k)
{
  if (k == K_DEL)
  {
    TToken_string row = s.row(r);
    if (row.get_char(2)<=' ')
    {
      xvt_fsys_removefile(row.get(0));
    }
  }
  return true;
}

bool TGolem_mask::ole_handler(TMask_field& f, KEY k)
{    
  if (k == K_SPACE)
  {     
    TSheet_field& sheet = f.mask().sfield(F_SHEET);
    TMask& sm = sheet.sheet_mask();

    int selected = sheet.selected();
    if (sheet.items() == 0 || !sm.field(S_FILE).empty())
    {
      sheet.row(-1);
      selected = sheet.items()-1;
    }  
    sheet.check_row(selected);

    TString16 ext = f.prompt();
    if (ext == "mailto")
    {
      TMail_message msg("");
      msg.send();
    }
    else
    {
      TFilename file;
      file.temp(NULL, ext);
      sm.set(S_FILE, file);
      ofstream out(file);
      out.close();
      sheet.row(selected) = file;
      sheet.force_update();
      edit_url(file);
    }  
  }
  return TRUE;
}

TGolem_mask::TGolem_mask()
           : TMask("bagn006")
{
  TSheet_field& sheet = sfield(F_SHEET);
  sheet.set_notify(sheet_notify);
  set_handler(F_NEWREC, main_link_handler);
  set_handler(F_LINK, main_link_handler);
  set_handler(F_PRINT, main_link_handler);
    
  TMask& sm = sheet.sheet_mask();
  sm.set_handler(S_FILE, file_handler);
  sm.set_handler(F_NEWREC, link_handler);
  sm.set_handler(F_LINK, link_handler);
  sm.set_handler(F_PRINT, link_handler);
  
  TConfig ini(CONFIG_USER, "Link");
  TString_array list;
  ini.list_variables(list);
  
  // Elimina la posta se c'e'
  const int mmf_pos = list.find("mailto");     
  if (mmf_pos >= 0) list.destroy(mmf_pos);
  
  // Ordina alfabeticamente
  list.sort();

  const int ITEMS_PER_ROW = 15;
  const int MAX_ITEMS = ITEMS_PER_ROW * 2;
  
  // Aggiungi la posta se attiva
#ifdef WIN32  // verificare
  if (GetProfileInt("Mail", "MAPI", 0))
  {
    const int items = list.items();
    const int mmf_pos = (items >= MAX_ITEMS) ? MAX_ITEMS-1 : -1;
    list.add("mailto", mmf_pos);
  }
#else
	SORRY_BOX();  // verificare
#endif  

  int i = 0;
  FOR_EACH_ARRAY_ROW(list, r, row)
  {               
    TString16 ext = *row;   
    unsigned int icon;
    
    if (ext == "mailto") 
    {
      icon = mail2icon();
    }
    else
    {
      ext.insert(".", 0);
      icon = xvt_sys_load_icon(ext);
    } 
    if (icon)
    {
      const int x = (i < ITEMS_PER_ROW ? i*5 : (i-ITEMS_PER_ROW)*5) + 1;
      const int y = i < ITEMS_PER_ROW ? 0 : 2;
      TButton_field& btn = add_button(301+i, 0, ext, x, y, 3, 2);
      btn.set_handler(ole_handler);
      btn.set_central_icon(icon);
      i++;
      if (i > MAX_ITEMS)
        break;
    }
  }
}

///////////////////////////////////////////////////////////
// Campi GOLEM
///////////////////////////////////////////////////////////

TString& TGolem_field::get_window_data()
{
  return _str;
}

void TGolem_field::set_window_data(const char* data)
{ 
  unsigned int icon = 0;         
  
  char* pipe = strchr(data, '|');
  if (pipe)
  {
    *pipe = '\0';                // Poor man TToken_string
    icon = xvt_sys_load_icon(data);
    *pipe = '|';
  }
  
  TPushbutton_control* btn = (TPushbutton_control*)_ctl;
  if (icon)
    btn->set_central_icon(icon);
  else  
    btn->set_bmp(BMP_OLE, 0);

  RCT rct; btn->get_rect(rct);
  xvt_dwin_invalidate_rect(btn->parent(), &rct);
}

bool TGolem_field::is_editable() const
{ return FALSE; } 

void TGolem_field::parse_head(TScanner& scanner)
{
  _ctl_data._width = scanner.integer();
  if (_ctl_data._width <= 0) _ctl_data._width = 10;
  _ctl_data._height  = scanner.integer(); // Height
  if (_ctl_data._height <= 0) _ctl_data._height = 1;
  _ctl_data._bmp_up = BMP_OLE;
}

void TGolem_field::create(WINDOW parent)
{
  _ctl = new TPushbutton_control(parent, _ctl_data._dlg,
                                 _ctl_data._x, _ctl_data._y, 
                                 _ctl_data._width+2, _ctl_data._height,
                                 _ctl_data._flags, _ctl_data._prompt,
                                 _ctl_data._bmp_up, _ctl_data._bmp_dn);
}                                 

bool TGolem_field::autoload(const TRelation& r)
{
  const bool ok = TEditable_field::autoload(r);
  if (ok)
    _old_value = get();
  else
    _old_value.cut(0);
  return ok;  
}

bool TGolem_field::autosave(TRelation& r)
{       
  if (field() != NULL)
  {
    TToken_string new_value(get(), '\n');
    if (new_value != _old_value)
    {                   
      TDir dir; 
      const int logicnum = r.lfile().num();
      dir.get(logicnum, _nolock, _nordir, _sysdirop);
      const long ditta = dir.is_com() ? 0 : prefix().get_codditta();
      
      TFilename golem_path;
      golem_path = firm2dir(ditta);
      golem_path.add("golem");
      if (!fexist(golem_path))
        make_dir(golem_path);
      
      const int old_items = _old_value.items();
      const int new_items = new_value.items();
      const int items = new_items > old_items ? new_items : old_items;
      
      TToken_string old_row, new_row;
      TFilename old_file, new_file;
      for (int i = 0; i < items; i++)
      {
        old_row = _old_value.get(i);    // Devo usare l'indice ogni volta ...
        new_row = new_value.get(i);     // ... perche' le TToken_string cambiano!
    
        old_file = old_row.get(0);
        new_file = new_row.get(0);
        
        if (old_file != new_file)
        {
          const bool was_stored = old_row.get_char(2) <= ' ' && 
                                  golem_path.compare(old_file, golem_path.len(), TRUE) == 0;
          const bool tobe_stored = new_row.get_char(2) <= ' ';
          if (was_stored && fexist(old_file))
            ::remove(old_file);
          if (tobe_stored && !new_file.blank())
          {                
            char ext[_MAX_EXT];
            xvt_fsys_parse_pathname(new_file, NULL, NULL, NULL, ext, NULL);
            old_file.temp(golem_path, ext);

            fcopy(new_file, old_file);
            new_row.add(old_file, 0);
            new_value.add(new_row, i);
            
            // Cancella il file originale se temporaneo
            TFilename tmp; tmp.tempdir();
            if (tmp.compare(new_file, tmp.len(), TRUE) == 0)
              ::remove(new_file);
          }
        }
      }    
      set(_old_value = new_value);  
    }
  }  
  return TEditable_field::autosave(r);
}

bool TGolem_field::on_key(KEY key)
{           
  bool ok = TRUE;
  if (key == K_SPACE)
  {
    TMask* msk = new TGolem_mask;
    TSheet_field& sheet = msk->sfield(F_SHEET);
    
    TToken_string tmp(get(), '\n');
    if (roman())
    {
      FOR_EACH_TOKEN(tmp, row)
      {
        if (*row > ' ')
          sheet.row(-1) = row;
      }    
      if (msk->run() == K_ENTER)
      {
        tmp.cut(0);
        FOR_EACH_SHEET_ROW(sheet, r, row)
        {
          if ((*row)[0] > ' ')
            tmp.add(*row);
        }  
        set(tmp);
        _selected = sheet.selected();
        do_message(0);
      }
    }
    else
    {
      tmp.separator('|');
      TMask& sm = sheet.sheet_mask();
      for (int i = 0; i < 3; i++)
        sm.set(S_FILE+i, tmp.get());
      
      if (sm.run() == K_ENTER)
      {
        tmp.cut(0);
        for (int i = 0; i < 3; i++)
          tmp.add(sm.get(S_FILE+i));
        set(tmp);
        do_message(0);
      }
    }
    
    delete msk;
  }
  return ok;
}
              
TGolem_field::TGolem_field(TMask* m) 
            : TEditable_field(m), _old_value(80, '\n'), _selected(0)
{ }

TGolem_field::~TGolem_field()
{ }

///////////////////////////////////////////////////////////
// TGolem_client_field 
///////////////////////////////////////////////////////////

class TGolem_window : public TField_window
{   
  TFilename _last_file;
  unsigned long _last_handle;
   
protected:
  virtual bool on_key(KEY k);
  virtual void update();
  virtual void handler(WINDOW win, EVENT* ep);
  
public:

  TGolem_field* driver() const { return ((TGolem_client_field&)owner()).driver(); }
  const TFilename& get_current_file(TFilename& fn) const;

  TGolem_window(int x, int y, int dx, int dy, WINDOW parent, TGolem_client_field* owner);
  virtual ~TGolem_window();
};

const TFilename& TGolem_window::get_current_file(TFilename& fn) const
{
  TGolem_field* gf = driver();
  if (gf != NULL)
  {
    TToken_string list(gf->get(), '\n');
    TToken_string tok = list.get(gf->selected());
    fn = tok.get();
  }
  else
    fn.cut(0);
  return fn;    
}

void TGolem_window::update()
{
  bool drawn = FALSE;

  TField_window::update();
  
  TFilename file; get_current_file(file);
  if (file.not_empty() && file.exist())
  {
    const TString16 ext = file.ext();
    if (ext.compare("bmp", -1, TRUE) == 0)
    {
      TImage img(file);
      if (img.ok())
      {  
        RCT cli; xvt_vobj_get_client_rect(win(), &cli);
        if (owner().automagic())
        {
          const double ratiox = double(cli.right-cli.left) / img.width();
          const double ratioy = double(cli.bottom-cli.top) / img.height();
          const double ratio = min(ratiox, ratioy);
          const int maxx = int(ratio * img.width()); 
          const int maxy = int(ratio * img.height());
          RCT dst; xvt_rect_set(&dst, 0, 0, maxx, maxy);
          img.draw(win(), dst);
        }
        else  
        {
          long sx = img.width() - (cli.right-cli.left); if (sx < 0) sx = 0;
          long sy = img.height() - (cli.bottom-cli.top); if (sy < 0) sy = 0;
          set_scroll_max(sx, sy);  // Setta le scroll bar
          const TPoint orig = origin();
          img.draw(win(), -int(orig.x), -int(orig.y));
        }  
        drawn = TRUE;
      }
    }
  }  
  if (!drawn)
  {
    set_scroll_max(0, 0);  // Toglie le scroll bar
    RCT cli; xvt_vobj_get_client_rect(win(), &cli);
    set_pen(COLOR_RED, 5);
    line(cli.right/4, cli.bottom/4, 3*cli.right/4, 3*cli.bottom/4);
    line(cli.right/4, 3*cli.bottom/4, 3*cli.right/4, cli.bottom/4);
  }
}

bool TGolem_window::on_key(KEY k)
{
  if (k == K_SPACE)
  {         
    TFilename file; get_current_file(file);
    if (file != _last_file)
    {
#ifdef WIN32 // verificare   
      if (_last_handle)
      { 
        if (IsWindow(HWND(_last_handle)))
          ::SendMessage(HWND(_last_handle), WM_CLOSE, 0, 0);
        _last_handle = NULL;
      }  
#endif
      _last_file = file;

      if (!file.blank() && file.exist())
      {
        const TString16 ext = file.ext();
        if (ext != "BMP")
        {
          TFilename cmd;
          const char* good_ext[] = { "BAT", "COM", "DLL", "DOC", "EXE", "INI", "TXT", "WRI", "XLS", NULL };
          for (int i = 0; good_ext[i]; i++)
          {
            if (ext == good_ext[i])
            {               
#ifdef WIN32 // verificare   
              ::GetSystemDirectory(cmd.get_buffer(), cmd.size());
              cmd.add("viewers"); 
              cmd.add("quikview.exe");
#endif 
              break;
            }  
          }
          if (!cmd.exist())
            xvt_sys_find_editor(file, cmd.get_buffer());
          if (cmd.not_empty())  
          {
            cmd << ' ' << file;
            _last_handle = xvt_sys_execute_in_window(cmd, win());
          }  
        }  
      }
      force_update();
    }  
  }
  
  return TField_window::on_key(k);
} 

void TGolem_window::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {
  case E_USER:
    switch (ep->v.user.id)
    {       
    case E_DROP_FILES:
      if (driver())
      {              
#ifdef WIN32 // verificare   
        HDROP hdrop = (HDROP)(DWORD)ep->v.user.ptr;
        const int num_files = DragQueryFile(hdrop, -1, NULL, 0); // Numero totale di files

        TToken_string file_list(driver()->get(), '\n');
        TFilename fname;    // File corrente
        for (int i = 0; i < num_files; i++)
        { 
          DragQueryFile(hdrop, i, (char*)(const char*)fname, fname.size());
          fname << '|' << fname.name() << "|X";
          if (driver()->roman())
          {
            file_list.add(fname);
          }
          else  
          {
            file_list = fname;
            break;
          }  
        }
        DragFinish(hdrop);
        driver()->set(file_list);
#endif
        on_key(K_SPACE);
      }                                
      break;
    default:
      break;
    }
    break;
  case E_MOUSE_DBL:
    on_key(K_SPACE);
    break;
  default: 
    break;
  }
  TField_window::handler(win, ep);
}

TGolem_window::TGolem_window(int x, int y, int dx, int dy, WINDOW parent, TGolem_client_field* owner) 
             : TField_window(x, y, dx, dy, parent, owner), _last_handle(0)
{ 
  _pixmap = TRUE;  

#ifdef WIN32 // verificare   
  HWND nat = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW);
  DragAcceptFiles(nat, TRUE);
#endif
}

TGolem_window::~TGolem_window()
{
#ifdef WIN32 // verificare   
  if (_last_handle)
    PostMessage(HWND(_last_handle), WM_CLOSE, 0, 0);
  HWND nat = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW);
  DragAcceptFiles(nat, FALSE);
#endif
}

word TGolem_client_field::class_id() const
{ return CLASS_GOLEM_CLIENT_FIELD; }

bool TGolem_client_field::is_kind_of(word cid) const
{ return cid == CLASS_GOLEM_CLIENT_FIELD || TWindowed_field::is_kind_of(cid); }
          
TField_window* TGolem_client_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
{ return new TGolem_window(x, y, dx, dy, parent, this); }

bool TGolem_client_field::parse_item(TScanner& scan)
{
  if (scan.key() == "DR")
  {      
    TMask& m = mask();
    const short driver = atodlg(scan.pop());
    const int pos = m.id2pos(driver);
    if (pos >= 0)
    {
      _driver = (TGolem_field*)&m.fld(pos);
      TString16 msg;
      msg.format("PUSH,%d", dlg());
      _driver->message(0, TRUE)->add(msg);
    }
    else
      yesnofatal_box("Non esiste il campo driver %d", driver);
    return TRUE; 
  }
  return TWindowed_field::parse_item(scan);
}

bool TGolem_client_field::on_hit()
{
  dispatch_e_char(win().win(), K_SPACE);
  return TWindowed_field::on_hit();
}

TGolem_client_field::TGolem_client_field(TMask* m) 
                   : TWindowed_field(m), _driver(NULL) 
{ }                 

TGolem_client_field::~TGolem_client_field()
{ }

#ifdef WIN32 // verificare   
///////////////////////////////////////////////////////////
// MAPI session
///////////////////////////////////////////////////////////

class TMAPI_session : public TObject
{
  HINSTANCE _hlibMAPI;
  LHANDLE _hSession;
  LONG _hWnd;

  TFilename m_strBaseDir;  // Directory iniziale

protected:
  bool load_mapi();
  void unload_mapi();

public:
  operator LHANDLE() const { return _hSession; }
  LONG hWnd() const { return _hWnd; }

  bool open();
  
  bool send(MapiMessage& msg, FLAGS flags = MAPI_DIALOG);
  bool remove(const TString& id);
  
  void close();

  virtual bool ok() const 
  { return _hlibMAPI != NULL && _hSession != NULL; }

  TMAPI_session();
  virtual ~TMAPI_session();
};

const char* const szMAPIDLL = "MAPI32.DLL";
const int MAPI_USE_DEFAULT = 0;

LPMAPILOGON lpfnMAPILogon = NULL;
LPMAPILOGOFF lpfnMAPILogoff = NULL;
LPMAPISENDMAIL lpfnMAPISendMail = NULL;
LPMAPISENDDOCUMENTS lpfnMAPISendDocuments = NULL;
LPMAPIFINDNEXT lpfnMAPIFindNext = NULL;
LPMAPIREADMAIL lpfnMAPIReadMail = NULL;
LPMAPISAVEMAIL lpfnMAPISaveMail = NULL;
LPMAPIDELETEMAIL lpfnMAPIDeleteMail = NULL;
LPMAPIFREEBUFFER lpfnMAPIFreeBuffer = NULL;
LPMAPIADDRESS lpfnMAPIAddress = NULL;
LPMAPIDETAILS lpfnMAPIDetails = NULL;
LPMAPIRESOLVENAME lpfnMAPIResolveName = NULL;

bool TMAPI_session::load_mapi()
{            
  if (_hlibMAPI)    // Check in already loaded
    return TRUE;

  // Check if MAPI is installed on the system
  BOOL MAPI_installed = ::GetProfileInt("Mail", "MAPI", 0);
  if(!MAPI_installed)
    return error_box("MAPI non attivato nel file win.ini");

  UINT fuError = ::SetErrorMode(SEM_NOOPENFILEERRORBOX);
  _hlibMAPI = ::LoadLibrary(szMAPIDLL);
  ::SetErrorMode(fuError);

  if (_hlibMAPI < (HINSTANCE)HINSTANCE_ERROR)
  {
    DWORD err = *((DWORD*)_hlibMAPI) & 0xFFFF;
    _hlibMAPI = NULL;
    return error_box("Errore di caricamento di %s: %lu", szMAPIDLL, err);
  }  

  if (!(lpfnMAPILogon         = (LPMAPILOGON)GetProcAddress (_hlibMAPI, "MAPILogon"))) return FALSE;
  if (!(lpfnMAPILogoff        = (LPMAPILOGOFF)GetProcAddress (_hlibMAPI, "MAPILogoff"))) return FALSE;
  if (!(lpfnMAPISendMail      = (LPMAPISENDMAIL)GetProcAddress (_hlibMAPI, "MAPISendMail"))) return FALSE;
  if (!(lpfnMAPISendDocuments = (LPMAPISENDDOCUMENTS) GetProcAddress (_hlibMAPI, "MAPISendDocuments"))) return FALSE;
  if (!(lpfnMAPIFindNext      = (LPMAPIFINDNEXT)GetProcAddress (_hlibMAPI, "MAPIFindNext"))) return FALSE;
  if (!(lpfnMAPIReadMail      = (LPMAPIREADMAIL)GetProcAddress (_hlibMAPI, "MAPIReadMail"))) return FALSE;
  if (!(lpfnMAPISaveMail      = (LPMAPISAVEMAIL)GetProcAddress (_hlibMAPI, "MAPISaveMail"))) return FALSE;
  if (!(lpfnMAPIDeleteMail    = (LPMAPIDELETEMAIL)GetProcAddress (_hlibMAPI, "MAPIDeleteMail"))) return FALSE;
  if (!(lpfnMAPIFreeBuffer    = (LPMAPIFREEBUFFER)GetProcAddress (_hlibMAPI, "MAPIFreeBuffer"))) return FALSE;
  if (!(lpfnMAPIAddress       = (LPMAPIADDRESS)GetProcAddress (_hlibMAPI, "MAPIAddress"))) return FALSE;
  if (!(lpfnMAPIDetails       = (LPMAPIDETAILS)GetProcAddress (_hlibMAPI, "MAPIDetails"))) return FALSE;
  if (!(lpfnMAPIResolveName   = (LPMAPIRESOLVENAME)GetProcAddress (_hlibMAPI, "MAPIResolveName"))) return FALSE;

  return TRUE;
}

void TMAPI_session::unload_mapi()
{
  if (_hlibMAPI)
  {
    ::FreeLibrary(_hlibMAPI);
    _hlibMAPI = NULL;
  }
}

bool TMAPI_session::open()
{
  if (_hSession == NULL)
  {
    TWait_cursor hourglass;                                  

    DIRECTORY dir; xvt_fsys_get_dir(&dir); // Salva dir corrente che verra' cambiata da Outlook

    if (!load_mapi())
      return error_box("Impossibile inizializzare MAPI");

    FLAGS flags = MAPI_USE_DEFAULT | MAPI_LOGON_UI;
    ULONG err = lpfnMAPILogon(_hWnd, NULL, NULL, flags, 0L, &_hSession);

    if (err != SUCCESS_SUCCESS)
      return error_box("Impossibile collegarsi a MAPI: %lu", err);

    _hWnd = xvt_vobj_get_attr(cur_win(), ATTR_NATIVE_WINDOW);

    xvt_fsys_set_dir(&dir); // Ripristina dir corrente
  }
  return TRUE;
}

void TMAPI_session::close()
{
  if (_hlibMAPI)
  {
    if (_hSession)
    {
      lpfnMAPILogoff(_hSession, _hWnd, 0L, 0L);
      _hSession = NULL;
    }
    unload_mapi();
  }
}

bool TMAPI_session::send(MapiMessage& msg, FLAGS flags)
{
  bool ok = false;
  if (open())
  {
    LONG err = lpfnMAPISendMail(_hSession, _hWnd, &msg, flags, 0L);
    if (err == SUCCESS_SUCCESS)
      ok = true;
    else
      ok = error_box("Can't send mail message: %ld", err);
  }
  return ok;
}

bool TMAPI_session::remove(const TString& id)
{
  bool ok = FALSE;
  if (open())
  {
    LONG err = lpfnMAPIDeleteMail(_hSession, _hWnd, 
                                  (char*)(const char*)id, 0L, 0L);
    if (err == SUCCESS_SUCCESS)
      ok = TRUE;
    else
      error_box("Can't delete mail message: %ld", err);
  }
  return ok;
}

TMAPI_session::TMAPI_session() 
             : _hlibMAPI(NULL), _hSession(NULL), _hWnd(NULL)
{ 
  // Memorizza directory base
  GetCurrentDirectory(_MAX_PATH, m_strBaseDir.get_buffer(_MAX_PATH));
}

TMAPI_session::~TMAPI_session()
{ 
  close(); 

  // Ripristina directory base
  SetCurrentDirectory(m_strBaseDir);
}
#endif
///////////////////////////////////////////////////////////
// TMail_message
///////////////////////////////////////////////////////////

int TMail_message::add_line(const char* s)
{               
  const long len = (s && *s) ? strlen(s) : 0;
  int n = items() - 1; 
  
  if (n < 0 || (long(row(n).len()) + len) > 30000L)
    n = TString_array::add("");

  TString & line = row(n);
  line << s;
  if (line.right(1) != "\n")
    line << '\n';

  return n;
}

bool TMail_message::send(TMAPI_session& lhSession, bool hide_ui)
{
#ifdef WIN32
  const int MAX_RECIPIENTS = _recipients.items() + _copy_recipients.items() + 1;
  MapiRecipDesc* msgRecipient = new MapiRecipDesc[MAX_RECIPIENTS];              
  memset(msgRecipient, 0, MAX_RECIPIENTS*sizeof(MapiRecipDesc));
  
  int nr = 0;
  if (_sender.not_empty())
  {
    msgRecipient[nr].ulRecipClass = MAPI_ORIG;
    msgRecipient[nr++].lpszName   = _sender.get_buffer();
  }
  FOR_EACH_ARRAY_ROW(_recipients, rec, recipient)
  {   
    msgRecipient[nr].ulRecipClass = MAPI_TO;
    msgRecipient[nr].lpszName   = recipient->get_buffer();
    nr++;
  }
  FOR_EACH_ARRAY_ROW(_copy_recipients, cc_rec, cc_recipient)
  {   
    msgRecipient[nr].ulRecipClass = MAPI_CC;
    msgRecipient[nr].lpszName   = cc_recipient->get_buffer();
    nr++;
  }
  
  const int MAX_ATTACHMENTS = _attachments.items() + 1;
  MapiFileDesc* msgAttachment = new MapiFileDesc[MAX_ATTACHMENTS];              
  memset(msgAttachment, 0, MAX_ATTACHMENTS*sizeof(MapiFileDesc));
  int na = 0;
  FOR_EACH_ARRAY_ROW(_attachments, att, attachment)
  {                               
    TString80 tmp; tmp.spaces(32);
    this-> row(0) << tmp;
    msgAttachment[na].nPosition      = row(0).len()-1; 
    msgAttachment[na].lpszPathName = attachment->get_buffer();
    na++;
  }

  MapiMessage msgSend;
  memset(&msgSend, 0, sizeof(MapiMessage)); 

  long tot = 0;
  for (int i = 0; i < items(); i++)
    tot += row(i).len();
  char * buffer = new char[tot + 1];
  long b = 0;     
  for (i = 0; i < items(); i++)
  {
    strcpy(&buffer[b], row(i));
    b += row(i).len();
  }
  buffer[b] = '\0';    
  
  msgSend.lpszNoteText = buffer;
  msgSend.lpszSubject  = _subject.empty() ? NULL : _subject.get_buffer();
  msgSend.nRecipCount  = nr;
  msgSend.lpRecips     = nr == 0 ? NULL : msgRecipient;
  msgSend.nFileCount   = na;
  msgSend.lpFiles      = na == 0 ? NULL : msgAttachment;
                         
  bool ok = lhSession.send(msgSend, hide_ui ? 0 : MAPI_DIALOG);
                        
  delete buffer;                        
  delete msgRecipient;
  delete msgAttachment;

  return ok;
#else
	return TRUE;
#endif
}

bool TMail_message::send(bool hide_ui)
{   
#ifdef WIN32 //verificare
  TMAPI_session lhSession;  // Open MAPI session
  return send(lhSession, hide_ui);
#else
	return TRUE;
#endif
}

bool TMail_message::remove(TMAPI_session& lhSession)
{
#ifdef WIN32 //verificare
  bool ok = _id.not_empty() && lhSession.remove(_id);
  return ok; 
#else
	return TRUE;
#endif 
}

bool TMail_message::remove()
{   
#ifdef WIN32 //verificare
  TMAPI_session lhSession;  // Open MAPI session
  return remove(lhSession);
#else
	return TRUE;
#endif 
}

bool TMail_message::add_recipient_ex(const char* recipient, int type)
{
  TFilename rec(recipient);           
  rec.trim();
  bool ok = rec.not_empty();
  if (ok) switch(type)
  {
  case  1: 
    _copy_recipients.add(rec); 
    break;
  case  2: 
    if (rec.is_relative_path())
      rec.make_absolute_path();
    ok = fexist(rec);
    if (ok)
      _attachments.add(rec); 
    break;
  default: 
    _recipients.add(rec); 
    break;
  }
  return ok;
}

// Certified 99%
void TMail_message::set_date_time(const char* str)
{
  if (str == NULL)
    str = "";

  int anno, mese, giorno;
  int ora = 12, minuto = 0, secondo = 0;
  const int num = sscanf(str, "%d/%d/%d %d:%d:%d", 
                         &anno, &mese, &giorno, &ora, &minuto, &secondo);
  if (num >= 3)
  {
    _date = TDate(giorno, mese, anno);
    _hms = ora*10000L + minuto*100L + secondo;
  }
  else
  {
    _date = TODAY;
    _hms = 0;
  }
}

// Certified 100%
const TString& TMail_message::recipient(int n) const
{
  if (n >= 0 && n < recipients())
    return _recipients.row(n);
  return EMPTY_STRING;
}

TMail_message::TMail_message(const char* recipient, const char* subject, 
                             const char* text, const char* sender)
             : _subject(subject), _sender(sender), _hms(0)
{
  add_recipient(recipient);  
  if (text && *text)
    add(text);
}

///////////////////////////////////////////////////////////
// TMail_messages
///////////////////////////////////////////////////////////

TMail_messages::TMail_messages()
{ }

int TMail_messages::get(const char* senderFilter, 
                        const char* subjectFilter, 
                        const char* bodyFilter,
                        bool attach, bool mark)
{  
#ifdef WIN32 //verificare
 	TMAPI_session lhSession;
  destroy();
  if (!lhSession.open())
    return 0;

  TString str_msg_id(512);   // Accept MAPI_LONG_MSGID
  char* msg_id = str_msg_id.get_buffer();
  
  TString str_msg_next(512);  // Next MAPI message
  
  LONG err = lpfnMAPIFindNext(lhSession, lhSession.hWnd(), NULL, NULL, 
                              MAPI_GUARANTEE_FIFO | MAPI_LONG_MSGID, 
                              0, msg_id);

  FLAGS flags = 0;
  if (!mark) flags |= MAPI_PEEK;
  if (!attach) flags |= MAPI_SUPPRESS_ATTACH;
  
  while (err == SUCCESS_SUCCESS)
  {
    MapiMessage FAR* pMessage = NULL;
    err = lpfnMAPIReadMail(lhSession, lhSession.hWnd(), msg_id, 
                           flags, 0, &pMessage);
    
    if (err == SUCCESS_SUCCESS)
    {
      const char* id = NULL;
      const char* sender = NULL;
      const char* subject = NULL;
      const char* body = NULL;
      const char* recipient = NULL;

      bool should_add = TRUE;
      if (should_add)
      {
        const MapiRecipDesc* pSender = pMessage->lpOriginator;
        sender = pSender->lpszName;
        if (senderFilter && *senderFilter)
          should_add = stricmp(senderFilter, sender) == 0;
      }

      if (should_add)
      {
        subject = pMessage->lpszSubject;
        if (subjectFilter && *subjectFilter)
        {
          const unsigned lenFilter = strlen(subjectFilter);
          should_add = strncmp(subject, subjectFilter, lenFilter) == 0;
        }
      }

      if (should_add)
      {
        body = pMessage->lpszNoteText;
        if (bodyFilter && *bodyFilter)
          should_add = strstr(body, bodyFilter) != NULL;
        
        // Toglie eventuale = finale derivante da cattiva traduzione quoted-printable  
        char* tail = pMessage->lpszNoteText + strlen(body)-2;  
        if (tail[0] == '\n' && tail[1] == '=')
          *tail = '\0';
      }
       
      if (should_add)
      {
        TMail_message* msg = new TMail_message(recipient, subject, body, sender);
        msg->set_date_time(pMessage->lpszDateReceived);
        msg->set_id(msg_id);
        add(msg);
      }

      lpfnMAPIFreeBuffer(pMessage);
    }

    err = lpfnMAPIFindNext(lhSession, lhSession.hWnd(), NULL, 
                           msg_id, MAPI_LONG_MSGID, 0, str_msg_next.get_buffer());
    str_msg_id = str_msg_next;
  }

  return items();
#else
	return 0;
#endif
}

bool TMail_messages::send(bool hide_ui)
{
#ifdef WIN32 //verificare
  TMAPI_session lhSession;
  bool ok = TRUE;
  for (int i = 0; i < items(); i++)
    ok &= msg(i).send(lhSession, hide_ui);
  return ok;
#else
	return TRUE;
#endif
}

bool TMail_messages::remove()
{
#ifdef WIN32 //verificare
  TMAPI_session lhSession;
  bool ok = TRUE;
  for (int i = 0; ok && i < items(); i++)
    ok &= msg(i).remove(lhSession);
  return ok;
#else
	return TRUE;
#endif
}