/************************************************************************/
/* Module : mquel.c					                */
/* Purpose: motif SQL / query tool for Postgres95	                */
/* By     : Keith R. Davis				                */
/* Date   : 12/8/95					                */
/* Notes  : Copyright(c) 1996 White River Software	                */
/************************************************************************/

#include <Xm/Xm.h>		/* motif lib header		*/
#include <Xm/Protocols.h>       /* motif protocol header        */
#include <Xm/PushB.h>		/* push button widget header	*/
#include <Xm/MainW.h>		/* main window widget header	*/
#include <Xm/PanedW.h>		/* paned window widget header	*/
#include <Xm/ScrolledW.h>       /* scrolled window widget head. */
#include <Xm/Form.h>		/* form widget header	        */
#include <Xm/Frame.h>           /* frame widget header          */
#include <Xm/Label.h>		/* label widget header		*/
#include <Xm/Text.h>		/* text widget header		*/
#include <stdio.h>              /* stdio header                 */ 
#include <stdlib.h>             /* stdlib header                */
#include <unistd.h>             /* unix std header              */

#ifdef AMIGA
XtAppContext   app;		        /* application context		        */
#endif

#include "image.h"              /* image header                 */
#include "pixmap.h"             /* button bar icons             */
#include "mquel.h"              /* mquel header                 */
#include "callback.h"           /* callback header              */
#include "menu.h"		/* menu header		        */
#include "llist.h"              /* linked list header           */
#include "util.h"               /* util function header         */
#include "db.h"                 /* db header                    */
#include "tooltip.h"            /* tooltip header               */

/* global widget handle */
MQUELwidgets AppWidgets;
MQUELwidgets *AppWidgetsPtr = &AppWidgets;

/* tooltip actions and translations */
#ifdef TOOLTIP
XtActionsRec actions[] = {
  { "PopupTooltip",        (XtActionProc) PopupTooltip   },
  { "PopdownTooltip",      (XtActionProc) PopdownTooltip },
};
char transTable[] = "#<EnterWindow>:PopupTooltip()\n<LeaveWindow>:PopdownTooltip()";
XtTranslations toolTrans;
#endif

