
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>

#include <qfile.h>
#include <qfileinf.h>
#include <qmsgbox.h>
#include <stdlib.h>

#include "KColorMLE.h"



bool KSyntaxMultiLineEdit::loadFile(const QString& name, int mode)
{
    int fdesc;
    struct stat s;
    char *addr;
    QFileInfo info(name);

    if(!info.exists())
	{
		 
	  QString msg = klocale->translate("The file '") + name + klocale->translate("'\ndoes not exist");
      QMessageBox::warning(this, klocale->translate("Sorry:"),
				  msg, klocale->translate("OK"), "", "", 0, 0 );
      return false;
    }

    if(info.isDir())
	{
      QMessageBox::warning(
			   this,
			   klocale->translate("Sorry:"),
			   klocale->translate("You have specificated a directory"),
			   klocale->translate("OK"),
			   "", "", 0,0 );		
      return false;
    }


   if(!info.isReadable())
	{
      QMessageBox::warning(
				  this,
				  klocale->translate("Sorry"),
                  klocale->translate("You do not have read permission to this file."),
                  klocale->translate("OK"),
				  "", "", 0,0 );
      return false;
    }


    fdesc = open(name, O_RDONLY);

    if(fdesc == -1) 
	{
        switch(errno) 
		{
        case EACCES:
		  	QMessageBox::warning(
				  this,
				  klocale->translate("Sorry"),
                  klocale->translate("You do not have read permission to this file."),
                  klocale->translate("OK"),
				  "", "", 0,0 );
		  	return false;

        default:
            QMessageBox::warning(
				  this, 
				  klocale->translate("Sorry"),				 
				  klocale->translate("An Error occured while trying to open this Document"),
                  klocale->translate("OK"),
				  "", "", 0,0 );
            return false;
        }
    }
    
    fstat(fdesc, &s);
    addr = (char *)mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fdesc, 0);

    setAutoUpdate(false);

// The following is a horrible hack that we need to put up with until
// Qt 1.3 comes along. The problem is that QMultiLineEdit::setText(char*)
// is O(n^2) which makes loading larger files very slow. We resort here to 
// a workaroud which load the file line by line. I use a memmap construction
// but probably a QTextStream is just about as fast.

    if(mode & EDITOR_OPEN_INSERT) 
	{
     	register long int  i;
      	char *beginning_of_line;
      	beginning_of_line = addr;
      	int line, col;
      
      	char c = '\n';

      	if(s.st_size != 0)
		{
			c = *(addr + s.st_size - 1 ); // save the last character of the file
      	}
      
      		// this boolean will indicate whether we already have inserted the first line.
      	bool past_first_line = false;

      	getCursorPosition(&line,&col);

      		// let's save the position of the cursor
      	int save_line = line;
      	int save_col = col;

      	for(i = 0; i < s.st_size; i++)
		{

			if( *(addr + i) == '\n' )
			{
			  	*(addr + i) = '\0';

	  			if(!past_first_line)
				{
	    			QString string;
	    			string = beginning_of_line;
	    			string += '\n';
	    			insertAt(string,line,col);
	    			past_first_line = true;
	  			}
	  			else
				{
	    			insertLine(beginning_of_line,line);
			  	}

	  			line ++;
	  			beginning_of_line = addr + i + 1;
			}
      	}

      	// What if the last char of the file wasn't a newline?
      	// In this case we have to manually insert the last "couple" of characters.
      	// This routine could be avoided if I knew for sure that I could
      	// memmap a file into a block of memory larger than the file size.
      	// In that case I would simply put a zero after the last char in the file.
      	// and the above would go through almost unmodified. Well, I don't, so 
      	// here we go:

      	if( c != '\n')
		{ // we're in here if s.st_size != 0 and last char != '\n'

			char* buf = (char*)malloc(addr + i - 1 - beginning_of_line + 2);
			strncpy(buf, beginning_of_line, addr + i - 1 - beginning_of_line +1);
			buf[ addr + i - 1 - beginning_of_line + 2 ] = '\0';
			append(buf);

			free(buf);
      
	      }
    		  // restore the initial Curosor Position
		  setCursorPosition(save_line,save_col);
	}
    else
	{ // Not inserting but loading a completely new file.

      	register long int  i;
      	char *beginning_of_line;
      	beginning_of_line = addr;

      		// eradicate the old text.
      	clear();
      	char c = '\n';

      	if(s.st_size != 0)
		{
			c = *(addr + s.st_size - 1 ); // save the last character of the file
      	}

      	for(i = 0; i < s.st_size; i++)
		{
			if( *(addr + i) == '\n' )
			{
	  			*(addr + i) = '\0';
	  			append(beginning_of_line);
	  			beginning_of_line = addr + i + 1;
			}
      	}

      		// Same consideration as above:
      	if( c != '\n')
		{ // we're in here if s.st_size != 0 and last char != '\n'

			char* buf = (char*)malloc(addr + i - 1 - beginning_of_line + 2);
			strncpy(buf, beginning_of_line, addr + i - 1 - beginning_of_line + 1);
			buf[addr + i - 1 - beginning_of_line + 1] = '\0';
			append(buf);

			free(buf);
      	}
    }

    munmap(addr, s.st_size);

    setAutoUpdate(true);
    
    	// clear the undo list, since after loading there is nothing to be undone
    UndoRedoList.Clear();
        // after loading doc is not dirty:
    setDirty(false);
    
    	// init the state cache after loading a new file; repaint after that
    initStateCache();
    deselect();
	setCursorPosition(0, 0); 	// deselect reicht nicht aus!

//	updateCellWidth();
    repaint();

    return true;
}



bool KSyntaxMultiLineEdit::saveTo(const QString& fn)
{
    if(!fn.length())
    {
    	warning("no filename\n");
		return false;
    }
    
	QString backFile;
    QFile f(fn);
    QFileInfo fi(fn);
    bool exists = f.exists();
    struct stat st;
    int stat_ok = -1;
    
    if(exists)
    {
 		stat_ok = stat(fn.data(), &st);

		backFile = fi.dirPath(true) + "/";
		backFile += fi.baseName() + ".bak";
		rename(fn, backFile);
    }
    
    if(!f.open(IO_WriteOnly | IO_Truncate)) 
	{
      	rename(backFile, fn);	// back to original name
      	QMessageBox::warning(this,
			   				 klocale->translate("Sorry"),
			   				 klocale->translate("Could not save the document.\n"),
			   				 klocale->translate("OK"),
			   				 "", "", 0,0);
    	return false;
    }

		// do the file operations...
	QTextStream t(&f);
	int line_count = numLines();
	for(int i = 0 ; i < line_count ; i++)
		t << textLine(i) << '\n';
	f.close();

	if(exists)
		chmod(fn, st.st_mode); // preserve filepermissions

        // after saving doc is not dirty:
    setDirty(false);

    	// clear the undo list, since after saving changes can't be undone any more:
    UndoRedoList.Clear();

    return true;
}




