/* This file is part of the KDE libraries
   Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "katecodefoldinghelpers.h"
#include "katecodefoldinghelpers.moc"

#include "katebuffer.h"
#include "katecursor.h"
#include <kdebug.h>

#include <qstring.h>

#define JW_DEBUG 0

bool KateCodeFoldingTree::trueVal = true;

KateCodeFoldingNode::KateCodeFoldingNode() :
    parentNode(0),
    startLineRel(0),
    endLineRel(0),
    startCol(0),
    endCol(0),
    startLineValid(false),
    endLineValid(false),
    type(0),
    visible(true),
    deleteOpening(false),
    deleteEnding(false)
{
}//the endline fields should be initialised to not valid

KateCodeFoldingNode::KateCodeFoldingNode(KateCodeFoldingNode *par, signed char typ, unsigned int sLRel):
    parentNode(par),
    startLineRel(sLRel),
    endLineRel(10000),
    startCol(0),
    endCol(0),
    startLineValid(true),
    endLineValid(false),
    type(typ),
    visible(true),
    deleteOpening(false),
    deleteEnding(false)
{
}//the endline fields should be initialised to not valid

KateCodeFoldingNode::~KateCodeFoldingNode()
{
  // delete all child nodes
  clearChildren ();
}

bool KateCodeFoldingNode::getBegin(KateCodeFoldingTree *tree, KateTextCursor* begin) {
  if (!startLineValid) return false;
  unsigned int line=startLineRel;
  for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode)
    line+=n->startLineRel;

  tree->m_buffer->codeFoldingColumnUpdate(line);
  begin->setLine(line);
  begin->setCol(startCol);

  return true;
}

bool KateCodeFoldingNode::getEnd(KateCodeFoldingTree *tree, KateTextCursor *end) {
  if (!endLineValid) return false;
  unsigned int line=startLineRel+endLineRel;
  for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode)
    line+=n->startLineRel;

  tree->m_buffer->codeFoldingColumnUpdate(line);
  end->setLine(line);
  end->setCol(endCol);

  return true;
}

int KateCodeFoldingNode::cmpPos(KateCodeFoldingTree *tree, uint line,uint col) {
    KateTextCursor cur(line,col);
    KateTextCursor start,end;
    kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (1)"<<endl;
    bool startValid=getBegin(tree, &start);
    kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (2)"<<endl;
    bool endValid=getEnd(tree, &end);
    kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (3)"<<endl;
    if ((!endValid) && startValid) {
      return ((start>cur)?-1:0);
    }
    if ((!startValid) && endValid) {
      return ((cur>end)?1:0);
    }
    //here both have to be valid, both invalid must not happen
    Q_ASSERT(startValid && endValid);
    return  ( (cur<start)?(-1):( (cur>end) ? 1:0));
}

void KateCodeFoldingNode::insertChild (uint index, KateCodeFoldingNode *node)
{
  uint s = m_children.size ();

  if (index > s)
    return;

  m_children.resize (++s);

  for (uint i=s-1; i > index; --i)
    m_children[i] = m_children[i-1];

  m_children[index] = node;
}

KateCodeFoldingNode *KateCodeFoldingNode::takeChild (uint index)
{
  uint s = m_children.size ();

  if (index >= s)
    return 0;

  KateCodeFoldingNode *n = m_children[index];

  for (uint i=index; (i+1) < s; ++i)
    m_children[i] = m_children[i+1];

  m_children.resize (s-1);

  return n;
}

void KateCodeFoldingNode::clearChildren ()
{
  for (uint i=0; i < m_children.size(); ++i)
    delete m_children[i];

  m_children.resize (0);
}

KateCodeFoldingTree::KateCodeFoldingTree(KateBuffer *buffer): QObject(buffer), m_buffer (buffer)
{
  clear();
}

void KateCodeFoldingTree::fixRoot(int endLRel)
{
  m_root.endLineRel = endLRel;
}

void KateCodeFoldingTree::clear()
{
  m_root.clearChildren();

  // initialize the root "special" node
  m_root.startLineValid=true;
  m_root.endLineValid=true; // temporary, should be false;
  m_root.endLineRel=1;      // temporary;

  hiddenLinesCountCacheValid=false;
  lineMapping.setAutoDelete(true);
  hiddenLines.clear();
  lineMapping.clear();
  nodesForLine.clear();
  markedForDeleting.clear();
  dontIgnoreUnchangedLines.clear();
}

KateCodeFoldingTree::~KateCodeFoldingTree()
{
}

bool KateCodeFoldingTree::isTopLevel(unsigned int line)
{
  if (m_root.noChildren())
    return true; // no childs

  // look if a given lines belongs to a sub node
  for ( uint i=0; i < m_root.childCount(); ++i )
  {
    KateCodeFoldingNode *node = m_root.child(i);

    if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
      return false;  // the line is within the range of a subnode -> return toplevel=false
  }

  return true;  // the root node is the only node containing the given line, return toplevel=true
}

void KateCodeFoldingTree::getLineInfo(KateLineInfo *info, unsigned int line)
{
  // Initialze the returned structure, this will also be returned if the root node has no child nodes
  // or the line is not within a childnode's range.
  info->topLevel = true;
  info->startsVisibleBlock = false;
  info->startsInVisibleBlock = false;
  info->endsBlock = false;
  info->invalidBlockEnd = false;

  if (m_root.noChildren())
    return;

  //let's look for some information
  for ( uint i=0; i < m_root.childCount(); ++i )
  {
    KateCodeFoldingNode *node = m_root.child(i);

    if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel)) // we found a node, which contains the given line -> do a complete lookup
    {
      info->topLevel = false; //we are definitly not toplevel
      findAllNodesOpenedOrClosedAt(line); //lookup all nodes, which start or and at the given line

      for ( KateCodeFoldingNode *node = nodesForLine.first(); node; node = nodesForLine.next() )
      {
        uint startLine = getStartLine(node);

        // type<0 means, that a region has been closed, but not opened
        // eg. parantheses missmatch
        if (node->type < 0)
          info->invalidBlockEnd=true;
        else
        {
          if (startLine != line)  // does the region we look at not start at the given line
            info->endsBlock = true; // than it has to be an ending
          else
          {
            // The line starts a new region, now determine, if it's a visible or a hidden region
            if (node->visible)
              info->startsVisibleBlock=true;
            else
              info->startsInVisibleBlock=true;
          }
        }
      }

      return;
    }
  }

  return;
}


KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLine(unsigned int line)
{
  if (m_root.noChildren()) // does we have child list + nodes ?
    return &m_root;

  // lets look, if given line is within a subnode range, and then return the deepest one.
  for ( uint i=0; i < m_root.childCount(); ++i )
  {
    KateCodeFoldingNode *node = m_root.child(i);

    if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
    {
      // a region surounds the line, look in the next deeper hierarchy step
      return findNodeForLineDescending(node,line,0);
    }
  }

  return &m_root;
}


KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLineDescending ( KateCodeFoldingNode *node,
    unsigned int line, unsigned int offset, bool oneStepOnly )
{
  if (node->noChildren())
    return node;

  // calculate the offset, between a subnodes real start line and its relative start
  offset += node->startLineRel;

  for ( uint i=0; i < node->childCount(); ++i )
  {
    KateCodeFoldingNode *subNode = node->child(i);

    if ((subNode->startLineRel+offset<=line) && (line<=subNode->endLineRel+subNode->startLineRel+offset)) //warning fix me for invalid ends
    {
      // a subnode contains the line.
      // if oneStepOnly is true, we don't want to search for the deepest node, just return the found one

      if (oneStepOnly)
        return subNode;
      else
        return findNodeForLineDescending (subNode,line,offset); // look into the next deeper hierarchy step
    }
  }

  return node; // the current node has no sub nodes, or the line couldn'te be found within a subregion
}

KateCodeFoldingNode *KateCodeFoldingTree::findNodeForPosition(unsigned int line, unsigned int column)
{
  KateCodeFoldingNode *node=findNodeForLine(line);

  if (node==&m_root) return &m_root;

  kdDebug(13000)<<"initial cmpPos"<<endl;

  KateCodeFoldingNode *tmp;
  int leq=node->cmpPos(this, line,column);
  while (true) {
    switch (leq) {
      case 0: {
                if (node->noChildren())
                  return node;
                else
                {
                  tmp=node;
                  for ( uint i=0; i < node->childCount(); ++i )
                  {
                    KateCodeFoldingNode *subNode = node->child(i);
                    kdDebug(13000)<<"cmdPos(case0):calling"<<endl;
                    leq=subNode->cmpPos(this, line,column);
                    kdDebug(13000)<<"cmdPos(case0):returned"<<endl;
                    if (leq==0) {
                        tmp=subNode;
                        break;
                    } else if (leq==-1) break;
                  }
                  if (tmp!=node) node=tmp; else return node;
                }
                break;
              }
      //this could be optimized a littlebit
      case -1:
      case 1:  {
                  if (!(node->parentNode)) return &m_root;
                  kdDebug(13000)<<"current node type"<<node->type<<endl;
                  node=node->parentNode;
                  kdDebug(13000)<<"cmdPos(case-1/1):calling:"<<node<<endl;
                  leq=node->cmpPos(this, line,column);
                  kdDebug(13000)<<"cmdPos(case-1/1):returned"<<endl;
                  break;
                }
    }

  }
  Q_ASSERT(false);
  return &m_root;
}

void KateCodeFoldingTree::debugDump()
{
  //dump all nodes for debugging
  kdDebug(13000)<<"The parsed region/block tree for code folding"<<endl;
  dumpNode(&m_root, "");
}

void KateCodeFoldingTree::dumpNode(KateCodeFoldingNode *node, const QString &prefix)
{
  //output node properties
  kdDebug(13000)<<prefix<<QString("Type: %1, startLineValid %2, startLineRel %3, endLineValid %4, endLineRel %5, visible %6").
      arg(node->type).arg(node->startLineValid).arg(node->startLineRel).arg(node->endLineValid).
      arg(node->endLineRel).arg(node->visible)<<endl;

  //output child node properties recursive
  if (node->noChildren())
    return;

  QString newprefix(prefix + "   ");
  for ( uint i=0; i < node->childCount(); ++i )
    dumpNode (node->child(i),newprefix);
}

/*
 That's one of the most important functions ;)
*/
void KateCodeFoldingTree::updateLine(unsigned int line,
  QMemArray<uint> *regionChanges, bool *updated,bool changed,bool colsChanged)
{
  if ( (!changed) || colsChanged)
  {
    if (dontIgnoreUnchangedLines.isEmpty())
      return;

    if (dontIgnoreUnchangedLines[line])
      dontIgnoreUnchangedLines.remove(line);
    else
      return;
  }

  something_changed = false;

  findAndMarkAllNodesforRemovalOpenedOrClosedAt(line);

  if (regionChanges->isEmpty())
  {
    //  KateCodeFoldingNode *node=findNodeForLine(line);
    //  if (node->type!=0)
    //  if (getStartLine(node)+node->endLineRel==line) removeEnding(node,line);
  }
  else
  {
    for (unsigned int i=0;i<regionChanges->size() / 4;i++)
    {
        signed char tmp=(*regionChanges)[regionChanges->size()-2-i*2];
        uint tmppos=(*regionChanges)[regionChanges->size()-1-i*2];
        (*regionChanges)[regionChanges->size()-2-i*2]=(*regionChanges)[i*2];
        (*regionChanges)[regionChanges->size()-1-i*2]=(*regionChanges)[i*2+1];
        (*regionChanges)[i*2]=tmp;
        (*regionChanges)[i*2+1]=tmppos;
    }


    signed char data= (*regionChanges)[regionChanges->size()-2];
    uint charPos=(*regionChanges)[regionChanges->size()-1];
    regionChanges->resize (regionChanges->size()-2);

    int insertPos=-1;
    KateCodeFoldingNode *node = findNodeForLine(line);

    if (data<0)
    {
      //  if (insertPos==-1)
      {
        unsigned int tmpLine=line-getStartLine(node);

        for ( uint i=0; i < node->childCount(); ++i )
        {
          if (node->child(i)->startLineRel >= tmpLine)
          {
            insertPos=i;
            break;
          }
        }
      }
    }
    else
    {
      for (; (node->parentNode) && (getStartLine(node->parentNode)==line) && (node->parentNode->type!=0); node=node->parentNode);

      if ((getStartLine(node)==line) && (node->type!=0))
      {
        insertPos=node->parentNode->findChild(node);
        node = node->parentNode;
      }
      else
      {
        for ( uint i=0; i < node->childCount(); ++i )
        {
          if (getStartLine(node->child(i))>=line)
          {
            insertPos=i;
            break;
          }
        }
      }
    }

    do
    {
      if (data<0)
      {
        if (correctEndings(data,node,line,charPos,insertPos))
        {
          insertPos=node->parentNode->findChild(node)+1;
          node=node->parentNode;
        }
        else
        {
          if (insertPos!=-1) insertPos++;
        }
      }
      else
      {
        int startLine=getStartLine(node);
        if ((insertPos==-1) || (insertPos>=(int)node->childCount()))
        {
          KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
          something_changed = true;
          node->appendChild(newNode);
          addOpening(newNode, data, regionChanges, line,charPos);
          insertPos = node->findChild(newNode)+1;
        }
        else
        {
          if (node->child(insertPos)->startLineRel == line-startLine)
          {
            addOpening(node->child(insertPos), data, regionChanges, line,charPos);
            insertPos++;
          }
          else
          {
//              kdDebug(13000)<<"ADDING NODE "<<endl;
            KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
            something_changed = true;
            node->insertChild(insertPos, newNode);
            addOpening(newNode, data, regionChanges, line,charPos);
            insertPos++;
          }
        }
      }

      if (regionChanges->isEmpty())
        data = 0;
      else
      {
        data = (*regionChanges)[regionChanges->size()-2];
        charPos=(*regionChanges)[regionChanges->size()-1];
        regionChanges->resize (regionChanges->size()-2);
      }
    } while (data!=0);
  }

  cleanupUnneededNodes(line);
//  if (something_changed) emit regionBeginEndAddedRemoved(line);
  (*updated) = something_changed;
}


