#define STRICT
#define XVT_INCL_NATIVE
#include <controls.h>
#include <execp.h> 
#include <golem.h> 
#include <msksheet.h> 
#include <prefix.h>
#include <relation.h>
#include <urldefid.h>
#include <utility.h>

#include <shellapi.h>

#include "bagn006.h"

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

HIDDEN long get_reg_key(HKEY key, const char* subkey, TString& retdata)
{                                            
  HKEY hkey;
  long retval = RegOpenKey(key, subkey, &hkey);
  if (retval == ERROR_SUCCESS)
  {
    long datasize = retdata.size();
    RegQueryValue(hkey, NULL, retdata.get_buffer(), &datasize);
    RegCloseKey(hkey);
  }
  return retval;
}

HIDDEN bool file2app(const char* filename, TString& app)
{                               
  bool ok = FALSE;
  
  if (*filename != '.')
  {
    HINSTANCE hinst = FindExecutable(filename, ".", app.get_buffer());
    DWORD* pinst = (DWORD*)hinst;
    UINT err = LOWORD(pinst);
    ok = err > 32;
  }
    
  if (!ok)
  {                         
    char ext[_MAX_EXT];
    if (*filename == '.')
      strncpy(ext, filename, _MAX_EXT);
    else  
      _splitpath(filename, NULL, NULL, NULL, ext);

    TFilename key;
    if (get_reg_key(HKEY_CLASSES_ROOT, ext, key) == ERROR_SUCCESS)
    {
      key << "\\shell\\open\\command";
      if (get_reg_key(HKEY_CLASSES_ROOT, key, key) == ERROR_SUCCESS)
      {
        key.strip("\"");
        int pos = key.find("%1");
        if (pos > 0)
          key.cut(pos);
        key.trim();
        app = key;  
        ok = TRUE;  
      }
    }      
  }  
  
  return ok;
}

HIDDEN bool internet_address(const char* filename)
{           
  const TFilename url(filename);
  if (url.find("://") > 0)
    return TRUE;
  if (url.find("www.") >= 0)
    return TRUE;                                
  
  const char* extensions[] = { "com","edu","gov","it","mil","net","org", NULL };
  const char* ext = url.ext();
  for (int e = 0; extensions[e]; e++)
    if (stricmp(ext, extensions[e]) == 0)
      return TRUE;
  
  return FALSE;
}

HIDDEN word file2icon(const char* filename)
{
  word icon = 0;

  char ext[_MAX_EXT];
  if (*filename == '.')
    strncpy(ext, filename, _MAX_EXT);
  else                   
  {         
    if (internet_address(filename))
      strcpy(ext, ".htm");                      
    else  
      _splitpath(filename, NULL, NULL, NULL, ext);
  }  
  
  int icon_number = 0;
  TFilename key;

  if (stricmp(ext, ".exe") != 0)
  {
    if (get_reg_key(HKEY_CLASSES_ROOT, ext, key) == ERROR_SUCCESS)
    {                     
      key << "\\DefaultIcon";                                     
      if (get_reg_key(HKEY_CLASSES_ROOT, key, key) == ERROR_SUCCESS)  // Windows 95 only
      {
        const int comma = key.find(',');
        if (comma > 0)
        {
          icon_number = atoi(key.mid(comma+1));
          key.cut(comma);
        }
      }
      else
      {
        if (!file2app(filename, key))
          file2app(".htm", key);
      }  
    }
  }  
  else
    key = filename;
  
  if (fexist(key))
  {
    HINSTANCE hInst = (HINSTANCE)xvt_vobj_get_attr(NULL_WIN, ATTR_WIN_INSTANCE);
    HICON hicon = ExtractIcon(hInst, key, icon_number);
    if (hicon == NULL) 
      ExtractIcon(hInst, key, 0);
    if (hicon == NULL)
      warning_box("Impossibile accedere alll'icona del programma %s", key.get_buffer());
    DWORD dwicon = DWORD((DWORD*)hicon);
    icon = LOWORD(dwicon);
  }
  
  return icon;  
}

