#include <stdlib.h>

#include <xvt.h>

#include <extcdecl.h>
#include <modaut.h>

#include <applicat.h>
#include <config.h>
#include <mask.h>
#include <prefix.h>
#include <progind.h>
#include <relation.h>
#include <urldefid.h>
#include <utility.h>

#include <bagn002.h>

#define  BITTEST(w,p) (((w) & (0x0001 << (p))) != 0)

///////////////////////////////////////////////////////////
// Metodi di accesso globali all'applicazione corrente
///////////////////////////////////////////////////////////

HIDDEN TApplication* _application = NULL;

TString16 TApplication::_user;

TApplication* MainApp()
{
  CHECK(_application, "NULL application!");
#ifdef DBG
  error_box("Ti lascio usare MainApp ancora per pochi giorni!");
#endif  
  return _application;
}

TApplication& main_app()
{
  CHECK(_application, "NULL application!");
  return *_application;
}

bool xvt_running() { return _application != NULL; }

///////////////////////////////////////////////////////////
// Gestione dello sfondo della finestra principale
///////////////////////////////////////////////////////////

HIDDEN long backdrop_eh( WINDOW  win, EVENT* ep)
{
#if XVT_OS == XVT_OS_WIN
  clear_window( win, COLOR_GRAY );
#else
  clear_window( win, COLOR_BLUE );
#endif

  return 0L;
}

HIDDEN void create_backdrop( void )
{
#if XVT_OS == XVT_OS_WIN
  xvt_create_statbar();
  xvt_statbar_set("");
#else
  xvt_escape(XVT_ESC_CH_COLOR, XVT_CH_CLR_MENU,     COLOR_BLACK, COLOR_WHITE);
  xvt_escape(XVT_ESC_CH_COLOR, XVT_CH_CLR_DIALOG,   COLOR_BLUE,  COLOR_WHITE);
  xvt_escape(XVT_ESC_CH_COLOR, XVT_CH_CLR_WINDOW,   COLOR_RED,   COLOR_WHITE);
  xvt_escape(XVT_ESC_CH_COLOR, XVT_CH_CLR_CONTROL,  COLOR_BLACK, COLOR_WHITE);
  xvt_escape(XVT_ESC_CH_COLOR, XVT_CH_CLR_DISABLED, COLOR_GRAY,  COLOR_WHITE);
  xvt_escape(XVT_ESC_CH_COLOR, XVT_CH_CLR_MNEMONIC, COLOR_RED,   COLOR_WHITE);

  RCT rct;
  get_client_rect( SCREEN_WIN, &rct );
  create_window(W_PLAIN, &rct, (char*) "BACKDROP", 0, TASK_WIN,
                WSF_NO_MENUBAR | WSF_CH_BACKDROP , EM_UPDATE,
                backdrop_eh, 0L );
#endif
}

///////////////////////////////////////////////////////////
// Gestione del banner iniziale
///////////////////////////////////////////////////////////

class TBanner : public TWindow
{ 
protected:
  virtual void handler(WINDOW win, EVENT* ep);

public:
  TBanner();
  ~TBanner();
};

TBanner::TBanner()
{
  create(-1, 6, 72, 6, "BANNER", WSF_NONE, W_PLAIN);
  hide_brush();
  open();
  do_events();
}

TBanner::~TBanner()
{      
  if (is_open())
    close();
}

void TBanner::handler(WINDOW win, EVENT* ep)
{
  if (ep->type == E_UPDATE)
  { 
    const int BIGY = CHARY<<1;
    
    clear(COLOR_LTGRAY);
    RCT r; get_client_rect(win, &r);

    set_color(COLOR_WHITE, COLOR_LTGRAY);
    set_font(FF_TIMES, FS_BOLD | FS_ITALIC, BIGY);
    char* t = (char*)(const char*)main_app().title();
    int w = win_get_text_width(win, t, -1);
    int a; win_get_font_metrics(win, NULL, &a, NULL);
    int x = (r.right-w)>>1, y = (r.bottom+a)>>1 ;
    win_draw_text(win, x+1, y+1, t, -1);
    set_color(COLOR_BLACK, COLOR_LTGRAY);
    win_draw_text(win, x, y, t, -1);

    set_font(FF_TIMES);
    t = "PRASSI S.p.A.";
    w = win_get_text_width(win, t, -1);
    x = (r.right-r.left-w)>>1, y = BIGY;
    win_draw_text(win, x, y, t, -1);

    t = "Caricamento in corso";
    w = win_get_text_width(win, t, -1);
    x = (r.right-r.left-w)>>1, y = r.bottom - CHARY;
    win_draw_text(win, x, y, t, -1);

    r.left += 4; r.right -= 4;
    r.top += 4; r.bottom -= 4;
    set_pen(COLOR_BLACK); win_draw_rect(win, &r);
    offset_rect(&r, 1, 1);
    set_pen(COLOR_WHITE); win_draw_rect(win, &r);
    
    win_draw_icon(win, CHARX<<1, CHARX<<1, ICON_RSRC); 
  }
  else
    TWindow::handler(win, ep);
}