bool KateCodeFoldingTree::removeOpening(KateCodeFoldingNode *node,unsigned int line)
{
  signed char type;
  if ((type=node->type) == 0)
  {
    dontDeleteOpening(node);
    dontDeleteEnding(node);
    return false;
  }

  if (!node->visible)
  {
  toggleRegionVisibility(getStartLine(node));
  }

  KateCodeFoldingNode *parent = node->parentNode;
  int mypos = parent->findChild(node);

  if (mypos > -1)
  {
  //move childnodes() up
  for(; node->childCount()>0 ;)
  {
    KateCodeFoldingNode *tmp;
    parent->insertChild(mypos, tmp=node->takeChild(0));
    tmp->parentNode = parent;
    tmp->startLineRel += node->startLineRel;
    mypos++;
  }

  // remove the node
  //mypos = parent->findChild(node);
  bool endLineValid = node->endLineValid;
  int endLineRel = node->endLineRel;
  uint endCol=node->endCol;

  // removes + deletes
  KateCodeFoldingNode *child = parent->takeChild(mypos);
  markedForDeleting.removeRef(child);
  delete child;

  if ((type>0) && (endLineValid))
    correctEndings(-type, parent, line+endLineRel/*+1*/,endCol, mypos); // why the hell did I add a +1 here ?
  }

  return true;
}

