/* ****************************************************************************
  This file is part of KBabel

  Copyright (C) 1999-2000 by Matthias Kiefer
                            <matthias.kiefer@gmx.de>
		2002	  by Stanislav Visnovsky
			    <visnovsky@nenya.ms.mff.cuni.cz>

  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 "catalogitem.h"
#include "catalogitem_private.h"
#include "editcmd.h"
#include "resources.h"
#include "libgettext/pofiles.h"

#include "tagextractor.h"
#include "argextractor.h"

#include <qtextstream.h>
#include <qtextcodec.h>
#include <FlexLexer.h>
#include <qdom.h>

CatalogItem::CatalogItem()
{
   d=0;
   clear();
}

CatalogItem::CatalogItem(const CatalogItem& item)
{
   d=0;
   clear();
   *d=*(item.d);
}

CatalogItem::CatalogItem(QString itemStr)
{
   d=0;
   clear();

   QTextStream stream(&itemStr,IO_ReadOnly);

   read(stream);
}

CatalogItem::~CatalogItem()
{
    if( d ) delete d;
}

CatalogItem::CatalogItem(const char* msgid, const char* msgstr,
			 const char* comment)
{
   d=0;
    clear();

    d->_comment=comment;
    (*(d->_msgid).begin())=msgid;
    (*(d->_msgstr).begin())=msgstr;
    d->_valid=true;
    d->_error = None;
}

QString CatalogItem::comment() const
{
    return d->_comment;
}

QString CatalogItem::msgid(const bool noNewlines) const
{
    QString result=d->_msgid.first();
    if( noNewlines )
    {
	result.replace(QRegExp("\\n"),"");
    }
    return result;
}

QString CatalogItem::msgstr(const bool noNewlines) const
{
    QString result=d->_msgstr.first();
    if( noNewlines )
    {
	result.replace(QRegExp("\\n"),"");
    }
    return result;
}


QStringList CatalogItem::msgidAsList(bool plural) const
{
   QString str;
   if(d->_gettextPluralForm && plural)
       str = d->_msgid.last();
   else
       str = d->_msgid.first();
   
   QStringList list=QStringList::split("\n",str);

   if(str.left(1)=="\n")
      list.prepend("");

   if(list.isEmpty())
      list.append("");

   return list;
}


QStringList CatalogItem::msgstrAsList(int nr) const
{
   QString str;
   if(d->_gettextPluralForm && nr > 0)
   {
       QStringList::ConstIterator it = d->_msgstr.at(nr);
       if(it != d->_msgstr.end())
       {
           str = *it;
       }
       else
       {
           kdDebug(KBABEL) << "request for non existing plural form " 
               << nr << endl;
       }
   }
   else
   {
       str = d->_msgstr.first();
   }
   
   QStringList list=QStringList::split("\n",str);

   if(str.left(1)=="\n")
      list.prepend("");

   if(list.isEmpty())
      list.append("");

   return list;
}

QStringList CatalogItem::commentAsList() const
{
   QStringList list=QStringList::split("\n",comment());

   return list;
}

bool CatalogItem::isValid() const
{
    return d->_valid;
}

void CatalogItem::setMsgid(QString msg)
{
    d->_msgid=msg;
}

void CatalogItem::setMsgstr(QString msg)
{
    d->_msgstr=msg;
}

void CatalogItem::setComment(QString com)
{
    d->_comment=com;
}

int CatalogItem::errors() const
{
    return d->_error;
}

QStringList CatalogItem::tagList()
{
    if(!d->_haveTagList)
    {
        TagExtractor te(msgid(true));
        d->_tagList = QStringList(te.tags());
	d->_haveTagList = true;
    }

    return d->_tagList;
}

QStringList CatalogItem::argList()
{
    if(!d->_haveArgList)
    {
        ArgExtractor te(msgid(true));
        d->_argList = QStringList(te.args());
    }

    return d->_argList;
}


bool CatalogItem::isFuzzy() const
{
   bool flag=false;
   if( d->_comment.contains(", fuzzy") )
     flag=true;

   return flag;
}

bool CatalogItem::isCformat() const
{
   bool flag=false;
   if(d->_comment.contains(", c-format"))
     flag=true;

   return flag;
}

bool CatalogItem::isNoCformat() const
{
   bool flag=false;
   if(d->_comment.contains(", no-c-format"))
     flag=true;

   return flag;
}

bool CatalogItem::isUntranslated() const
{
   return d->_msgstr.first().isEmpty();
}

bool CatalogItem::isPluralForm() const
{
    return d->_gettextPluralForm;
}

int CatalogItem::totalLines() const
{
   int lines=0;
   if(!d->_comment.isEmpty())
   {
      lines = d->_comment.contains('\n')+1;
   }
   int msgidLines=0;
   QStringList::ConstIterator it;
   for(it=d->_msgid.begin(); it != d->_msgid.end(); ++it)
   {
      msgidLines += (*it).contains('\n')+1;
   }
   int msgstrLines=0;
   for(it=d->_msgstr.begin(); it != d->_msgstr.end(); ++it)
   {
      msgstrLines += (*it).contains('\n')+1;
   }

   if(msgidLines>1)
      msgidLines++;
   if(msgstrLines>1)
      msgstrLines++;

   lines+=( msgidLines+msgstrLines );

   return lines;
}


void CatalogItem::setSyntaxError(bool on)
{
	if(on)
		d->_error = d->_error | Syntax;
	else
		d->_error = d->_error & ~Syntax;
}

QPtrList<EditCommand> CatalogItem::removeFuzzy(bool doIt)
{
   QPtrList<EditCommand> editList;
   editList.setAutoDelete(false);

   QString comment=d->_comment;

   if(isFuzzy())
   {
       EditCommand *cmd;
       QString fuzzyStr(", fuzzy");

       int offset;
       offset=comment.find(fuzzyStr);
       while(offset>=0)
       {
          cmd = new DelTextCmd(offset,fuzzyStr);
          cmd->setPart(EditCommand::Comment);
          editList.append(cmd);
	  
          comment.remove(offset,fuzzyStr.length());

          offset=comment.find(fuzzyStr,offset+1);
       }
       
       // remove empty comment lines
       if( comment.contains( QRegExp("^#\\s*$") ))
       {
          cmd = new DelTextCmd(0,comment);
          cmd->setPart(EditCommand::Comment);
          editList.append(cmd);
	  
          comment="";
       }
       if( comment.contains( QRegExp("\n#\\s*$") ))
       {
          offset=comment.find( QRegExp("\n#\\s*$") );
          while(offset>=0)
          {
             cmd = new DelTextCmd(offset,comment.mid(offset));
             cmd->setPart(EditCommand::Comment);
             editList.append(cmd);

             comment.remove(offset,comment.length()-offset);

             offset=comment.find( QRegExp("\n#\\s*$"), offset+1 );
          }
       }
       if( comment.contains( QRegExp("\n#\\s*\n") ))
       {
          offset=comment.find( QRegExp("\n#\\s*\n") )+1;
          while(offset>=0)
          {
             int endIndex=comment.find("\n",offset)+1;

             cmd = new DelTextCmd(offset,comment.mid(offset,endIndex-offset));
             cmd->setPart(EditCommand::Comment);
             editList.append(cmd);

             comment.remove(offset,endIndex-offset);

             offset=comment.find( QRegExp("\n#\\s*\n"), offset+1 );
          }
       }
       
       if(doIt)
          d->_comment=comment;

   }

   return editList;
}



QPtrList<EditCommand> CatalogItem::addFuzzy(bool doIt)
{
   QPtrList<EditCommand> editList;
   editList.setAutoDelete(false);


   if(!isFuzzy())
   {
       EditCommand *cmd;
       int offset=d->_comment.length();

       QString addStr;
       if(offset > 0 && d->_comment[offset-1] != '\n')
       {
           addStr='\n';
       }
       addStr+="#, fuzzy";

       cmd = new InsTextCmd(offset,addStr);
       cmd->setPart(EditCommand::Comment);
       editList.append(cmd);


       if(doIt)
          d->_comment+=addStr;
   }

   return editList;
}


void CatalogItem::processCommand(EditCommand* cmd, bool undo)
{
    if(cmd->terminator()!=0)
       return;

    DelTextCmd* delcmd = (DelTextCmd*) cmd;

    bool ins =  true;
    if (delcmd->type() == EditCommand::Delete )
       ins = undo;
    else if (delcmd->type() == EditCommand::Insert )
       ins = !undo;
    else
    {
       kdDebug() << "what kind of edit command is this?" << endl;
       return;
    }

    if ( ins )
    {
       if(delcmd->part()==EditCommand::Msgstr)
       {
          (*(d->_msgstr).begin()).insert(delcmd->offset,delcmd->str);
       }
       else if(delcmd->part()==EditCommand::Comment)
       {
          d->_comment.insert(delcmd->offset,delcmd->str);
       }
    }
    else
    { // del
       if(delcmd->part()==EditCommand::Msgstr)
       {
          (*(d->_msgstr).begin()).remove(delcmd->offset,delcmd->str.length());
       }
       else if(delcmd->part()==EditCommand::Comment)
       {
          d->_comment.remove(delcmd->offset,delcmd->str.length());
       }
    }
}


void CatalogItem::clear()
{
   if( !d ) d = new CatalogItemPrivate();
   d->_comment="";
   d->_msgid.clear();
   d->_msgid.append("");
   d->_msgstr.clear();
   d->_msgstr.append("");
   d->_valid=true;
   d->_error=None;
   d->_gettextPluralForm=false;

   d->_tagList.clear();
   d->_argList.clear();
   d->_haveTagList=false;
   d->_haveArgList=false;
}

bool CatalogItem::checkArgs(const QRegExp& comment) 
{
	if( isNoCformat() ) return true;
	bool hasError = false;

	if(!isUntranslated())
	{
		QString formatChars="dioxXucsfeEgGpn%";

		QString line=msgid();
		QStringList argList;
		
//		if( isPluralForm() ) 
		{
		    // FIXME: this is KDE specific
		    if( line.startsWith("_n:" ))
		    {
			// truncate text after first \n to get args only once
			line = line.mid(0,line.find("\\n"));
		    }
		}
		line.replace( comment, "");
		line.replace(QRegExp("\\n"),"");
		line.simplifyWhiteSpace();
		
		int index=line.find(QRegExp("%."));
		
		while(index>=0)
		{
			int endIndex=line.find(QRegExp("[^\\d]"),index+1);
			if(endIndex<0)
			{
				endIndex=line.length();
			}
			else if( formatChars.contains(line[endIndex]) )
			{
					endIndex++;
			}
			
			if(endIndex - index > 1 )
				argList.append(line.mid(index,endIndex-index));
	
			index=line.find(QRegExp("%."),endIndex);
		}
		
//		if( isPluralForm() ) 
		{
		    // FIXME: this is KDE specific
		    if( line.startsWith("_n:" ) && !argList.contains("%n") )
		    {
			argList.append("%n");
		    }
		}
		
		// generate for each plural form to be checked separately
		line=d->_msgstr.first();
		QStringList lines;
		
		// FIXME; this is KDE specific
		if( msgid().startsWith("_n:") )
		{
		    lines = QStringList::split("\\n",line);
		}
		else 
		{
		    lines.append(line);
		}

		QStringList argCache = argList;
		
		for(QStringList::Iterator i = lines.begin() ; i!=lines.end() ; i++)
		{
		    line=(*i);
		    
    		    line.replace(QRegExp("\\n"),"");
		    index=line.find(QRegExp("%."));
		    
		    argList = argCache;

		    while(index>=0)
		    {
			int endIndex=line.find(QRegExp("[^\\d]"),index+1);
			if(endIndex<0)
			{
				endIndex=line.length();
			}
			else if( formatChars.contains(line[endIndex]) )
			{
					endIndex++;
			}
	
			if(endIndex - index > 1 )
			{
				QStringList::Iterator it = argList.find(line.mid(index,endIndex-index));
				if( it!= argList.end())
				{
					argList.remove(it);
				}
				else
				{
					hasError = true;
				}
			}

			index=line.find(QRegExp("%."),endIndex);
		    }

		    if(!argList.isEmpty())
		    {
			hasError = true;
		    }
		}
	}
	
	if(hasError)
	{
		d->_error = d->_error | Args;
	}
	else
	{
		d->_error = d->_error & ~Args;
	}
			
	return !hasError;
}


bool CatalogItem::checkAccelerator(QChar accelMarker, const QRegExp& comment)
{
	bool hasError = false;
	if(!isUntranslated())
	{
		QString lineid=msgid();
		lineid.replace( comment, "");
		lineid.replace(QRegExp("\\n"),"");
		lineid.simplifyWhiteSpace();
		QString linestr=msgstr(true);
		linestr.simplifyWhiteSpace();

		QString regStr(accelMarker);
		regStr+="[^\\s]";
		QRegExp reg(regStr);
		int n = lineid.contains(reg);
		if( accelMarker == '&' ) 
		    n = n - lineid.contains(QRegExp("(&[a-z,A-Z,\\-,0-9,#]*;)|(&&(?!&+))"));
		int m = linestr.contains(reg);
		if( accelMarker == '&' ) 
		    m = m - linestr.contains(QRegExp("(&[a-z,A-Z,\\-,0-9,#]*;)|(&&(?!&+))"));

		hasError = ( n<=1 &&  m != n);
	}

	if(hasError)
	{
		d->_error = d->_error | Accel;
	}
	else
	{
		d->_error = d->_error & ~Accel;
	}
			
	return !hasError;
}


bool CatalogItem::checkEquation() 
{
	bool error = false;
	
    QString msg=d->_msgid.first();
	if(!isUntranslated() && !msg.contains('\n') 
					&& msg.contains(QRegExp("^[a-zA-Z0-9]+=.+")))
	{
		int index = msg.find('=');
		QString left = msg.left(index);
		index = d->_msgstr.first().find('=');
		if(left != d->_msgstr.first().left(index))
			error = true;
	}
	
	if(error)
	{
		d->_error = d->_error | Equation;
	}
	else
	{
		d->_error = d->_error & ~Equation;
	}

	return !error;
}


bool CatalogItem::checkForContext(const QRegExp& reg)
{
	bool error = false;

	if(!isUntranslated() && d->_msgid.first().contains(reg) 
            && d->_msgstr.first().contains(reg))
	{
		error = true;
	}
	
	if(error)
	{
		d->_error = d->_error | Context;
	}
	else
	{
		d->_error = d->_error & ~Context;
	}

	return !error;
}

bool CatalogItem::checkSingularPlural(const QRegExp& reg, const int neededLines)
{
	bool error = false;

	if(!isUntranslated() && d->_msgid.first().contains(reg))
	{
        if(neededLines <= 0 || d->_msgstr.first().contains(reg))
        {
    		error = true;
        }
        else if( d->_msgstr.first().contains(QString("\\n"))+1 != neededLines )
        {
            error = true;
        }
	}
	
	if(error)
	{
		d->_error = d->_error | SingularPlural;
	}
	else
	{
		d->_error = d->_error & ~SingularPlural;
	}

	return !error;
}



bool CatalogItem::checkXmlTags()
{
	bool error = false;

	if(!isUntranslated())
	{
		QString text = msgstr(true);
		QDomDocument doc;

		text.replace( QRegExp( "\\\\\"" ), "\"" ); // Change '\"' to '"'
		error = ! doc.setContent("<para>" + text + "</para>" );
	}

	if(error)
	{
		d->_error = d->_error | XmlTags;
	}
	else
	{
		d->_error = d->_error & ~XmlTags;
	}

	return !error;
}



int CatalogItem::checkErrors(QChar accelMarker,const QRegExp& contextInfo
        , const QRegExp& singularPlural, const int neededLines)
{
	d->_error=None;

	checkArgs(contextInfo);
	checkAccelerator(accelMarker, contextInfo);
	checkEquation();
	checkForContext(contextInfo);
    checkSingularPlural(singularPlural,neededLines);

	return d->_error;
}

QString CatalogItem::asString()
{
   QString temp;
   QTextStream stream(&temp,IO_WriteOnly);

   write(stream);
   return temp;
}


void CatalogItem::write(QTextStream& stream) const
{
   if(!comment().isEmpty())
   {
      stream << comment() << "\n";
   }

   QStringList list=msgidAsList();
   QValueList<QString>::ConstIterator lit;

   // if the msgid has more than one line
   if(list.count() > 1)
      list.prepend("");

   stream << "msgid ";
   for( lit = list.begin(); lit != list.end(); ++lit )
   {
      stream << "\"" << (*lit) << "\"\n";
   }

   if(d->_gettextPluralForm)
   {
       list = msgidAsList(true);
       // if the msgid has more than one line
       if(list.count() > 1)
          list.prepend("");

       stream << "msgid_plural ";
       for( lit = list.begin(); lit != list.end(); ++lit )
       {
          stream << "\"" << (*lit) << "\"\n";
       }
   }

   if(!d->_gettextPluralForm)
   {
       list=msgstrAsList();
       // if the msgstr has more than one line
       if(list.count() > 1)
          list.prepend("");

       stream << "msgstr ";
       for( lit = list.begin(); lit != list.end(); ++lit )
       {
          stream << "\"" << (*lit) << "\"\n";
       }
   }
   else
   {
       int forms = d->_msgstr.count();
       for(int i=0; i<forms; i++)
       {
           list=msgstrAsList(i);
           // if the msgstr has more than one line
           if(list.count() > 1)
              list.prepend("");

           stream << "msgstr[" << i << "] ";
           for( lit = list.begin(); lit != list.end(); ++lit )
           {
              stream << "\"" << (*lit) << "\"\n";
           }
       }
   }
}


CatalogItem::IOStatus CatalogItem::read(QTextStream& stream)
{
   enum {Begin,Comment,Msgid,Msgstr} part=Begin;

   clear();

   d->_valid=false;

   QString line;
   bool cancelLoop=false;
   bool error=false;
   bool recoverableError=false;
   bool obsolete=false;
   QStringList::Iterator msgstrIt=d->_msgstr.begin();
   
   while(!stream.eof() && !cancelLoop)
   {
       int pos=stream.device()->at();

       line=stream.readLine();
       if(line.isNull()) // file end
		   break;

       // remove whitespaces from beginning and end of line
       line=line.stripWhiteSpace();

       if(part==Begin)
       {
           // ignore trailing newlines
           if(line.isEmpty())
              continue;

           if(line.contains(QRegExp("^#~")))
           {
              obsolete=true;
	      part=Comment;
	      d->_comment=line;
           }
           else if(line.contains(QRegExp("^#")))
           {
               part=Comment;
               d->_comment=line;
           }
           else if(line.contains(QRegExp("^msgid\\s*\".*\"$")))
           {
               part=Msgid;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgid\\s*\""),"");
               line.replace(QRegExp("\"$"),"");

               (*(d->_msgid).begin())=line;
           }
		     // one of the quotation marks is missing
           else if(line.contains(QRegExp("^msgid\\s*\"?.*\"?$")))
           {
               part=Msgid;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgid\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               (*(d->_msgid).begin())=line;

			   if(!line.isEmpty())
			       recoverableError=true;
           }
           else
           {
              kdDebug(KBABEL) << "no comment or msgid found after a comment: " << line << endl;

               cancelLoop=true;
               error=true;
           }
       }
       else if(part==Comment)
       {
            if(line.isEmpty() && obsolete ) return Obsolete;
	    if(line.isEmpty() )
	       continue;
            else if(line.contains(QRegExp("^#~")))
            {
               d->_comment+=("\n"+line);
	       obsolete=true;
            }
            else if(line.contains(QRegExp("^#")))
            {
               d->_comment+=("\n"+line);
            }
            else if(line.contains(QRegExp("^msgid\\s*\".*\"$")))
            {
               part=Msgid;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgid\\s*\""),"");
               line.replace(QRegExp("\"$"),"");

               (*(d->_msgid).begin())=line;
            }
			// one of the quotation marks is missing
			else if(line.contains(QRegExp("^msgid\\s*\"?.*\"?$")))
            {
               part=Msgid;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgid\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               (*(d->_msgid).begin())=line;
			   
			   if(!line.isEmpty())
			       recoverableError=true;
            }
            else
            {
               kdDebug(KBABEL) << "no comment or msgid found after a comment while parsing: " << d->_comment << endl;

               error=true;
               cancelLoop=true;
            }
        }
        else if(part==Msgid)
        {
            if(line.isEmpty())
               continue;
            else if(line.contains(QRegExp("^\".*\\n?\"$")))
            {
               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^\""),"");
               line.replace(QRegExp("\"$"),"");

               QStringList::Iterator it;
               if(d->_gettextPluralForm)
                   it = d->_msgid.fromLast();
               else
                   it = d->_msgid.begin();
               
               // add Msgid line to item
               if((*it).isEmpty())
                  (*it)=line;
               else
                  (*it)+=("\n"+line);
            }
            else if(line.contains(QRegExp("^msgid_plural\\s*\".*\"$")))
            {
               part=Msgid;
               d->_gettextPluralForm = true;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgid_plural\\s*\""),"");
               line.replace(QRegExp("\"$"),"");

               d->_msgid.append(line);
            }
			// one of the quotation marks is missing
			else if(line.contains(QRegExp("^msgid_plural\\s*\"?.*\"?$")))
            {
               part=Msgid;
               d->_gettextPluralForm = true;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgid_plural\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               d->_msgid.append(line);
			   
               if(!line.isEmpty())
			       recoverableError=true;
            }
           else if(!d->_gettextPluralForm 
                   && line.contains(QRegExp("^msgstr\\s*\".*\\n?\"$")))
            {
               part=Msgstr;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgstr\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               (*msgstrIt)=line;
            }
			else if(!d->_gettextPluralForm
                    && line.contains(QRegExp("^msgstr\\s*\"?.*\\n?\"?$")))
            {
               part=Msgstr;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgstr\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               (*msgstrIt)=line;

			   if(!line.isEmpty())
			       recoverableError=true;
            }
            else if(d->_gettextPluralForm 
                    && line.contains(QRegExp("^msgstr\\[0\\]\\s*\".*\\n?\"$")))
            {
               part=Msgstr;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgstr\\[0\\]\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               (*msgstrIt)=line;
            }
			else if(d->_gettextPluralForm 
                    && line.contains(QRegExp("^msgstr\\[0\\]\\s*\"?.*\\n?\"?$")))
            {
               part=Msgstr;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgstr\\[0\\]\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               (*msgstrIt)=line;

			   if(!line.isEmpty())
			       recoverableError=true;
            }
 
            else if(line.contains(QRegExp("^#")) || line.contains(QRegExp("^msgid")))
            {
               kdDebug(KBABEL) << "no msgstr or msgid found after a msgid while parsing: " << d->_msgid.first() << endl;

               cancelLoop=true;
               error=true;
            }
            // a line of the msgid with a missing quotation mark
            else if(line.contains(QRegExp("^\"?.+\\n?\"?$")))
            {
               recoverableError=true;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^\""),"");
               line.replace(QRegExp("\"$"),"");

               QStringList::Iterator it;
               if(d->_gettextPluralForm)
                   it = d->_msgid.fromLast();
               else
                   it = d->_msgid.begin();
               
               // add Msgid line to item
               if((*it).isEmpty())
                  (*it)=line;
               else
                  (*it)+=("\n"+line);
            }
            else
            {
               kdDebug(KBABEL) << "no msgstr or msgid found after a msgid while parsing: " << d->_msgid.first() << endl;

               cancelLoop=true;
               error=true;
            }
        }
        else if(part==Msgstr)
        {
            if(line.isEmpty())
               continue;
            // another line of the msgstr
            else if(line.contains(QRegExp("^\".*\\n?\"$")))
            {
               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^\""),"");
               line.replace(QRegExp("\"$"),"");

               if((*msgstrIt).isEmpty())
                  (*msgstrIt)=line;
               else
                  (*msgstrIt)+=("\n"+line);
            }
            else if(d->_gettextPluralForm 
                    && line.contains(QRegExp("^msgstr\\[[0-9]+\\]\\s*\".*\\n?\"$")))
            {
               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgstr\\[[0-9]+\\]\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               msgstrIt=d->_msgstr.append(line);
            }
			else if(d->_gettextPluralForm 
                    && line.contains(QRegExp("^msgstr\\[[0-9]\\]\\s*\"?.*\\n?\"?$")))
            {
               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^msgstr\\[[0-9]\\]\\s*\"?"),"");
               line.replace(QRegExp("\"$"),"");

               msgstrIt=d->_msgstr.append(line);

			   if(!line.isEmpty())
			       recoverableError=true;
            }
            else if(line.contains(QRegExp("^\\s*msgid")) || line.contains(QRegExp("^\\s*#")))
            {
               cancelLoop=true;
               stream.device()->at(pos);// reset position in stream to beginning of this line
               break;
            }
            else if(line.contains(QRegExp("^msgstr")))
            {
               kdDebug(KBABEL) << "no msgid or comment found after a msgstr while parsing: " << d->_msgstr.last() << endl;

               cancelLoop=true;
               error=true;
            }
            // another line of the msgstr with a missing quotation mark
            else if(line.contains(QRegExp("^\"?.+\\n?\"?$")))
            {
               recoverableError=true;

               // remove quotes at beginning and the end of the lines
               line.replace(QRegExp("^\""),"");
               line.replace(QRegExp("\"$"),"");

               if((*msgstrIt).isEmpty())
                  (*msgstrIt)=line;
               else
                  (*msgstrIt)+=("\n"+line);
            }
            else
            {
               kdDebug(KBABEL) << "no msgid or comment found after a msgstr while parsing: " << d->_msgstr.last() << endl;

               cancelLoop=true;
               error=true;
            }
        }
    }
/*
   if(_gettextPluralForm)
   {
       kdDebug() << "gettext plural form:\n"
                 << "msgid:\n" << _msgid.first() << "\n"
                 << "msgid_plural:\n" << _msgid.last() << "\n" << endl;
       int counter=0;
       for(QStringList::Iterator it = _msgstr.begin(); it != _msgstr.end(); ++it)
       {
           kdDebug() << "msgstr[" << counter << "]:\n" 
                     << (*it) << endl;
           counter++;
       }
   }
  */ 
    if(error)
       return ParseError;
	else if(recoverableError)
		return RecoveredParseError;
    else
    {
      d->_valid=true;
      return obsolete ? Obsolete : Ok;
    }
}