HIDDEN bool goto_url(const char* url)
{
  bool retflag = FALSE;
  
  HINSTANCE hinst = ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
  DWORD winst = DWORD((DWORD*)hinst);
  UINT error = UINT(winst);  // Tutto 'sto giro per evitare un warning
  if (error <= 32)
  {    
    TFilename app(_MAX_PATH);
    if (file2app(".htm", app))
    {
      app << ' ' << url;
      error = WinExec(app, SW_SHOWNORMAL);
      if (error > 32)
        retflag = TRUE;
    }
  }
  else
    retflag = TRUE;
  
  return retflag;
}


HIDDEN bool print_url(const char* url)
{
  HINSTANCE hinst = ShellExecute(NULL, "print", url, NULL, NULL, SW_SHOWNORMAL);
  DWORD winst = DWORD((DWORD*)hinst);
  UINT error = UINT(winst);  // Tutto 'sto giro per evitare un warning
  return error >= 32;
}  

///////////////////////////////////////////////////////////
// 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);

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;
    xvt_fsys_convert_str_to_dir(".", &fs.dir);
    strcpy(fs.type, "");
    strcpy(fs.name, "*.*");
    strcpy(fs.creator, "GOLEM");
    
    xvt_fsys_save_dir();
    FL_STATUS ok = xvt_dm_post_file_open(&fs, "Selezionare il file ...");
    if (ok == FL_OK)
    {
      xvt_fsys_restore_dir();

      TFilename file;
      xvt_fsys_convert_dir_to_str(&fs.dir, file.get_buffer(), file.size());
      file.add(fs.name); 
      file.ext(fs.type);
      f.set(file);
      k = K_TAB;
    }
  }

  if (k == K_TAB && f.focusdirty())
  {   
    TFilename file = m.get(S_FILE);
    if (fexist(file))
    {
      char ext[_MAX_EXT];
      char name[_MAX_FNAME];
      _splitpath(file, NULL, NULL, name, ext);
      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 (!fexist(url))
      {
        TFilename name;
        char ext[_MAX_EXT];
        _splitpath(url, NULL, NULL, name.get_buffer(), ext);
        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::ole_handler(TMask_field& f, KEY k)
{    
  if (k == K_SPACE)
  {     
    TSheet_field& sheet = f.mask().sfield(F_SHEET);
    if (sheet.items() == 0)
      sheet.row(-1);

    sheet.check_row(sheet.selected());
    TMask& sm = sheet.sheet_mask();
    TString16 ext = f.prompt();
    if (ext[0] != '.') 
      ext.insert(".", 0);
      
    TFilename file = sm.get(S_FILE);
    if (file.blank())
    {
      file.temp(NULL, ext);
      sm.set(S_FILE, file);
      ofstream out(file);
      sheet.row(sheet.selected()) = file;
    }  
    sheet.force_update();

    TFilename app; file2app(ext, app);
    app << ' ' << file;
    WinExec(app, SW_SHOWNORMAL);
  }
  return TRUE;
}

TGolem_mask::TGolem_mask()
           : TMask("bagn006")
{
  TSheet_field& sheet = sfield(F_SHEET);

  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;
  const int items = ini.list_variables(list);
  list.sort();
  const int ITEMS_PER_ROW = 15;
  const int MAX_ITEMS = ITEMS_PER_ROW * 2 - 1;
  for (int i = 0; i < items && i < MAX_ITEMS; i++)
  {                          
    TString16 ext = list.row(i);
    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);
    
    if (ext[0] != '.') 
      ext.insert(".", 0);
    word icon = file2icon(ext);
    if (icon)
      btn.set_icon(icon);
    else  
      btn.set_bmp(BMP_OLE, 0);
  }
}

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

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

void TGolem_field::set_window_data(const char* data)
{ 
  WORD icon = 0;         
  
  char* pipe = strchr(data, '|');
  if (pipe)
  {
    *pipe = '\0';
    icon = file2icon(data);
    *pipe = '|';
  }
  
  TPushbutton_control* btn = (TPushbutton_control*)_ctl;
  if (icon)
    btn->set_icon(icon);
  else  
    btn->set_bmp(BMP_OLE, 0);

  RCT rct; btn->get_rect(rct);
  xi_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)
    {                   
      TFilename golem_path(_MAX_PATH);
      TDir dir; dir.get(r.lfile().num());
      const long ditta = dir.is_com() ? 0 : prefix().get_codditta();
      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 was_stored = old_row.get_char(2) <= ' ';
          const is_stored = new_row.get_char(2) <= ' ';
          if (was_stored && fexist(old_file))
            ::remove(old_file);
          if (is_stored && !new_file.blank())
          {                
            char ext[_MAX_EXT];
            _splitpath(new_file, NULL, NULL, NULL, ext);
             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);
          }
        }
      }    
      _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();
    if (roman())
    {
      tmp.separator('\n');
      for (const char* row = tmp.get(0); row; row = tmp.get())
      {
        if (*row > ' ')
          sheet.row(-1) = row;
      }    
      if (msk->run() == K_ENTER)
      {
        tmp.cut(0);
        for (int r = 0; r < sheet.items(); r++)
        {
          TToken_string& row = sheet.row(r);
          if (row[0] > ' ')
            tmp.add(row);
        }  
        set(tmp);
      }
    }
    else
    {
      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);
      }
    }
    
    delete msk;
  }
  return ok;
}
              