bool KateCodeFoldingTree::removeEnding(KateCodeFoldingNode *node,unsigned int /* line */)
{
  KateCodeFoldingNode *parent = node->parentNode;

  if (!parent)
    return false;

  if (node->type == 0)
    return false;

  if (node->type < 0)
  {
    // removes + deletes
    int i = parent->findChild (node);
    if (i >= 0)
    {
      KateCodeFoldingNode *child = parent->takeChild(i);
      markedForDeleting.removeRef(child);
      delete child;
    }

    return true;
  }

  int mypos = parent->findChild(node);
  int count = parent->childCount();

  for (int i=mypos+1; i<count; i++)
  {
    if (parent->child(i)->type == -node->type)
    {
      node->endLineValid = true;
      node->endLineRel = parent->child(i)->startLineRel - node->startLineRel;

      KateCodeFoldingNode *child = parent->takeChild(i);
      markedForDeleting.removeRef(child);
      delete child;

      count = i-mypos-1;
      if (count > 0)
      {
        for (int i=0; i<count; i++)
        {
          KateCodeFoldingNode *tmp = parent->takeChild(mypos+1);
          tmp->startLineRel -= node->startLineRel;
          tmp->parentNode = node; //should help 16.04.2002
          node->appendChild(tmp);
        }
      }
      return false;
    }
  }

  if ( (parent->type == node->type) || /*temporary fix */ (!parent->parentNode))
  {
    for (int i=mypos+1; i<(int)parent->childCount(); i++)
    {
      KateCodeFoldingNode *tmp = parent->takeChild(mypos+1);
      tmp->startLineRel -= node->startLineRel;
      tmp->parentNode = node; // SHOULD HELP 16.04.2002
      node->appendChild(tmp);
    }

    // this should fix the bug of wrongly closed nodes
    if (!parent->parentNode)
      node->endLineValid=false;
    else
      node->endLineValid = parent->endLineValid;

    node->endLineRel = parent->endLineRel-node->startLineRel;

    if (node->endLineValid)
      return removeEnding(parent, getStartLine(parent)+parent->endLineRel);

    return false;
  }

  node->endLineValid = false;
  node->endLineRel = parent->endLineRel - node->startLineRel;

  return false;
}


