 /*  R : A Computer Langage for Statistical Data Analysis
 *  Copyright (C) 1995  Robert Gentleman and Ross Ihaka
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "wincons.h"
#include "Fileio.h"
#include "IOStuff.h"
#include "Parse.h"

char REdfilename[MAX_PATH];
//static int inFlag=1;
static int FindMsg, offset;
static WNDPROC oldEd;

static HWND REditFrame, REditWnd, ModelessDlg;
LRESULT FAR PASCAL EdProc(HWND, UINT, WPARAM,LPARAM);

HMENU RMenuEdit, RMenuEditWin;

int InitEditor(void) 
{
        MDICREATESTRUCT mdicreate;
        RECT r;

        GetClientRect(RClient, &r);
        mdicreate.szClass = REditClass;
        mdicreate.szTitle = "R Editor";
        mdicreate.hOwner = RInst;
        mdicreate.x = CW_USEDEFAULT;
        mdicreate.y = CW_USEDEFAULT;
        mdicreate.cx = CW_USEDEFAULT;
        mdicreate.cy = CW_USEDEFAULT;
        mdicreate.style = 0;
        mdicreate.lParam = NULL;
        REditFrame = (HWND) (UINT) SendMessage(RClient, WM_MDICREATE, 0,
                (LONG) (LPMDICREATESTRUCT) &mdicreate);
        if( REditFrame == NULL )
                return 0;
        
        GetClientRect(REditFrame, &r);
        REditWnd = CreateWindow("Edit", NULL,
                        WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL |
                        ES_MULTILINE  ,
                        0, 0, (r.right - r.left), (r.bottom - r.top),
                        REditFrame, (HMENU) RRR_TEDIT, RInst, NULL);
		/*probably we should get the font from the main window and use it*/
		SendMessage(REditWnd, WM_SETFONT, (WPARAM)GetStockObject(OEM_FIXED_FONT),
        MAKELPARAM(TRUE,0L));

		oldEd = (WNDPROC) SetWindowLong(REditWnd, GWL_WNDPROC, (LONG) EdProc);

        DragAcceptFiles(REditFrame, TRUE);
		FindMsg = RegisterWindowMessage(FINDMSGSTRING);
		ModelessDlg=NULL;
        return 1;
}



void CloseEd(void) {
        SendMessage(RClient, WM_MDIDESTROY, (WPARAM) (HWND) REditFrame, 0L);
        SetFocus(RConsoleFrame);
}
/*Empty the Edit Window */

static void EmptyEd(void) {
        int nchars;
        char Rbuf[1];

        nchars = Edit_GetTextLength(REditWnd);
        Rbuf[0]='\0';
        Edit_SetSel(REditWnd, 0, nchars);
        Edit_ReplaceSel(REditWnd, Rbuf);
}

/* Set the contents back to the original supplied argument */
static int RFileLen(FILE *file)
{
	int i, j;

	i = ftell(file);
	fseek(file, 0, SEEK_END);
	j= ftell(file);
	fseek(file, i, SEEK_SET);
	return j;
}

static void RefreshEd(void) {
    int c, j, i=0, lines=0;
    char *tmp;
    FILE *fp;

	if( fp = fopen(REdfilename, "rt") ) {
		while( (c = fgetc(fp)) != EOF ) {
			if( c=='\n' ) lines++;
			i++;
		}
		fseek(fp, 0, SEEK_SET);
		if( NULL == (tmp = (char *) malloc(i+lines+1)) )
		{ 
			fclose(fp);
			MessageBox(REditWnd, "Out of memory",
                                "R Editor", MB_OK | MB_ICONEXCLAMATION);
		}
		j=0;
		while( (c = fgetc(fp)) !=EOF ) {
			if( c== '\n' )
				tmp[j++]='\r';
			tmp[j]= c;
			j++;
		}
		tmp[j]='\0';

		fclose(fp);
		SetWindowText(REditWnd, tmp);
		free(tmp);
		Edit_SetSel(REditWnd, 0, 0);
		SetFocus(REditWnd);
		return;
	}
	return;
}