TGolem_field::TGolem_field(TMask* m) 
            : TEditable_field(m), _old_value(80, '\n')
{ }

TGolem_field::~TGolem_field()
{ }

///////////////////////////////////////////////////////////
// Obsolete DDE management
///////////////////////////////////////////////////////////

#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_WIN32

#include <dde.h>

///////////////////////////////////////////////////////////
// DDE
///////////////////////////////////////////////////////////

static TDDE* CUR_DDE = NULL;

HIDDEN BOOLEAN hook(HWND hwnd,
                    UINT msg,
                    UINT wparam,
                    ULONG lparam,
                    long* ret)
{  
  CHECK(CUR_DDE, "No DDE available");
  bool normal_process = TRUE;        
  
  if (CUR_DDE->hwnd() == (word)hwnd) switch (msg)
  {
  case WM_DDE_INITIATE:    
    if (wparam != CUR_DDE->hwnd())     // Non initiarti da solo!
    {
      ATOM app = LOWORD(lparam);
      ATOM topic = HIWORD(lparam);
      TString a(256), t(256); 
      if (app)
        GlobalGetAtomName(app, (char*)(const char*)a, a.size()); 
      if (topic)  
        GlobalGetAtomName(topic, (char*)(const char*)t, t.size());
      
      bool ok = FALSE;
      const char* an = CUR_DDE->get_app_name();
      if (an && *an) 
        ok = a.empty() || a.compare(an, -1, TRUE) == 0;
      if (ok)       // Server name ok
      {                                   
        const bool query_topics = t.empty() || t == "*";
        TToken_string topics = CUR_DDE->get_topics();
        ok = !topics.empty_items();    // No topics?
        if (ok && !query_topics)
        { 
          ok = FALSE;
          for (const char* topo = topics.get(0); topo; topo = topics.get())
          {
            if (t.compare(topo, -1, TRUE) == 0)
            {
              ok = TRUE;
              break;
            }
          }  
        }  
        if (ok)     // Topic ok
        {
          ok = CUR_DDE->do_initiate(wparam, t);
          if (ok)   // Connection ok       
          {   
            if (!query_topics)
              topics = t;
            for (t = topics.get(0); t.not_empty(); t = topics.get())
            { // E' obbligatorio crearne dei nuovi! Non spostare fuori dal ciclo! 
              app = GlobalAddAtom(CUR_DDE->get_app_name());    
              topic = GlobalAddAtom(t);  
              SendMessage((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, MAKELPARAM(app,topic));
            }  
          }  
        }  
      }  
      normal_process = FALSE;
    }           
    break;
  case WM_DDE_ACK:               
    {
      ATOM a = LOWORD(lparam); if (a) GlobalDeleteAtom(a);
      ATOM t = HIWORD(lparam); if (t) GlobalDeleteAtom(t);
      CUR_DDE->do_ack(wparam);
      normal_process = FALSE;
    }  
    break;       
  case WM_DDE_DATA:
    {
      ATOM i = HIWORD(lparam); if (i) GlobalDeleteAtom(i);
    }
    break;     
  case WM_DDE_EXECUTE:
    {
      const TString cmd((const char*)lparam);
      DDEACK ack; memset(&ack, 0, sizeof(ack));
      ack.fAck = CUR_DDE->do_execute(wparam, cmd);
      // Ritorna indietro l'handle globale che verra' distrutto dal chiamante
      PostMessage((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam);
      normal_process = FALSE;
    }           
    break;
  case WM_DDE_TERMINATE:  
    CUR_DDE->do_terminate(wparam);
    normal_process = FALSE;
    break;
  case WM_DROPFILES:
    if (CUR_DDE->do_custom_message(msg, wparam, lparam))
    {
      *ret = 0;            
      normal_process = FALSE;
    }  
    break;  
  default:   
    if (msg > (UINT)WM_USER && msg < 0x7FFF)
    {
      if (CUR_DDE->do_custom_message(msg, wparam, lparam))
        normal_process = FALSE;
    }    
    break;  
  }
  
  return normal_process;
}

TDDE::TDDE()
    : _server(0), _old_hook(-1)
{          
  CHECK(CUR_DDE == NULL, "Double DDE");
  CUR_DDE = this;
  _hwnd = (word)xvt_vobj_get_attr(TASK_WIN, ATTR_NATIVE_WINDOW);
}

TDDE::~TDDE()
{              
  do_events();  // Flush degli venti in coda prima di sganciare tutto
  terminate();
  _hwnd = 0;
  CUR_DDE = NULL;  
}

bool TDDE::initiate(const char* app, const char* topic)
{ 
  if (_old_hook == -1)
  {
    _old_hook = xvt_vobj_get_attr(NULL_WIN, ATTR_EVENT_HOOK);  
    xvt_vobj_set_attr(NULL_WIN, ATTR_EVENT_HOOK, (long)hook); 
  }
 
  _server = 0;
  ATOM a_app = GlobalAddAtom(app);
  ATOM a_topic = GlobalAddAtom(topic);
  SendMessage(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)_hwnd, MAKELPARAM(a_app, a_topic));
  GlobalDeleteAtom(a_app);
  GlobalDeleteAtom(a_topic);
  
  return _server != 0;
}

bool TDDE::execute(const char* cmd) const
{                                    
  HGLOBAL hg = GlobalAlloc(GMEM_DDESHARE, strlen(cmd)+1);
  char* c = (char*)GlobalLock(hg);
  strcpy(c, cmd);
  GlobalUnlock(hg);
  return PostMessage((HWND)_server, WM_DDE_EXECUTE, (WPARAM)_hwnd, MAKELPARAM(0, hg));
}       

bool TDDE::execute(const char* app, const char* topic, const char* cmd, const char* filename)
{
  bool running = initiate(app, topic);
  if (!running)
  { 
    if (filename == NULL || *filename == '\0')
      filename = app;
      
    TExternal_app server(filename);
    if (server.run(TRUE) == 0)
    {
      for (int failures = 0; !running && failures < 10; failures++)
      {
        const clock_t end = clock() + 3*CLOCKS_PER_SEC;
        while (clock() < end)
          do_events();
        running = initiate(app, topic);
      }    
    }  
  }
  
  if (running)
  {   
    if (cmd && *cmd)
      execute(cmd);
    terminate();
  }
  
  return running;
}

bool TDDE::start_server()
{ 
  if (_old_hook == -1)
  {
    _old_hook = xvt_vobj_get_attr(NULL_WIN, ATTR_EVENT_HOOK);  
    xvt_vobj_set_attr(NULL_WIN, ATTR_EVENT_HOOK, (long)hook); 
  }  
  return TRUE;
}

void TDDE::terminate()
{
  if (_old_hook != -1)
  {
    xvt_vobj_set_attr(NULL_WIN, ATTR_EVENT_HOOK, _old_hook);   
    _old_hook = -1;                                          
    PostMessage((HWND)_server, (WPARAM)_hwnd, WM_DDE_TERMINATE, (LPARAM)0);
    _server = 0;
  }
}

#endif