bool KateCodeFoldingTree::correctEndings(signed char data, KateCodeFoldingNode *node,unsigned int line,unsigned int endCol,int insertPos)
{
//  if (node->type==0) {kdError()<<"correct Ending should never be called with the root node"<<endl; return true;}
  uint startLine = getStartLine(node);
  if (data != -node->type)
  {
#if JW_DEBUG
    kdDebug(13000)<<"data!=-node->type (correctEndings)"<<endl;
#endif
    //invalid close -> add to unopend list
    dontDeleteEnding(node);
    if (data == node->type) {
      node->endCol=endCol;
      return false;
    }
    KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
    something_changed = true;
    newNode->startLineValid = false;
    newNode->endLineValid = true;
    newNode->endLineRel = 0;
    newNode->endCol=endCol;

    if ((insertPos==-1) || (insertPos==(int)node->childCount()))
      node->appendChild(newNode);
    else
      node->insertChild(insertPos,newNode);

      // find correct position
    return false;
  }
  else
  {
    something_changed = true;
    dontDeleteEnding(node);

    // valid closing region
    if (!node->endLineValid)
    {
      node->endLineValid = true;
      node->endLineRel = line - startLine;
      node->endCol=endCol;
      //moving

      moveSubNodesUp(node);
    }
    else
    {
#if JW_DEBUG
      kdDebug(13000)<<"Closing a node which had already a valid end"<<endl;
#endif
      // block has already an ending
      if (startLine+node->endLineRel == line)
      {
         node->endCol=endCol;
         // we won, just skip
#if JW_DEBUG
        kdDebug(13000)<< "We won, just skipping (correctEndings)"<<endl;
#endif
      }
      else
      {
        int bakEndLine = node->endLineRel+startLine;
        uint bakEndCol = node->endCol;
        node->endLineRel = line-startLine;
        node->endCol=endCol;

#if JW_DEBUG
        kdDebug(13000)<< "reclosed node had childnodes()"<<endl;
        kdDebug(13000)<<"It could be, that childnodes() need to be moved up"<<endl;
#endif
  moveSubNodesUp(node);

        if (node->parentNode)
        {
          correctEndings(data,node->parentNode,bakEndLine, bakEndCol,node->parentNode->findChild(node)+1); // ????
        }
        else
        {
          //add to unopened list (bakEndLine)
        }
      }
      }
    }
    return true;
}

void KateCodeFoldingTree::moveSubNodesUp(KateCodeFoldingNode *node)
{
        int mypos = node->parentNode->findChild(node);
        int removepos=-1;
        int count = node->childCount();
        for (int i=0; i<count; i++)
          if (node->child(i)->startLineRel >= node->endLineRel)
          {
            removepos=i;
            break;
          }
#if JW_DEBUG
        kdDebug(13000)<<QString("remove pos: %1").arg(removepos)<<endl;
#endif
        if (removepos>-1)
        {
#if JW_DEBUG
          kdDebug(13000)<<"Children need to be moved"<<endl;
#endif
          KateCodeFoldingNode *moveNode;
          if (mypos == (int)node->parentNode->childCount()-1)
          {
            while (removepos<(int)node->childCount())
            {
              node->parentNode->appendChild(moveNode=node->takeChild(removepos));
              moveNode->parentNode = node->parentNode;
              moveNode->startLineRel += node->startLineRel;
            }
          }
          else
          {
            int insertPos=mypos;
            while (removepos < (int)node->childCount())
            {
              insertPos++;
              node->parentNode->insertChild(insertPos, moveNode=node->takeChild(removepos));
              moveNode->parentNode = node->parentNode; // That should solve a crash
              moveNode->startLineRel += node->startLineRel;
            }
          }
        }

}