// this does not like any incorrect files
CatalogItem::IOStatus CatalogItem::fastRead(FlexLexer *lexer, QTextCodec *codec, bool storeText)
{
   clear();

   d->_valid=false;

    // comment
    if( lastToken == T_COMMENT )
    {
	d->_comment = codec->toUnicode(lexer->YYText());
	while( lexer->yylex() == T_COMMENT )
	    d->_comment += "\n"+codec->toUnicode(lexer->YYText());
//	kdDebug() << "Comment: " << _comment << endl;
    }

    //obsolete
    if( lastToken == T_OBSOLETE ) {
	lexer->yylex();
	return Obsolete;
    }
        
    // msgid
    if( lastToken != T_MSGID ) return ParseError;
    
    if( lexer->yylex() != T_STRING ) return ParseError;
    QStringList::Iterator it = d->_msgid.begin();
    *it = codec->toUnicode(lexer->YYText());
    if( storeText )
	while( lexer->yylex() == T_STRING )
    	    (*it) += ("\n"+ codec->toUnicode(lexer->YYText()) );
    else {
	if( lexer->yylex() == T_STRING ) // this is not header
	{
	    *it = "SKIPPED";
	    while( lexer->yylex() == T_STRING );
	}
    }
    
//    kdDebug() << "Msgid: " << *it << endl;

    if( lastToken == T_MSGIDPLURAL ) 
    {
	d->_gettextPluralForm = true;
	if( lexer->yylex() != T_STRING ) return ParseError;
	it = d->_msgid.fromLast();
	*it = codec->toUnicode(lexer->YYText());
	if( storeText ) 
	    while( lexer->yylex() == T_STRING )
    		(*it)+="\n"+ codec->toUnicode(lexer->YYText());
	else while( lexer->yylex() == T_STRING );
//	kdDebug() << "Msgid_plural: " << *it << endl;
    }
    
    // msgstr
    if( lastToken != T_MSGSTR ) return ParseError;

    if( !d->_gettextPluralForm )
    {
	if( lexer->yylex() != T_STRING ) return ParseError;
	it = d->_msgstr.begin();
	*it = codec->toUnicode(lexer->YYText());
	if( storeText || msgid().isEmpty() ) // if we should store the text or it is a header
	    while( lexer->yylex() == T_STRING )
    		(*it)+= ("\n"+ codec->toUnicode(lexer->YYText()));
	else 
	if( lexer->yylex() == T_STRING ) // check next token, whether it is really translated
	{
	    *it = "SKIPPED";
	    while( lexer->yylex() == T_STRING );
	}
//	kdDebug() << "Msgstr: " << *it << endl;
    } 
    else 
    {
	QString s = codec->toUnicode(lexer->YYText());
	while( lastToken == T_MSGSTR && s.contains( QRegExp("^msgstr\\[[0-9]+\\]" ) ) )
	{
	    if( lexer->yylex() != T_STRING ) return ParseError;
	    it = d->_msgstr.fromLast();
	    *it = codec->toUnicode(lexer->YYText());
	    
	    if( storeText )
		do {
    		    (*it)+="\n"+codec->toUnicode(lexer->YYText());
		} while( lexer->yylex() == T_STRING );
	    else while( lexer->yylex() == T_STRING );
//	    kdDebug() << "Msgstr: " << *it << endl;
	    s = codec->toUnicode(lexer->YYText());
	} 
    }
    
     d->_valid=true;
    return Ok;
}

void CatalogItem::operator=(const CatalogItem& rhs)
{
    d->_comment = rhs.d->_comment;
    d->_msgid = rhs.d->_msgid;
    d->_msgstr = rhs.d->_msgstr;
    d->_valid = rhs.d->_valid;
    d->_error = rhs.d->_error;
    d->_gettextPluralForm = rhs.d->_gettextPluralForm;    
}