#if XVT_OS == XVT_OS_WIN

///////////////////////////////////////////////////////////
// Gestione dei processi per Windows(R)
///////////////////////////////////////////////////////////

#include <windows.h>

const word WM_WAKEUP = RegisterWindowMessage("WAKEUP");
DWORD waiting_for = 0;

HIDDEN BOOLEAN waiting_event_hook(HWND, WORD msg, WORD,
                                  DWORD lparam, long far* ret)
{
  if (msg == WM_WAKEUP)
  {
    if( lparam == waiting_for || lparam == 0)
    {
      waiting_for = 0L;
      TTemp_window tw(TASK_WIN);
      tw.maximize();
    }
    *ret = 0;
    return TRUE;
  }

  return FALSE;
}


HIDDEN DWORD name2id(const char* name)
{
  waiting_for = 0L;
  for (int i = 0; i < 5 && name[i] > ' '; i++)
  {
    waiting_for <<= 6;
    waiting_for |= toupper(name[i]) - '0';
  }
  return waiting_for;
}


void TApplication::wait_for(const char* command)
{
  TTemp_window tw(TASK_WIN);
  tw.iconize();
  name2id(cmd2name(command));
  
  const long old_hook = get_value(TASK_WIN, ATTR_EVENT_HOOK);
  set_value(TASK_WIN, ATTR_EVENT_HOOK, (long)waiting_event_hook);
  while (waiting_for) do_events();

  // We need to restore these things
  set_value(TASK_WIN, ATTR_EVENT_HOOK, old_hook);
  xvt_statbar_refresh();
}

void TApplication::wake_up_caller() const
{
  name2id(name());
  SendMessage(-1, WM_WAKEUP, 0, waiting_for);
}

#endif


long TApplication::task_eh(WINDOW win, EVENT *ep)
{
  switch (ep->type)
  {
  case E_CREATE:
    create_backdrop();
#if defined(DBG) && XVT_OS == XVT_OS_SCOUNIX
    message_box("Attach to process %d ...", getpid());
#endif
    do_events();
    break;
#if XVT_OS == XVT_OS_WIN
  case E_UPDATE:
    backdrop_eh(win, ep);
    break;
#endif
  default:
    break;
  }

  return _application->handler(win, ep);
}

long TApplication::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {
  case E_CREATE:
  {
    TBanner banner;
    _create_ok = create();
  }    
  if (!_create_ok)  
    stop_run();
  break;
 case E_COMMAND:
  switch(ep->v.cmd.tag)
  {
  case M_FILE_QUIT:
    if (can_close())
      stop_run();
    break;
  case M_FILE_PG_SETUP:
    printer().set();
    break;
  case M_FILE_PRINT:
    print();
    break;
  case M_FILE_NEW:
    set_firm();
    break;
  case M_FILE_REVERT:
    config();
    break;
  case (M_FILE+11):
    about();
    break;
  default:
    if (ep->v.cmd.tag >= BAR_ITEM(1))
    {
      if(!menu(ep->v.cmd.tag))
        stop_run();
    }   
    break;
  }
  break;
 case E_CLOSE:
  if (can_close())
    stop_run();
  break;
 case E_QUIT:
  if (ep->v.query) 
  {
    if (can_close())
      quit_OK();
  } 
  else 
    stop_run();
  break;
default:
  break;
}

  return 0L;
}


void TApplication::stop_run()
{
  if (_savefirm) prefhndl->set_codditta(_savefirm);
  terminate();
  xvt_terminate();
}


TApplication::TApplication() : _printer(NULL), _savefirm(0), _create_ok(FALSE)
{
  init_global_vars();
  _bar = TASK_MENUBAR;
}


TApplication::~TApplication()
{}


TPrinter* TApplication::set_printer(TPrinter* p)
{
  TPrinter* printer = _printer;
  _printer = p;
  return printer;
}


TPrinter& TApplication::printer()
{
  if (_printer  == NULL) 
    _printer = new TPrinter;
  return *_printer;
}


bool TApplication::create()
{
  return TRUE;
}