void KateCodeFoldingTree::addOpening(KateCodeFoldingNode *node,signed char nType, QMemArray<uint>* list,unsigned int line,unsigned int charPos)
{
  uint startLine = getStartLine(node);
  if ((startLine==line) && (node->type!=0))
  {
#if JW_DEBUG
    kdDebug(13000)<<"startLine equals line"<<endl;
#endif
    if (nType == node->type)
    {
#if JW_DEBUG
      kdDebug(13000)<<"Node exists"<<endl;
#endif
      node->deleteOpening = false;
      node->startCol=charPos;
      KateCodeFoldingNode *parent = node->parentNode;

      if (!node->endLineValid)
      {
        int current = parent->findChild(node);
        int count = parent->childCount()-(current+1);
        node->endLineRel = parent->endLineRel - node->startLineRel;

// EXPERIMENTAL TEST BEGIN
// move this afte the test for unopened, but closed regions within the parent node, or if there are no siblings, bubble up
        if (parent)
          if (parent->type == node->type)
          {
            if (parent->endLineValid)
            {
              removeEnding(parent, line);
              node->endLineValid = true;
            }
          }

// EXPERIMENTAL TEST BEGIN

        if (current != (int)parent->childCount()-1)
        {
        //search for an unopened but closed region, even if the parent is of the same type
#ifdef __GNUC__
#warning  "FIXME:  why does this seem to work?"
#endif
//          if (node->type != parent->type)
          {
            for (int i=current+1; i<(int)parent->childCount(); i++)
            {
              if (parent->child(i)->type == -node->type)
              {
                count = (i-current-1);
                node->endLineValid = true;
                node->endLineRel = getStartLine(parent->child(i))-line;
                node->endCol = parent->child(i)->endCol;
                KateCodeFoldingNode *child = parent->takeChild(i);
                markedForDeleting.removeRef( child );
                delete child;
                break;
              }
            }
          }
//          else
//          {
//            parent->endLineValid = false;
//            parent->endLineRel = 20000;
//          }

          if (count>0)
          {
            for (int i=0;i<count;i++)
            {
              KateCodeFoldingNode *tmp;
              node->appendChild(tmp=parent->takeChild(current+1));
              tmp->startLineRel -= node->startLineRel;
              tmp->parentNode = node;
            }
          }
        }

      }

      addOpening_further_iterations(node, nType, list, line, 0, startLine,node->startCol);

    } //else ohoh, much work to do same line, but other region type
  }
  else
  { // create a new region
    KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,nType,line-startLine);
    something_changed = true;

    int insert_position=-1;
    for (int i=0; i<(int)node->childCount(); i++)
    {
      if (startLine+node->child(i)->startLineRel > line)
      {
         insert_position=i;
         break;
      }
    }

    int current;
    if (insert_position==-1)
    {
      node->appendChild(newNode);
      current = node->childCount()-1;
    }
    else
    {
      node->insertChild(insert_position, newNode);
      current = insert_position;
    }

//    if (node->type==newNode->type)
//    {
//      newNode->endLineValid=true;
//      node->endLineValid=false;
//      newNode->endLineRel=node->endLineRel-newNode->startLineRel;
//      node->endLineRel=20000; //FIXME

      int count = node->childCount() - (current+1);
      newNode->endLineRel -= newNode->startLineRel;
      if (current != (int)node->childCount()-1)
      {
        if (node->type != newNode->type)
        {
          for (int i=current+1; i<(int)node->childCount(); i++)
          {
            if (node->child(i)->type == -newNode->type)
            {
              count = node->childCount() - i - 1;
              newNode->endLineValid = true;
              newNode->endLineRel = line - getStartLine(node->child(i));
              KateCodeFoldingNode *child = node->takeChild(i);
              markedForDeleting.removeRef( child );
              delete child;
              break;
            }
          }
        }
        else
        {
          node->endLineValid = false;
          node->endLineRel = 10000;
        }
        if (count > 0)
        {
          for (int i=0;i<count;i++)
          {
            KateCodeFoldingNode *tmp;
            newNode->appendChild(tmp=node->takeChild(current+1));
            tmp->parentNode=newNode;
          }
        }
//      }
    }

    addOpening(newNode, nType, list, line,charPos);

    addOpening_further_iterations(node, node->type, list, line, current, startLine,node->startCol);
  }
}


void KateCodeFoldingTree::addOpening_further_iterations(KateCodeFoldingNode *node,signed char /* nType */, QMemArray<uint>*
    list,unsigned int line,int current, unsigned int startLine,unsigned int charPos)
{
  while (!(list->isEmpty()))
  {
    if (list->isEmpty())
      return;
    else
    {
         signed char data = (*list)[list->size()-2];
         uint charPos=(*list)[list->size()-1];
       list->resize (list->size()-2);

      if (data<0)
      {
#if JW_DEBUG
        kdDebug(13000)<<"An ending was found"<<endl;
#endif

        if (correctEndings(data,node,line,charPos,-1))
          return; // -1 ?

#if 0
        if(data == -nType)
        {
          if (node->endLineValid)
          {
            if (node->endLineRel+startLine==line) // We've won again
            {
              //handle next node;
            }
            else
            { // much moving
              node->endLineRel=line-startLine;
              node->endLineValid=true;
            }
            return;  // next higher level should do the rest
          }
          else
          {
            node->endLineRel=line-startLine;
            node->endLineValid=true;
            //much moving
          }
        } //else add to unopened list
#endif
      }
      else
      {
        bool needNew = true;
        if (current < (int)node->childCount())
        {
          if (getStartLine(node->child(current)) == line)
            needNew=false;
        }
        if (needNew)
        {
          something_changed = true;
          KateCodeFoldingNode *newNode = new KateCodeFoldingNode(node, data, line-startLine);
          node->insertChild(current, newNode);  //find the correct position later
        }

               addOpening(node->child(current), data, list, line,charPos);
        current++;
        //lookup node or create subnode
      }
    }
  } // end while
}

unsigned int KateCodeFoldingTree::getStartLine(KateCodeFoldingNode *node)
{
  unsigned int lineStart=0;
  for (KateCodeFoldingNode *iter=node; iter->type != 0; iter=iter->parentNode)
    lineStart += iter->startLineRel;

  return lineStart;
}