/* Main: entry point */
void main (int argc, char **argv)
{	
    int		   status;		            /* function return status		*/
    Atom           WM_DELETE_WINDOW;                /* delete window atom               */
    int            showlbl = 1;                     /* show labels on toolbar flag      */

#ifdef AMIGA
INIT_AMIWIN_MOTIF(&argc,argv);
#endif
 
    /* setup the shell widget */
    AppWidgets.shell = XtVaAppInitialize(&app, "Mpsql", NULL, 0,
					 &argc, argv, NULL,
					 XmNdeleteResponse, XmDESTROY,
					 NULL);
#ifdef TOOLTIP
    InitializeTooltip (XtDisplay(AppWidgets.shell),"Mpsql");
    showlbl = 0;
#endif

    /* register wm protocol callback func. */
    WM_DELETE_WINDOW = XInternAtom(XtDisplay(AppWidgets.shell),
				   "WM_DELETE_WINDOW", FALSE);

    /* add callback for protocol */
    XmAddWMProtocolCallback(AppWidgets.shell, WM_DELETE_WINDOW,
			    DeleteWindowCallback, NULL);
    
    /* setup the mainwindow widget */
    AppWidgets.mainwindow = XtCreateManagedWidget("main",xmMainWindowWidgetClass,
						  AppWidgets.shell, NULL, 0);
    /* setup the button bar widget */
    AppWidgets.btnbar = XtCreateManagedWidget("btnbar", xmFormWidgetClass, 
					      AppWidgets.mainwindow, NULL, 0);

     /* setup the new file btn */
    AppWidgets.btn_new = XtVaCreateManagedWidget("btnnew",
						 xmPushButtonWidgetClass,
						 AppWidgets.btnbar,
						 XmNtopAttachment, XmATTACH_FORM,
						 XmNtopOffset, 5,
						 XmNbottomAttachment, XmATTACH_NONE,
						 XmNleftAttachment, XmATTACH_FORM,
						 XmNleftOffset, 5,
						 XmNrightAttachment, XmATTACH_NONE,
						 NULL);

    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_new, new_xpm, showlbl);

    /* add callback for the new btn */
    XtAddCallback(AppWidgets.btn_new, XmNactivateCallback,
		  NewCallback, NULL);
   
    /* setup the open file btn */
    AppWidgets.btn_open = XtVaCreateManagedWidget("btnopen",
						  xmPushButtonWidgetClass,
						  AppWidgets.btnbar,
						  XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
						  XmNtopWidget, AppWidgets.btn_new,
						  XmNbottomAttachment,
						  XmATTACH_OPPOSITE_WIDGET,
						  XmNbottomWidget, AppWidgets.btn_new,
						  XmNleftAttachment, XmATTACH_WIDGET,
						  XmNleftWidget, AppWidgets.btn_new,
						  XmNrightAttachment, XmATTACH_NONE, 
						  NULL);
    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_open, open_xpm, showlbl);

     /* add callback for open file btn */
    XtAddCallback(AppWidgets.btn_open, XmNactivateCallback,
		  OpenCallback, NULL);


    /* setup the save file btn */
    AppWidgets.btn_save = XtVaCreateManagedWidget("btnsave",
						    xmPushButtonWidgetClass,
						    AppWidgets.btnbar,
						    XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
						    XmNtopWidget, AppWidgets.btn_new,
						    XmNbottomAttachment,
						    XmATTACH_OPPOSITE_WIDGET,
						    XmNbottomWidget, AppWidgets.btn_new,
						    XmNleftAttachment, XmATTACH_WIDGET,
						    XmNleftWidget, AppWidgets.btn_open,
						    XmNrightAttachment, XmATTACH_NONE, 
						    NULL);
    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_save, save_xpm, showlbl);

    /* add callback for the save btn */
    XtAddCallback(AppWidgets.btn_save, XmNactivateCallback,
		  SaveCallback, NULL);

    /* setup the print file btn */
    AppWidgets.btn_print = XtVaCreateManagedWidget("btnprint",
						    xmPushButtonWidgetClass,
						    AppWidgets.btnbar,
						    XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
						    XmNtopWidget, AppWidgets.btn_new,
						    XmNbottomAttachment,
						    XmATTACH_OPPOSITE_WIDGET,
						    XmNbottomWidget, AppWidgets.btn_new,
						    XmNleftAttachment, XmATTACH_WIDGET,
						    XmNleftWidget, AppWidgets.btn_save,
						    XmNrightAttachment, XmATTACH_NONE, 
						    NULL);
    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_print, print_xpm, showlbl);

    /* add callback for the print btn */
    XtAddCallback(AppWidgets.btn_print, XmNactivateCallback,
		  PrintCallback, NULL);


    /* setup the execute SQL btn */
    AppWidgets.btn_exe = XtVaCreateManagedWidget("btnexe",
						    xmPushButtonWidgetClass,
						    AppWidgets.btnbar,
						    XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
						    XmNtopWidget, AppWidgets.btn_new,
						    XmNbottomAttachment,
						    XmATTACH_OPPOSITE_WIDGET,
						    XmNbottomWidget, AppWidgets.btn_new,
						    XmNleftAttachment, XmATTACH_WIDGET,
						    XmNleftWidget, AppWidgets.btn_print,
						    XmNrightAttachment, XmATTACH_NONE, 
						    NULL);
    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_exe, execute_xpm, showlbl);

    /* add callback for execute SQL btn */
    XtAddCallback(AppWidgets.btn_exe, XmNactivateCallback,
		  ExecSQLCallback, NULL);


    /* setup the spool results btn */
    AppWidgets.btn_spool = XtVaCreateManagedWidget("btnspool",
						    xmPushButtonWidgetClass,
						    AppWidgets.btnbar,
						    XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
						    XmNtopWidget, AppWidgets.btn_new,
						    XmNbottomAttachment,
						    XmATTACH_OPPOSITE_WIDGET,
						    XmNbottomWidget, AppWidgets.btn_new,
						    XmNleftAttachment, XmATTACH_WIDGET,
						    XmNleftWidget, AppWidgets.btn_exe,
						    XmNrightAttachment, XmATTACH_NONE, 
						    NULL);
    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_spool, spool_xpm, showlbl);

    /* add callback for spoll SQL btn */
    XtAddCallback(AppWidgets.btn_spool, XmNactivateCallback,
		  SpoolCallback, NULL);

    /* setup the db connect btn */
    AppWidgets.btn_connect = XtVaCreateManagedWidget("btnconnect",
						    xmPushButtonWidgetClass,
						    AppWidgets.btnbar,
						    XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
						    XmNtopWidget, AppWidgets.btn_new,
						    XmNbottomAttachment,
						    XmATTACH_OPPOSITE_WIDGET,
						    XmNbottomWidget, AppWidgets.btn_new,
						    XmNleftAttachment, XmATTACH_WIDGET,
						    XmNleftWidget, AppWidgets.btn_spool,
						    XmNrightAttachment, XmATTACH_NONE, 
						    NULL);
    /* set button image */
    InstallLabeledPixmap(AppWidgets.btn_connect, connect_xpm, showlbl);

    /* add callback for the connect btn */
    XtAddCallback(AppWidgets.btn_connect, XmNactivateCallback,
		  ConnectCallback, NULL);