bool TApplication::destroy()
{
  return TRUE;
}


void TApplication::terminate()
{
#if XVT_OS == XVT_OS_WIN
  wake_up_caller();               // Manda il segnale di risveglio al chiamante
#endif

  close_all_dialogs();
  
  if (_create_ok)
  {
    destroy();                    // Distruzione files e maschere
    do_events();

    if (_printer != NULL)         // Distruzione dell'eventuale stampante
    {
      if (printer().isopen())
        printer().close();
      delete _printer;
      _printer = NULL;
    }
  }
  
  free_global_vars();             // Distruzione variabili globali

  customize_controls(FALSE);      // Rilascio eventuali DLL
}


const char* TApplication::get_module_name() const
{
  TScanner scanner("prassi.aut");

  bool ok = FALSE;
  for (int aut = 0; scanner.line() != ""; aut++)
    if (strncmp(scanner.token(), _name, 2) == 0) { ok = TRUE; break; }

  const char* module = scanner.token().mid(3);
  if (ok) ok = has_module(aut);

  if (!ok)
  {
    error_box("Il modulo '%s' non e' autorizzato", module);
    module = "";
  }  

  return module;
}


void TApplication::set_perms()
{
  CGetAut(1);
  _dongle_aut.set(0, TRUE);
  for (int i = 1 ; i < ENDAUT; i++)
  {
    const int af = i - 1;
    const bool val = BITTEST(_int_tab0[af / 16], af % 16);
    _dongle_aut.set(i, val);
  }
  _user_aut.set(0, TRUE);
  if (_user.not_empty())
  {
    if (_user == "PRASSI")
    {
      for (int i = 1 ; i < ENDAUT; i++)
        _user_aut.set(i);
    }
    else
    {
      TLocalisamfile users(LF_USER);
      
      users.zero();
      users.put("USERNAME", _user);
      if (users.read() == NOERR)
      {
        const TString80 aut(users.get("AUTSTR"));
        const int max = aut.len();
        
        for (int i = 1 ; i < max; i++)
          _user_aut.set(i, aut[i] == 'X');
      }
    }
  }
}


void TApplication::check_parameters(int & argc, char* argv[])
{
  if (strncmp(argv[argc-1], "-u", 2) == 0)
    _user = &argv[--argc][2];
#ifdef DBG  
  else _user = "PRASSI";
#endif    
  long twin_style = WSF_ICONIZABLE | WSF_SIZE | WSF_CLOSE;

  if (stricmp(argv[argc-1], "-icon") == 0)
  {
    twin_style |= WSF_ICONIZED;
    argc--;
  }  
  else  
    twin_style |= WSF_MAXIMIZED;

#if XVT_OS == XVT_OS_WIN
  set_value(NULL_WIN,ATTR_WIN_PM_TWIN_STARTUP_STYLE, twin_style);
#endif
}


void TApplication::run(int argc, char* argv[], const char* title)
{
  TFilename base(argv[0]);

  base.ext(""); base.lower();
  _title = title;
  if (_user.empty())
    check_parameters(argc, argv);

  __argc = argc;
  __argv = (const char**)argv;

  int addbar;
  if (argc > 1)
  {
    addbar = atoi(argv[1]+1);
    _name = cmd2name(argv[0], argv[1]);
  }
  else
  {
    addbar = 0;
    _name = cmd2name(argv[0]);
  }
  set_perms();

  const TFixed_string mod(get_module_name());
  if (mod.empty()) return;
  
  TString80 caption;
  caption << "PRASSI S.p.A. - " << mod;

  static XVT_CONFIG cfg;
  cfg.base_appl_name = (char*)base.name();
  cfg.appl_name      = (char*)(const char*)_title;
  cfg.taskwin_title  = (char*)(const char*)caption;
  cfg.menu_bar_ID    = TASK_MENUBAR+addbar;
  cfg.about_box_ID   = 0;

  customize_controls(TRUE);

  _application = this;
  xvt_system(argc, argv, 0L, task_eh, &cfg);
}


// About box: risposta alla opzione Informazioni del menu File
void TApplication::about() const
{                                               
#include <prassi.ver>             
  const TFilename n(__argv[0]);
  message_box("PRASSI Versione Beta %4.2f\nProgramma %s\nLibreria del %s", 
              VERSION, (const char*)n.name(), __DATE__);
}

// Risposta alla selezione Stampa del menu File
void TApplication::print()
{
#ifdef DBG
  error_box("Non saprei bene cosa stampare!");
#endif
}