void KateCodeFoldingTree::lineHasBeenRemoved(unsigned int line)
{
  lineMapping.clear();
  dontIgnoreUnchangedLines.insert(line, &trueVal);
  dontIgnoreUnchangedLines.insert(line-1, &trueVal);
  dontIgnoreUnchangedLines.insert(line+1, &trueVal);
  hiddenLinesCountCacheValid = false;
#if JW_DEBUG
  kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenRemoved: %1").arg(line)<<endl;
#endif

//line ++;
  findAndMarkAllNodesforRemovalOpenedOrClosedAt(line); //It's an ugly solution
  cleanupUnneededNodes(line);  //It's an ugly solution

  KateCodeFoldingNode *node = findNodeForLine(line);
//?????  if (node->endLineValid)
  {
    int startLine = getStartLine(node);
    if (startLine == (int)line)
      node->startLineRel--;
    else
    {
      if (node->endLineRel == 0)
        node->endLineValid = false;
      node->endLineRel--;
    }

    int count = node->childCount();
    for (int i=0; i<count; i++)
    {
      if (node->child(i)->startLineRel+startLine >= line)
        node->child(i)->startLineRel--;
    }
  }

  if (node->parentNode)
    decrementBy1(node->parentNode, node);

  for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
  {
    if ((*it).start > line)
      (*it).start--;
    else if ((*it).start+(*it).length > line)
      (*it).length--;
  }
}


void KateCodeFoldingTree::decrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after)
{
  if (node->endLineRel == 0)
    node->endLineValid = false;
  node->endLineRel--;

  for (uint i=node->findChild(after)+1; i < node->childCount(); ++i)
    node->child(i)->startLineRel--;

  if (node->parentNode)
    decrementBy1(node->parentNode,node);
}


void KateCodeFoldingTree::lineHasBeenInserted(unsigned int line)
{
  lineMapping.clear();
  dontIgnoreUnchangedLines.insert(line, &trueVal);
  dontIgnoreUnchangedLines.insert(line-1, &trueVal);
  dontIgnoreUnchangedLines.insert(line+1, &trueVal);
  hiddenLinesCountCacheValid = false;
//return;
#if JW_DEBUG
  kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenInserted: %1").arg(line)<<endl;
#endif

//  findAndMarkAllNodesforRemovalOpenedOrClosedAt(line);
//  cleanupUnneededNodes(line);

  KateCodeFoldingNode *node = findNodeForLine(line);
// ????????  if (node->endLineValid)
  {
    int startLine=getStartLine(node);
    if (node->type < 0)
      node->startLineRel++;
    else
      node->endLineRel++;

    for (uint i=0; i < node->childCount(); ++i)
    {
      KateCodeFoldingNode *iter = node->child(i);

      if (iter->startLineRel+startLine >= line)
        iter->startLineRel++;
    }
  }

  if (node->parentNode)
    incrementBy1(node->parentNode, node);

  for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
  {
    if ((*it).start > line)
      (*it).start++;
    else if ((*it).start+(*it).length > line)
      (*it).length++;
  }
}

void KateCodeFoldingTree::incrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after)
{
  node->endLineRel++;

  for (uint i=node->findChild(after)+1; i < node->childCount(); ++i)
    node->child(i)->startLineRel++;

  if (node->parentNode)
    incrementBy1(node->parentNode,node);
}


void KateCodeFoldingTree::findAndMarkAllNodesforRemovalOpenedOrClosedAt(unsigned int line)
{
#ifdef __GNUC__
#warning "FIXME:  make this multiple region changes per line save";
#endif
//  return;
  markedForDeleting.clear();
  KateCodeFoldingNode *node = findNodeForLine(line);
  if (node->type == 0)
    return;

  addNodeToRemoveList(node, line);

  while (((node->parentNode) && (node->parentNode->type!=0)) && (getStartLine(node->parentNode)==line))
  {
    node = node->parentNode;
    addNodeToRemoveList(node, line);
  }
#if JW_DEBUG
  kdDebug(13000)<<" added line to markedForDeleting list"<<endl;
#endif
}


void KateCodeFoldingTree::addNodeToRemoveList(KateCodeFoldingNode *node,unsigned int line)
{
  bool add=false;
#ifdef __GNUC__
#warning "FIXME:  make this multiple region changes per line save";
#endif
  unsigned int startLine=getStartLine(node);
  if ((startLine==line) && (node->startLineValid))
  {
    add=true;
    node->deleteOpening = true;
  }
  if ((startLine+node->endLineRel==line) || ((node->endLineValid==false) && (node->deleteOpening)))
  {
    int myPos=node->parentNode->findChild(node); // this has to be implemented nicely
    if ((int)node->parentNode->childCount()>myPos+1)
     addNodeToRemoveList(node->parentNode->child(myPos+1),line);
    add=true;
    node->deleteEnding = true;
  }

  if(add)
  markedForDeleting.append(node);

}


void KateCodeFoldingTree::findAllNodesOpenedOrClosedAt(unsigned int line)
{
  nodesForLine.clear();
  KateCodeFoldingNode *node = findNodeForLine(line);
  if (node->type == 0)
    return;

  unsigned int startLine = getStartLine(node);
  if (startLine == line)
    nodesForLine.append(node);
  else if ((startLine+node->endLineRel == line))
    nodesForLine.append(node);

  while (node->parentNode)
  {
    addNodeToFoundList(node->parentNode, line, node->parentNode->findChild(node));
    node = node->parentNode;
  }
#if JW_DEBUG
  kdDebug(13000)<<" added line to nodesForLine list"<<endl;
#endif
}


