#define STRICT
#define XVT_INCL_NATIVE
#include <applicat.h>
#include <config.h>
#include <execp.h> 
#include <golem.h> 
#include <mask.h> 
#include <prefix.h>
#include <relation.h>
#include <urldefid.h>
#include <utility.h>

#include <bagn006.h>

long TGolem::_count = 0;
TFilename* TGolem::_path = NULL;
TConfig* TGolem::_config = NULL;
TRelation* TGolem::_golem = NULL;

TGolem::TGolem(const char* cls, long id)
: _class(cls), _id(id)
{                   
  CHECK(_class.not_empty() && id >= 0, "Invalid Golem creation");
  _class.upper();

  if (_count == 0)
  { 
    CHECK(_path == NULL, "Golem construction count error");
    _path = new TFilename;
    _config = new TConfig(CONFIG_GOLEM, _class);
    _golem = new TRelation(LF_GOLEM);
  }
  _count++;
}

TGolem::~TGolem()
{
  _count--;
  if (_count == 0)
  {
    CHECK(_path != NULL, "Golem destruction count error");
    delete _golem; _golem = NULL;
    delete _config; _config = NULL;
    delete _path; _path = NULL;
  }
}

TConfig& TGolem::config() const
{
  _config->set_paragraph(_class);
  return *_config;
}


bool TGolem::ok() const
{
  return _id > 0 && fexist(path());
} 

const char* TGolem::class_name() const
{
  *_path = "GOLEM_";
  *_path << _class << "_CLASS";
  return *_path;
}

word TGolem::class_id() const
{
  return CLASS_GOLEM;
}

short TGolem::icon() const
{
  const short id = config().get_int("Icon", NULL, -1, DLG_F9);
  return id;
}


const char* TGolem::ext() const
{
  return config().get("Extension", NULL, -1, _class.left(3));
}


const TFilename& TGolem::path(bool test) const
{
  const char* e = ext();
  *_path = firm2dir(-1);           // C:\PRASSI\DATI
  _path->add("golem");             // C:\PRASSI\DATI\GOLEM
  if (test && !fexist(*_path))
    make_dir(*_path);
  
  _path->add(_class);              // C:\PRASSI\DATI\GOLEM\BITMAP
  if (test && !fexist(*_path))
    make_dir(*_path);
  
  if (_id > 0)
  {
    _path->add(format("%ld", _id));// C:\PRASSI\DATI\GOLEM\BMP\883
    _path->ext(e);                 // C:\PRASSI\DATI\GOLEM\BMP\883.BMP
  }  
  return *_path;
}

int TGolem::compare(const TSortable& s) const
{
  if (s.class_id() != class_id())
    return UNDEFINED;
  const TGolem& g = (const TGolem&)s;  
  return int(_id - g._id);
}

bool TGolem::edit()
{
  const TFilename& p = path();
  bool ok = fexist(p);
  if (ok)
  {
    TFilename e(config().get("Editor"));
    e << ' ' << p;
    
    TExternal_app app(e);
    ok = app.run(FALSE, FALSE) == 0;
  }
  
  return ok;  
}


long TGolem::new_id() const
{   
  long id = 0;
  
  TLocalisamfile& gol = _golem->lfile();
  gol.zero();
  gol.put("CLASSE", _class);
  gol.put("CHIAVE", "99999999");
  
  const int err = gol.read(_isgteq);
  switch (err) 
  {
  case NOERR:                       
    id = 0; break;
  case _isemptyfile:
    id = 1; break;
  default:  
    if (gol.get("CLASSE") != _class)
      gol.prev();                     
    if (gol.get("CLASSE") == _class)
      id = gol.get_long("CHIAVE")+1;
    else
      id = 1;
    break;    
  }
  return id;
}

bool TGolem::import()
{
  FILE_SPEC fs;
  const char* const e = ext();                             
  xvt_fsys_convert_str_to_dir(".", &fs.dir);
  strcpy(fs.type, e);
  sprintf(fs.name, "*.%s", e);
  strcpy(fs.creator, "GOLEM");
  
  xvt_fsys_save_dir();
  FL_STATUS ok = xvt_dm_post_file_open(&fs, "Selezionare il file ...");
  xvt_fsys_restore_dir();
  if (ok == FL_OK)
  {            
    TFilename from;
    xvt_fsys_convert_dir_to_str(&fs.dir, (char*)(const char*)from, 80);
    from.add(fs.name); from.ext(e);

    TMask msk("bagn006");
    msk.set(F_CLASSE, _class);
    msk.set(F_CODICE, new_id());
    
    bool correct = msk.run() == K_ENTER;
    if (correct)
    {
      _id = msk.get_long(F_CODICE);
      correct = fcopy(from, path(TRUE));
      if (correct)
      {
        msk.autosave(*_golem);
        int err =  _golem->write();
        if (err == _isreinsert) 
          err = _golem->rewrite();
        if (err != NOERR)
        {
          error_box("Errore nell'aggiornamento del file degli oggetti");
          ok = FL_BAD;  
        }
      }
      else 
      {            
        error_box("Spazio su disco insufficiente per l'oggetto");
        ok = FL_BAD;  
      }  
    }
    else ok = FL_CANCEL;  
    
  }
  
  return ok == FL_OK;
}


#if XVT_OS == XVT_OS_WIN || XVT_OS == XVT_OS_NT

#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_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(NULL)
{          
  CHECK(CUR_DDE == NULL, "Double DDE");
  CUR_DDE = this;
  _hwnd = (word)xvt_vobj_get_attr(TASK_WIN, ATTR_NATIVE_WINDOW);
}

TDDE::~TDDE()
{
  terminate();
  _hwnd = 0;
  CUR_DDE = NULL;  
}

bool TDDE::initiate(const char* app, const char* topic)
{ 
  if (_old_hook == NULL)
  {
    _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 == NULL)
  {
    _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)
  {
    xvt_vobj_set_attr(NULL_WIN, ATTR_EVENT_HOOK, _old_hook);   
    _old_hook = NULL;
    PostMessage((HWND)_server, (WPARAM)_hwnd, WM_DDE_TERMINATE, (LPARAM)0);
    _server = 0;
  }
}

#endif