void TApplication::check_menu_item(MENU_TAG item)
{
  win_menu_check(TASK_WIN, item, TRUE);
  win_update_menu_bar(TASK_WIN);
}

void TApplication::uncheck_menu_item(MENU_TAG item)
{
  win_menu_check(TASK_WIN, item, FALSE);
  win_update_menu_bar(TASK_WIN);
}


void TApplication::enable_menu_item(MENU_TAG item, bool on)
{
  win_menu_enable(TASK_WIN, item, on);
  win_update_menu_bar(TASK_WIN);
}


void TApplication::dispatch_e_menu(MENU_TAG item)
{
#if XVT_OS == XVT_OS_WIN
  HWND w = (HWND)get_value(TASK_WIN, ATTR_NATIVE_WINDOW);
  PostMessage(w, WM_COMMAND, item, 0L);
#else
  EVENT e;
  e.type = E_COMMAND;
  e.v.cmd.tag = item;
  e.v.cmd.shift = e.v.cmd.control = 0;
  dispatch_event(TASK_WIN, &e);
#endif  
}


bool TApplication::has_module(int module, int checktype) const
{
  bool ok = TRUE;
  if (checktype == CHK_ALL || checktype == CHK_DONGLE)
    ok = _dongle_aut[module];
  if (ok && checktype == CHK_ALL || checktype == CHK_USER)
    ok = _user_aut[module];
  return ok;
}


long TApplication::get_firm() const
{
  return prefhndl->get_codditta();
}

const char* TApplication::get_firm_dir() const
{
  return format("%s%s", __ptprf, prefhndl->name());
}


bool TApplication::set_firm(long newfirm)
{
  disable_menu_item(M_FILE_NEW);
  const long oldfirm = get_firm();

  if (newfirm < 1)
  {
    TMask mask("bagn002");

    mask.send_key(K_CTRL+K_SHIFT+(extended_firm() ? 's' : 'h'), -2);
    const KEY k = mask.run();

    enable_menu_item(M_FILE_NEW);
    if (k == K_ENTER)
    {
      newfirm = mask.get_long(F_CODDITTA);
      const int tipodir = mask.get_int(F_TIPO);

      if (tipodir == 0 && !prefhndl->exist(newfirm) &&
          !build_firm_data(newfirm))
        return FALSE;
      if (tipodir > 0)
      {
        if (_savefirm == 0) _savefirm = oldfirm;
        prefhndl->set(tipodir == 1 ? "com" : "");
        return TRUE;
      }
    }
  }

  if (newfirm == oldfirm || newfirm < 1)
    return newfirm > 0;

  if (prefhndl->test(newfirm))
  {
    prefhndl->set_codditta(newfirm);
    _savefirm = 0;
  }

  return TRUE;
}

bool TApplication::config()
  // gestisce le voci di configurazione
  // le si passa il file in cui cercare il proprio
  // paragrafo (comunque relativo alla ditta)
  // se non c'e', viene creato copiando il default
  // la variabile EdMask di quel paragrafo specifica
  // la maschera da usare
{
  TConfig cnf(CONFIG_DITTA);
  bool ok = FALSE;
  
  TFilename maskname(cnf.get("EdMask"));
  if (!maskname.empty())
  {
    TMask m(maskname);
    
    // carica campi
    for (int i = 0; i < m.fields(); i++)
    {
      TMask_field& f = m.fld(i);
      const TFieldref* fref = f.field();
      if (fref != NULL)
      {
        const char* fname = fref->name();
        if (fname != NULL)
        {
          TString& oldvl = cnf.get(fname);
          if (!oldvl.empty())
            f.set(oldvl);
        }
      }
    }
    
    
    // run mask
    disable_menu_item(M_FILE_NEW);
    disable_menu_item(M_FILE_REVERT);
    
    if (m.run() == K_ENTER)
    {
      // aggiusta campi
      for (i = 0; i < m.fields(); i++)
      {
        TMask_field& f = m.fld(i);
        if (f.dirty())
        {
          const TFieldref* fref = f.field();
          if (fref != NULL)
          {
            const char* fname = fref->name();
            const char* value = f.get();
            const char* oldvl = cnf.get(fname);
            if (change_config(fname,oldvl,value))
              cnf.set(fname, value, NULL, TRUE);
          }
        }
      }
      ok = TRUE;
    }
    else  ok = FALSE;
    enable_menu_item(M_FILE_NEW);
    enable_menu_item(M_FILE_REVERT);
    return ok;
  }
  return warning_box("Nessun parametro da configurare");
}

bool TApplication::change_config(const char* var, const char* oldv,
                                 const char* newv)

{ return TRUE; }