void KateCodeFoldingTree::addNodeToFoundList(KateCodeFoldingNode *node,unsigned int line,int childpos)
{
  unsigned int startLine = getStartLine(node);

  if ((startLine==line) && (node->type!=0))
    nodesForLine.append(node);
  else if ((startLine+node->endLineRel==line) && (node->type!=0))
    nodesForLine.append(node);

  for (int i=childpos+1; i<(int)node->childCount(); i++)
  {
    KateCodeFoldingNode *child = node->child(i);

    if (startLine+child->startLineRel == line)
    {
      nodesForLine.append(child);
      addNodeToFoundList(child, line, 0);
    }
    else
      break;
  }
}


void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)
{
#if JW_DEBUG
  kdDebug(13000)<<"void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)"<<endl;
#endif

//  return;
  if (markedForDeleting.isEmpty())
    return;

  for (int i=0; i<(int)markedForDeleting.count(); i++)
  {
    KateCodeFoldingNode *node = markedForDeleting.at(i);
    if (node->deleteOpening)
      kdDebug(13000)<<"DELETE OPENING SET"<<endl;
    if (node->deleteEnding)
      kdDebug(13000)<<"DELETE ENDING SET"<<endl;

    if ((node->deleteOpening) && (node->deleteEnding))
    {
#if JW_DEBUG
      kdDebug(13000)<<"Deleting complete node"<<endl;
#endif
      if (node->endLineValid)    // just delete it, it has been opened and closed on this line
      {
        int f = node->parentNode->findChild (node);

        if (f >= 0)
          delete node->parentNode->takeChild(f);
      }
      else
      {
        removeOpening(node, line);
        // the node has subnodes which need to be moved up and this one has to be deleted
      }
      something_changed = true;
    }
    else
    {
      if ((node->deleteOpening) && (node->startLineValid))
      {
#if JW_DEBUG
        kdDebug(13000)<<"calling removeOpening"<<endl;
#endif
        removeOpening(node, line);
        something_changed = true;
      }
      else
      {
        dontDeleteOpening(node);

        if ((node->deleteEnding) && (node->endLineValid))
        {
          dontDeleteEnding(node);
          removeEnding(node, line);
          something_changed = true;
        }
        else
          dontDeleteEnding(node);
      }
    }
  }
}

void KateCodeFoldingTree::dontDeleteEnding(KateCodeFoldingNode* node)
{
  node->deleteEnding = false;
}


void KateCodeFoldingTree::dontDeleteOpening(KateCodeFoldingNode* node)
{
  node->deleteOpening = false;
}


void KateCodeFoldingTree::toggleRegionVisibility(unsigned int line)
{
  // hl whole file
  m_buffer->line (m_buffer->count()-1);

  lineMapping.clear();
  hiddenLinesCountCacheValid = false;
  kdDebug(13000)<<QString("KateCodeFoldingTree::toggleRegionVisibility() %1").arg(line)<<endl;

  findAllNodesOpenedOrClosedAt(line);
  for (int i=0; i<(int)nodesForLine.count(); i++)
  {
    KateCodeFoldingNode *node=nodesForLine.at(i);
    if ( (!node->startLineValid) || (getStartLine(node) != line) )
    {
      nodesForLine.remove(i);
      i--;
    }
  }

  if (nodesForLine.isEmpty())
    return;

  nodesForLine.at(0)->visible = !nodesForLine.at(0)->visible;

  if (!nodesForLine.at(0)->visible)
    addHiddenLineBlock(nodesForLine.at(0),line);
  else
  {
    for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end();++it)
      if ((*it).start == line+1)
      {
        hiddenLines.remove(it);
        break;
      }

    updateHiddenSubNodes(nodesForLine.at(0));
  }

  emit regionVisibilityChangedAt(line);
}

void KateCodeFoldingTree::updateHiddenSubNodes(KateCodeFoldingNode *node)
{
  for (uint i=0; i < node->childCount(); ++i)
  {
    KateCodeFoldingNode *iter = node->child(i);

    if (!iter->visible)
      addHiddenLineBlock(iter, getStartLine(iter));
    else
      updateHiddenSubNodes(iter);
  }
}

void KateCodeFoldingTree::addHiddenLineBlock(KateCodeFoldingNode *node,unsigned int line)
{
  KateHiddenLineBlock data;
  data.start = line+1;
  data.length = node->endLineRel-(existsOpeningAtLineAfter(line+node->endLineRel,node)?1:0); // without -1;
  bool inserted = false;

  for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
  {
    if (((*it).start>=data.start) && ((*it).start<=data.start+data.length-1)) // another hidden block starting at the within this block already exits -> adapt new block
    {
      // the existing block can't have lines behind the new one, because a newly hidden
      //  block has to encapsulate already hidden ones
      it=hiddenLines.remove(it);
      --it;
    }
    else
    {
      if ((*it).start > line)
      {
        hiddenLines.insert(it, data);
        inserted = true;

        break;
      }
    }
  }

  if (!inserted)
    hiddenLines.append(data);
}

bool KateCodeFoldingTree::existsOpeningAtLineAfter(unsigned int line, KateCodeFoldingNode *node)
{
  for(KateCodeFoldingNode *tmp = node->parentNode; tmp; tmp=tmp->parentNode)
  {
    KateCodeFoldingNode *tmp2;
    unsigned int startLine=getStartLine(tmp);

    if ((tmp2 = tmp->child(tmp->findChild(node) + 1))
         && ((tmp2->startLineRel + startLine) == line))
      return true;

    if ((startLine + tmp->endLineRel) > line)
      return false;
  }

  return false;
}