#ifdef TOOLTIP
    XtAppAddActions(app, actions, XtNumber(actions));
    toolTrans = XtParseTranslationTable(transTable);
    XtOverrideTranslations(AppWidgets.btn_new, toolTrans);
    XtOverrideTranslations(AppWidgets.btn_open, toolTrans);
    XtOverrideTranslations(AppWidgets.btn_save, toolTrans);
    XtOverrideTranslations(AppWidgets.btn_print, toolTrans);
    XtOverrideTranslations(AppWidgets.btn_exe, toolTrans);
    XtOverrideTranslations(AppWidgets.btn_spool, toolTrans);
    XtOverrideTranslations(AppWidgets.btn_connect, toolTrans);
#endif


    /* setup the paned widget */
    AppWidgets.pane = XtVaCreateManagedWidget("pane",
					      xmPanedWindowWidgetClass,
					      AppWidgets.mainwindow,
					      XmNsashHeight, 8,
					      NULL);

    /* setup the SQL window */
    AppWidgets.sqlwindow = XmCreateScrolledText(AppWidgets.pane, "sqlwindow", NULL, 0);
    XtManageChild(AppWidgets.sqlwindow);
    XtVaSetValues(AppWidgets.sqlwindow,
		  XmNeditMode, XmMULTI_LINE_EDIT,
		  NULL);

    /* add callback for the SQL window */
    XtAddCallback(AppWidgets.sqlwindow, XmNmodifyVerifyCallback,
		  TextModifiedCallback, NULL);
    
    /* setup the result window */
    AppWidgets.resultwindow = XmCreateScrolledText(AppWidgets.pane, "resultwindow", NULL, 0);
    XtManageChild(AppWidgets.resultwindow);
    XtVaSetValues(AppWidgets.resultwindow,
		  XmNautoShowCursorPosition, FALSE,
		  XmNcursorPositionVisible, FALSE,
		  XmNeditMode, XmMULTI_LINE_EDIT,
		  XmNeditable, FALSE,
		  NULL);
   
    /* create the menu */
    AppWidgets.menu = CreateMenuBar(AppWidgets.mainwindow);

    /* create the message label's frame */
    AppWidgets.msgframe = XtVaCreateManagedWidget("msgframe",
						  xmFrameWidgetClass,
						  AppWidgets.mainwindow,
						  XmNshadowType, XmSHADOW_ETCHED_IN,
						  NULL);

    /* create the message window */
    AppWidgets.message = XtVaCreateManagedWidget("message",
						 xmLabelWidgetClass,
						 AppWidgets.msgframe,
						 XmNmarginHeight, 4,
						 XmNmarginWidth, 4,
						 XmNalignment, XmALIGNMENT_BEGINNING,
						 NULL);

    /* set work areas for the main window */
    XtVaSetValues(AppWidgets.mainwindow,
		  XmNmenuBar, AppWidgets.menu,
		  XmNcommandWindow, AppWidgets.btnbar,
		  XmNworkWindow, AppWidgets.pane,
		  XmNmessageWindow, AppWidgets.msgframe,
		  XmNshowSeparator, FALSE,
		  NULL);

    /* set app's icon */
    SetupIcon(AppWidgets.shell, icon_xpm);

    /* get saved app options */
    MQUEL_GetOptions();

    /* initialize the buffer list */
    if(LLIST_Init() == NULL){
      fprintf(stderr, "Error: Out of memory.\n");
      exit(-1);
    }

    /* add the scratch buffer to the buffer list */
    if((buffer_curr = DB_OpenScratchBuffer()) == NULL){
      fprintf(stderr, "Error: Out of memory.\n"); 
      LLIST_Remove();
      exit(-1);
    }

    buffer_curr = DB_SetBuffer(buffer_curr);

    /* start the application */
    XtRealizeWidget(AppWidgets.shell);

    /* process cmd line */
    if(MQUEL_GetCmdLineArg(argc, argv))
      DB_Connect();

    /* redirect stderr so explain qy results do to the result window */
    DB_TrapStderr();

    XtAppMainLoop(app);
}

/************************************************************************/
/* Function: MQUEL_GetOptions                                           */
/* Purpose : gets app option from setup file                            */
/* Params  :                                                            */
/* Returns : 1 on SUCCESS / 0 on FAILURE                                */
/* Notes   :                                                            */
/************************************************************************/