LRESULT FAR PASCAL EdProc(HWND hWnd, UINT message, WPARAM wParam,
        LPARAM lParam)
{ 
	int i;
	static HWND ontop=0;

	switch(message) {
	case WM_CREATE:
		if( !ontop ) {
			ontop = hWnd;
		}
		break;
	case WM_WINDOWPOSCHANGED:
		if( ontop ) {
			WINDOWPOS FAR* pWP = (WINDOWPOS FAR*) lParam;
			if( pWP->hwnd != ontop )
				SetWindowPos(ontop, HWND_TOP, 0, 0, 0, 0,
				SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
		}
		break;
	case WM_LBUTTONDOWN:
			if( R_ValidFind() )
				PostMessage(REditWnd, EM_GETSEL, 0, (LPARAM) &offset);
			i=1;
			break;
	case WM_CLOSE:
		if( ontop == hWnd )
			ontop = 0;
		break;
	}
	return CallWindowProc( oldEd, hWnd, message, wParam, lParam);
}

LRESULT FAR PASCAL REditWndProc(HWND hWnd, UINT message, WPARAM wParam,
        LPARAM lParam)
{
	LPFINDREPLACE pfr;
	static HWND ontop = 0;
	int enable, i;
	MSG msg;

        switch(message) {
		case WM_CREATE:
			if( !ontop ){
				ontop = hWnd;
			}
        case WM_MDIACTIVATE:
            if((HWND) lParam == hWnd ) {
                SendMessage(RClient, WM_MDISETMENU, (WPARAM) RMenuEdit,
                (LPARAM) RMenuEditWin);
                DrawMenuBar(GetParent(RClient));
                SetFocus(REditWnd);
				R_Editing = 1;
                return 0;
             }
             else if( R_Editing ) { /* don't let them switch to another window */
                           MessageBox(hWnd, "You must quit the editor before doing something else.",
                               "R Editor", MB_OK | MB_ICONEXCLAMATION);
                        PostMessage(RClient, WM_MDIACTIVATE, (UINT) REditFrame,0);
						//i=SetWindowPos(REditFrame, HWND_TOP, 0, 0, 0, 0,
						// SWP_NOMOVE | SWP_NOSIZE);
						SetFocus(REditWnd);
						   return 0;
                        }
			break;
		case WM_WINDOWPOSCHANGED:
			//if( ontop) {
			//	WINDOWPOS FAR* pWP = (WINDOWPOS FAR*) lParam;
			//	if (pWP->hwnd != ontop)
			//		SetWindowPos(ontop, HWND_TOP, 0, 0, 0, 0,
			//			SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
			//}
			break;
		case WM_SYSCOMMAND: /* Disable minimizing */
                        if( (wParam & 0xFFF0) == SC_MINIMIZE ) {
                            MessageBox(hWnd, "You can't iconify the R Editor",
                                "R Data Entry", MB_OK | MB_ICONEXCLAMATION);
                            return 0;
                        }
                        break;
				 case WM_INITMENUPOPUP:
					 if( lParam == 1 ) {
						 enable = ModelessDlg == NULL ? MF_ENABLED: MF_GRAYED;
						 EnableMenuItem ((HMENU) wParam, RRR_FIND, enable);
						 EnableMenuItem ((HMENU) wParam, RRR_FINDNEXT, enable);
						 EnableMenuItem ((HMENU) wParam, RRR_REPLACE, enable);
						 return 0;
					 }
                case WM_DROPFILES:
                        R_ProcessDropFiles((HANDLE) wParam, 2);
                        EmptyEd();
                        RefreshEd();
                        return 0;
                case WM_COMMAND:
                        switch (GET_WM_COMMAND_ID(wParam,lParam)) {
                                case RRR_QUIT:
                                        R_Editing=0;
                                        return 0;
                                case RRR_CLEAR:
                                        EmptyEd();
                                        return 0;
                                case RRR_REFRESH:
                                        EmptyEd();
                                        RefreshEd();
                                        return 0;
								case RRR_FIND:
									SendMessage(REditWnd, EM_GETSEL, 0, (LPARAM) &offset);
									ModelessDlg = R_FindDlg(REditFrame);
									return 0;
								case RRR_FINDNEXT:
									SendMessage(REditWnd, EM_GETSEL, 0, (LPARAM) &offset);
									if (R_ValidFind())
										R_FindNextText(REditWnd, ModelessDlg, &offset);
									else
										ModelessDlg = R_FindDlg(REditFrame);
									return 0;
								case RRR_REPLACE:
									SendMessage(REditWnd, EM_GETSEL, 0, (LPARAM) &offset);
									ModelessDlg = R_ReplaceDlg(REditFrame);
									return 0;
                                case RRR_TEDIT:
                                        /* if there isn't enough room empty the buffer */
                                        switch(GET_WM_COMMAND_CMD(wParam,lParam)) {
                                                case EN_ERRSPACE:
                                                case EN_MAXTEXT:
                                                        MessageBox(hWnd,"Text is too large.","R Editor",
                                                                MB_ICONEXCLAMATION | MB_OK);
                                                        EmptyEd();
                                                        return 0;
                                        }
                                        break;
                        }
                        break;
                case WM_SIZE:
                        MoveWindow(REditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
                        break;
                case WM_CLOSE:
                case WM_DESTROY:
                        R_Editing=0;
						if( hWnd == ontop )
							ontop = 0;
						if( ModelessDlg != NULL ) {
							EndDialog(ModelessDlg, FALSE);
							ModelessDlg = NULL;
						}
                        return(0);
				default:		// Process Find-Replace Messages
					if( message == FindMsg ){
						pfr = (LPFINDREPLACE) lParam;
						if( pfr->Flags & FR_DIALOGTERM)
							ModelessDlg == NULL;

						if( pfr->Flags & FR_FINDNEXT )
							if( !R_FindText(REditWnd, ModelessDlg, &offset, pfr) )
								MessageBox(REditFrame, "Text not found", "R Editor", 
										MB_ICONEXCLAMATION | MB_OK);
							else {
								/*EndDialog(ModelessDlg, FALSE);
							    SetFocus(REditWnd);
								SendMessage (REditWnd, EM_SETSEL, 
									offset-strlen(pfr->lpstrFindWhat), offset) ;
								SendMessage (REditWnd, EM_SCROLLCARET, 0, 0);*/
							}

						if( pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL)
							if( !R_ReplaceText(REditWnd, ModelessDlg, &offset, pfr))
								MessageBox(REditFrame, "Text not found", "R Editor", 
										MB_ICONEXCLAMATION | MB_OK);
						if( pfr->Flags & FR_REPLACEALL)
							while (R_ReplaceText(REditWnd, ModelessDlg, &offset, pfr));
						return 0;
					}
					break;
        }
        return(DefMDIChildProc(hWnd, message, wParam, lParam));
}

void InitEd()
{
}

SEXP do_edit(SEXP call, SEXP op, SEXP args, SEXP env) 
{
        SEXP x, fn, envir;
        int i, l, n, status;
        char RTbuf[MAXELTSIZE];
        FILE *fp;

        checkArity(op, args);

        if( !InitEditor())
            error("couldn't start an editor window\n");

        x = CAR(args);
        if( TYPEOF(x) == CLOSXP )
                envir = CLOENV(x);
        else
                envir = R_NilValue;
        PROTECT(envir);

        fn = CADR(args);
        if( !isString(fn))
                error("invalid argument to edit\n");
        if( LENGTH(STRING(fn)[0]) > 0 ) 
               strcpy(REdfilename, CHAR(STRING(fn)[0]));
        else if(x!=R_NilValue || !strlen(REdfilename) ) {/* if x is R_NilValue then we are restoring and want to use the last file */
                GetTempPath(MAXELTSIZE, RTbuf);
                i=GetTempFileName(RTbuf,"NEW",0,(LPTSTR) REdfilename);
        }

        if( x != R_NilValue ) {
            if((fp = fopen(REdfilename, "wt")) == NULL )
                error("unable to open file for writing\n");
            x = deparse1(x,0);
            for(i = 0; i<LENGTH(x); i++ )
                fprintf(fp, "%s\n", CHAR(STRING(x)[i]));
            fclose(fp);
        }
        
 
        RefreshEd();

        //inFlag = 1;
        do{
                EventLoop(ModelessDlg);
        }
        while(R_Editing);

        n=Edit_GetLineCount(REditWnd);
                
        if( !(fp = fopen(REdfilename, "wt")) )
                error("unable to open file for writing");
        for(i=0 ; i<n ; i++) {
                l=Edit_GetLine(REditWnd,i,RTbuf,MAXELTSIZE-1);
                RTbuf[l] = '\0';
                fprintf(fp, "%s\n",RTbuf);
        }
        fclose(fp);
        
        CloseEd();

        fp=fopen(REdfilename, "rt");
        R_ParseCnt = 0;
        PROTECT(x = R_ParseFile(fp, -1, &status));
        if (status != PARSE_OK) 
            errorcall(call,"An error occurred on line %d\n use a command like\n x<-edit()\n to recover\n",R_ParseError);
        else
                fclose(fp);
        R_ResetConsole();
        x = WinCallEval(x, R_GlobalEnv);

		if (x == R_MissingArg) /* an error occurred */
			return R_NilValue;
        if( TYPEOF(x) == CLOSXP && envir != R_NilValue)
            CLOENV(x) = envir;
        UNPROTECT(2);
        return x;
}