//
// get the real line number for a virtual line
//
unsigned int KateCodeFoldingTree::getRealLine(unsigned int virtualLine)
{
  // he, if nothing is hidden, why look at it ;)
  if (hiddenLines.isEmpty())
    return virtualLine;

  // kdDebug(13000)<<QString("VirtualLine %1").arg(virtualLine)<<endl;

  unsigned int *real=lineMapping[virtualLine];
  if (real)
    return (*real);

  unsigned int tmp = virtualLine;
  for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it)
  {
    if ((*it).start<=virtualLine)
      virtualLine += (*it).length;
    else
      break;
  }

  // kdDebug(13000)<<QString("Real Line %1").arg(virtualLine)<<endl;

  lineMapping.insert(tmp, new unsigned int(virtualLine));
  return virtualLine;
}

//
// get the virtual line number for a real line
//
unsigned int KateCodeFoldingTree::getVirtualLine(unsigned int realLine)
{
  // he, if nothing is hidden, why look at it ;)
  if (hiddenLines.isEmpty())
    return realLine;

  // kdDebug(13000)<<QString("RealLine--> %1").arg(realLine)<<endl;

  for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.fromLast(); it!=hiddenLines.end(); --it)
  {
    if ((*it).start <= realLine)
      realLine -= (*it).length;
    // else
      // break;
  }

  // kdDebug(13000)<<QString("-->virtual Line %1").arg(realLine)<<endl;

  return realLine;
}

//
// get the number of hidden lines
//
unsigned int KateCodeFoldingTree::getHiddenLinesCount(unsigned int doclen)
{
  // he, if nothing is hidden, why look at it ;)
  if (hiddenLines.isEmpty())
    return 0;

  if (hiddenLinesCountCacheValid)
    return hiddenLinesCountCache;

  hiddenLinesCountCacheValid = true;
  hiddenLinesCountCache = 0;

  for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
  {
    if ((*it).start+(*it).length<=doclen)
      hiddenLinesCountCache += (*it).length;
    else
    {
      hiddenLinesCountCache += ((*it).length- ((*it).length + (*it).start - doclen));
      break;
    }
  }

  return hiddenLinesCountCache;
}

void KateCodeFoldingTree::collapseToplevelNodes()
{
  // hl whole file
  m_buffer->line (m_buffer->count()-1);

  if (m_root.noChildren ())
    return;

  for ( uint i=0; i < m_root.childCount(); ++i )
  {
    KateCodeFoldingNode *node = m_root.child(i);

    if (node->visible && node->startLineValid && node->endLineValid)
    {
        node->visible=false;
        lineMapping.clear();
        hiddenLinesCountCacheValid = false;
        addHiddenLineBlock(node,node->startLineRel);
        emit regionVisibilityChangedAt(node->startLineRel);
    }
  }
}

void KateCodeFoldingTree::expandToplevelNodes(int numLines)
{
  // hl whole file
  m_buffer->line (m_buffer->count()-1);

  KateLineInfo line;
  for (int i = 0; i < numLines; i++) {
    getLineInfo(&line, i);

    if (line.startsInVisibleBlock)
      toggleRegionVisibility(i);
  }
}

int KateCodeFoldingTree::collapseOne(int realLine)
{
  // hl whole file
  m_buffer->line (m_buffer->count()-1);

  KateLineInfo line;
  int unrelatedBlocks = 0;
  for (int i = realLine; i >= 0; i--) {
    getLineInfo(&line, i);

    if (line.topLevel && !line.endsBlock)
      // optimisation
      break;

    if (line.endsBlock  && ( line.invalidBlockEnd ) && (i != realLine)) {
      unrelatedBlocks++;
    }

    if (line.startsVisibleBlock) {
      unrelatedBlocks--;
      if (unrelatedBlocks == -1) {
        toggleRegionVisibility(i);
        return i;
      }
    }
  }
  return -1;
}

void KateCodeFoldingTree::expandOne(int realLine, int numLines)
{
  // hl whole file
  m_buffer->line (m_buffer->count()-1);

  KateLineInfo line;
  int blockTrack = 0;
  for (int i = realLine; i >= 0; i--) {
    getLineInfo(&line, i);

    if (line.topLevel)
      // done
      break;

    if (line.startsInVisibleBlock && i != realLine) {
      if (blockTrack == 0)
        toggleRegionVisibility(i);

      blockTrack--;
    }

    if (line.endsBlock)
      blockTrack++;

    if (blockTrack < 0)
      // too shallow
      break;
  }

  blockTrack = 0;
  for (int i = realLine; i < numLines; i++) {
    getLineInfo(&line, i);

    if (line.topLevel)
      // done
      break;

    if (line.startsInVisibleBlock) {
      if (blockTrack == 0)
        toggleRegionVisibility(i);

      blockTrack++;
    }

    if (line.endsBlock)
      blockTrack--;

    if (blockTrack < 0)
      // too shallow
      break;
  }
}

void KateCodeFoldingTree::ensureVisible( uint line )
{
  // first have a look, if the line is really hidden
  bool found=false;
  for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it)
  {
    if ( ((*it).start<=line)  && ((*it).start+(*it).length>line) )
    {
      found=true;
      break;
    }
  }


  if (!found) return;

  kdDebug(13000)<<"line "<<line<<" is really hidden ->show block"<<endl;

  // it looks like we really have to ensure visibility
  KateCodeFoldingNode *n = findNodeForLine( line );
  do {
    if ( ! n->visible )
      toggleRegionVisibility( getStartLine( n ) );
    n = n->parentNode;
  } while( n );

}

// kate: space-indent on; indent-width 2; replace-tabs on;