int MQUEL_GetOptions(void)
{
  FILE *optf;
  int col_sep;
  char home[MAX_PATH_LEN];    
  char db_msg[ERR_MSG_SIZE];  
  int toggle_state;

  strcat(strcpy(home, getenv("HOME")), "/.mpsql");

  if((optf = fopen(home, "r")) != NULL){
    fscanf(optf, "%s\n",&dbname);
    fscanf(optf, "%s\n",&host);
    fscanf(optf, "%s\n",&port);
    fscanf(optf, "%s\n",&printer);
    fscanf(optf, "%s\n",&spool_file);
    fscanf(optf, "%d\n",&col_width);
    fscanf(optf, "%d\n",&col_sep);
    SetColSeparator(col_sep);
    fscanf(optf, "%d\n",&toggle_state);
    XmToggleButtonSetState(AppWidgetsPtr->tglspool, toggle_state, TRUE);
    fscanf(optf, "%d\n",&toggle_state);
    XmToggleButtonSetState(AppWidgetsPtr->tglcol, toggle_state, TRUE);
    fscanf(optf, "%d\n",&toggle_state);
    XmToggleButtonSetState(AppWidgetsPtr->tglecho, toggle_state, TRUE);
  
    if(fclose(optf) != 0){
      sprintf(db_msg, "Error closing options file:\n%s", home);
      ErrorMsg("Save Options", db_msg);
      return 0;
    }
    return 1;
  }
  else{
   strcpy(dbname, "postgres");
   strcpy(host, "localhost");
   strcpy(port, "5432");
   strcpy(printer, "lpr");
   strcpy(spool_file, "/tmp/spool.txt");
   SetColSeparator(NONE);
   col_width = 12;
   return 0;
  }
}

/************************************************************************/
/* Function: MQUEL_SaveOptions                                          */
/* Purpose : saves app options to setup file                            */
/* Params  :                                                            */
/* Returns : 1 on SUCCESS / 0 on FAILURE                                */
/* Notes   :                                                            */
/************************************************************************/

int MQUEL_SaveOptions(void)
{
  FILE *optf;
  char home[MAX_PATH_LEN];  
  char db_msg[ERR_MSG_SIZE];   

  strcat(strcpy(home, getenv("HOME")), "/.mpsql");

  if((optf = fopen(home, "w+")) == NULL){
    sprintf(db_msg, "Error opening options file:\n%s", home);
    ErrorMsg("Save Options", db_msg);
    return 0;
  }

  DB_Grab();

  fprintf(optf, "%s\n", dbname);
  fprintf(optf, "%s\n", host, optf);
  fprintf(optf, "%s\n", port, optf);
  fprintf(optf, "%s\n", printer, optf);
  fprintf(optf, "%s\n", spool_file, optf);
  fprintf(optf, "%d\n", col_width);
  fprintf(optf, "%d\n", GetColSeparatorId());
  fprintf(optf, "%d\n", XmToggleButtonGetState(AppWidgetsPtr->tglspool));
  fprintf(optf, "%d\n", XmToggleButtonGetState(AppWidgetsPtr->tglcol));
  fprintf(optf, "%d\n", XmToggleButtonGetState(AppWidgetsPtr->tglecho));

  fflush(optf);

  DB_UnGrab();

  if(fclose(optf) != 0){
    sprintf(db_msg, "Error closing options file:\n%s", home);
    ErrorMsg("Save Options", db_msg);
    return 0;
  } 
  sprintf(db_msg, "Saved file: %s", home);
  XtVaSetValues(AppWidgetsPtr->message,
		XtVaTypedArg, XmNlabelString, XmRString,
		db_msg, strlen(db_msg)+1, NULL);
  return 1;
}

/************************************************************************/
/* Function: MQUEL_GetCmdLineArg                                        */
/* Purpose : gets the command line args                                 */
/* Params  : argc : arg count                                           */
/*           argv : array of cmd line args                              */
/* Returns : 1 if dbname passed) / 0 on no dbname passed                */
/* Notes   :                                                            */
/************************************************************************/

int MQUEL_GetCmdLineArg(int argc, char **argv)
{
  extern char* optarg;
  extern int optind, opterr;
  int c;
  int db = 0;

  while((c = getopt(argc, argv, "D:H:P:h")) != EOF){
    switch(c){
    case 'D':
      strcpy(dbname, optarg);
      db = 1;
      break;
    case 'H':
      strcpy(host, optarg);
      break;
    case 'P':
      strcpy(port, optarg);
      break;
    case 'h':
      fprintf(stdout, "usage: mpsql -D[database name] -H[host] -P[port] -h[help]\n");
      exit(0);
    default:
      exit(-1);
    }
  }

  if(db)
    return 1;
  else
    return 0;
}
