// KreateCD - CD recording software for the K desktop environment
//
// 1999 by Alexander Feigl <Alexander.Feigl@gmx.de>
//
// This file is subject to the terms and conditions of the GNU General
// Public License.  See the file COPYING in the main directory of the
// KreateCD distribution for more details.

#include "FileTree.h"
#include "IsoFile.h"

#include <qobject.h>
#include <ktreelist.h>
#include <kfileinfo.h>
#include <kdir.h>
#include <kapp.h>
#include <kiconloader.h>


FileTreeItem::FileTreeItem(ISOFile *isofile,FileTree *parent):KTreeListItem()
 {
  QPixmap pmap;
  fileObject=0;
  dirObject=0;
  dummyItem=0;
  inList=false;

  fileList=new FileTreeItems();
  isoFile=isofile;
  isDirExpanded=false;
  isDir=false;
  parentTree=parent;
  parentItem=0;

  if (isoFile->type()==ISOFile::ISO_RealDir)
   {
    fileObject=new KFileInfo(isoFile->reference()->data(),"");
    setupDirObject();
   }

  switch (isoFile->type())
   {
    case ISOFile::ISO_ISODir:
      pmap=Icon("mini/folder_red.xpm");
      isDir=true;
      break;
    case ISOFile::ISO_RealDir:
      pmap=Icon("mini/folder_blue.xpm");
      isDir=true;
      break;
    case ISOFile::ISO_RealFile:
      pmap=Icon("mini/unknown_blue.xpm");
      break;
   }

  setText(isoFile->name()->data());
  setPixmap(&pmap);

  if (isDir)
   {
    dummyItem=new FileTreeItem(parent);
    appendChild(dummyItem);
    dummyItem->parentItem=this;
   }

 }

FileTreeItem::FileTreeItem(FileTree *parent):KTreeListItem()
 {
  QPixmap pmap;
  fileObject=0;
  dirObject=0;
  dummyItem=0;
  inList=false;

  fileList=new FileTreeItems();
  isoFile=0;
  isDirExpanded=false;
  isDir=false;
  parentTree=parent;
  parentItem=0;

  pmap=Icon("mini/folder.xpm");
  setText(".");
  setPixmap(&pmap);
 }



FileTreeItem::FileTreeItem(KFileInfo *fileinfo,FileTree *parent):KTreeListItem()
 {
  QPixmap pmap;

  fileObject=new KFileInfo(*fileinfo);
  fileList=new FileTreeItems();
  isoFile=0;
  dummyItem=0;
  parentTree=parent;
  parentItem=0;
  isDirExpanded=false;
  isDir=false;
  inList=false;

  setupDirObject();

  if (fileObject->isDir())
   {
    if (fileObject->isReadable())
     {
      isDir=true;
      pmap=Icon("mini/folder.xpm");
     }
     else
     {
      pmap=Icon("mini/lockedfolder.xpm");
     }
   }
   else
   {
    if (fileObject->isReadable())
     {
      pmap=Icon("mini/unknown.xpm");
     }
     else
     {
      pmap=Icon("mini/locked.xpm");
     }
   }

  if (strcmp("",fileObject->fileName())==0)
   {
    this->setText(fileObject->absURL());
   }
   else
   {
    this->setText(fileObject->fileName());
   }
  this->setPixmap(&pmap);

  if (isDir)
   {
    dummyItem=new FileTreeItem(parent);
    appendChild(dummyItem);
    dummyItem->parentItem=this;
   }
 }

FileTreeItem::~FileTreeItem(void)
 {
  if (fileObject!=0) delete(fileObject);
  if (dirObject!=0) delete (dirObject);
  delete(fileList);
 }

void FileTreeItem::setupDirObject(void)
 {
  if ( (fileObject->isDir()) && (fileObject->isReadable()) )
   {
    QString filename;
    getFilename(&filename,fileObject);
    dirObject=new KDir(filename);
    connect(dirObject,SIGNAL(newFilesArrived(const KFileInfoList *)),
            this,SLOT(newFiles(const KFileInfoList *)));
    connect(dirObject,SIGNAL(finished()),
            this,SLOT(addChildItems()));
   }
   else
   {
    dirObject=0;
   }
 }


void FileTreeItem::expandTree(void)
 {
  if (!isDir) return;
  if (isDirExpanded==true) return;
  if (isoFile!=0)
   {
    if (isoFile->type()==ISOFile::ISO_ISODir)
     {
      ISOFile *walker=0;
      while ( (walker=isoFile->getChildren(walker))!=0)
       {
        addISOObject(walker,false);
       }
      addChildItems();
      isDirExpanded=true;
      return;
     }
   }
  if (dirObject==0) return;
  newFiles(dirObject->entryInfoList());
  if (dirObject->isFinished()) addChildItems();
  isDirExpanded=true;
 }

void FileTreeItem::addChildItems(void)
 {
  int additems=0,addidx;
  FileTreeItem *child;

  addidx=parentTree->itemIndex(this);
  parentTree->setAutoUpdate(false);
  child=fileList->first();
  while (child)
   {
    if (child->inList==false)
     {
      parentTree->addChildItem(child,addidx);
      child->inList=true;
      ++additems;
     }
   child=fileList->next();
   }
  if (dummyItem)
   {
    parentTree->removeItem(parentTree->itemIndex(dummyItem));
    dummyItem=0;
    additems++;
   }
  parentTree->setAutoUpdate(true);
  if (additems) parentTree->repaint();
 }

void FileTreeItem::collapseTree(void)
 {
  FileTreeItem *item;

  if (!isDir) return;
  if (!fileList->isEmpty())
   {
    collapseSubTree();
   }

  while ( (item=fileList->first())!=0)
   {
    fileList->remove(item);
    KTreeListItem::removeChild(item);
    delete item;
   }
  if (!dummyItem)
   {
    dummyItem=new FileTreeItem(parentTree);
    appendChild(dummyItem);
    dummyItem->parentItem=this;
   }
  isDirExpanded=false;
 }

void FileTreeItem::expandSubTree(void)
 {
  FileTreeItem *item;
  item=fileList->first();
  while (item!=0)
   {
    item->expandTree();
    if (item->isExpanded()) item->expandSubTree();
    item=fileList->next();
   }
 }

void FileTreeItem::collapseSubTree(void)
 {
  FileTreeItem *item;
  item=fileList->first();
  while (item!=0)
   {
    item->collapseTree();
    item=fileList->next();
   }
 }



void FileTreeItem::newFiles(const KFileInfoList *infos)
 {
  KFileInfoList *infolist;
  KFileInfo *info;
  FileTreeItem *item;

  infolist=new KFileInfoList(*infos);
  info=infolist->first();
  while (info!=0)
   {
    if ( (strcmp("..",info->fileName())==0) ||
         (strcmp(".",info->fileName())==0) )
     {
      info=infolist->next();
      continue;
     }
    item=new FileTreeItem(info,parentTree);
    addItemSorted(item,false);
    item->parentItem=this;
    info=infolist->next();
   }
 }


void FileTreeItem::getFilename(QString *fname,KFileInfo *finfo)
 {
  *fname=finfo->absURL();
  if (strcmp(finfo->absURL().data(),"//")==0)
   {
    *fname="/";
    *fname+=finfo->fileName();
   }
   else
   {
    *fname=finfo->absURL()+finfo->fileName();
   }
 }

bool FileTreeItem::fileName(QString *filename)
 {
  if (fileObject==0) return (false);
  getFilename(filename,fileObject);
  return(true);
 }

bool FileTreeItem::addISOObject(ISOFile *isoobj,bool addit)
 {
  FileTreeItem *item;
  if (isoFile==0) return(false);
  if ( (addit) && (isoFile->findChildren(isoobj->name())!=0)) return(false);
  if (isoFile->type()!=ISOFile::ISO_ISODir) return(false);
  item=new FileTreeItem(isoobj,parentTree);
  item->parentItem=this;
  if (isExpanded()) addItemSorted(item);
  if (addit) isoFile->addObject(isoobj);
  if (item->isExpanded()) item->expandTree();
  return(true);
 }

void FileTreeItem::addItemSorted(FileTreeItem *item,bool immed)
 {
  FileTreeItem *child,*ochild;
  const char *text1;
  int idx=0;
  bool preflag=false;

  child=fileList->first();
  if (child==0)
   {
    fileList->append(item);
    if (immed)
     {
      parentTree->addChildItem(item,parentTree->itemIndex(this));
      item->inList=true;
     }
    return;
   }
  text1=item->getText();
  while (child!=0)
   {
    ochild=child;
    if (strcmp(text1,child->getText())<0)
     {
      preflag=true;
      break;
     }
    child=fileList->next();
    ++idx;
   }
  fileList->insert(idx,item);

  if (immed)
   {
    KPath path1,path2;
    while (ochild!=0)
     {
      if (ochild->getText()==0) break;
      path1.push(new QString(ochild->getText()));
      ochild=(FileTreeItem *) ochild->getParent();
     }
    while (!path1.isEmpty())
     {
      path2.push(path1.pop());
     }
    parentTree->insertItem(item,&path2,preflag);
    item->inList=true;
   }
 }


bool FileTreeItem::deleteObject(void)
 {
  if (isoFile!=0)
   {
    if (isoFile->type()==ISOFile::ISO_ISODir)
     {
      while (!this->fileList->isEmpty())
       {
        FileTreeItem *temp;
        temp=this->fileList->first();
        if (!temp->deleteObject()) return(false);
        //FIXME: how do I delete // delete(temp);
       }
     }
   }
  if (parentItem!=0)
   {
    int ii;
    if (fileObject!=0) delete(fileObject);
    fileObject=0;
    if (dirObject!=0) delete(dirObject);
    dirObject=0;
    if (isoFile!=0) delete(isoFile);
    isoFile=0;
    parentItem->fileList->removeRef(this);
    ii=parentTree->itemIndex(this);
    if (ii!=-1)
     {
      parentTree->removeItem(ii);
     }
     else
     {
      parentItem->KTreeListItem::removeChild(this);
     }
    }
   else
   {
    return(false);
   }
  return(true);
 }


// main tree widget

FileTree::FileTree(QWidget *parent,const char *name,WFlags flags)
         :KTreeList(parent,name,flags)
 {
  currentItem=0;
  setExpandLevel(1);
  connect(this,SIGNAL(expanded(int)),this,SLOT(expandedTree(int)));
  connect(this,SIGNAL(collapsed(int)),this,SLOT(collapsedTree(int)));
  connect(this,SIGNAL(highlighted(int)),this,SLOT(changeSelect(int)));
 }

FileTree::~FileTree(void)
 {
 }

void FileTree::addDir(KFileInfo *finfo)
 {
  FileTreeItem *item;

  item=new FileTreeItem(finfo,this);
  this->insertItem(item,-1);
  if (item->isExpanded()) item->expandTree();
 }

void FileTree::addDir(ISOFile *isofile)
 {
  FileTreeItem *item;
  item=new FileTreeItem(isofile,this);
  this->insertItem(item,-1);
  if (item->isExpanded()) item->expandTree();
  setCurrentItem(itemIndex(item));
 }

void FileTree::expandedTree(int index)
 {
  FileTreeItem *item;
  item=itemAt(index);
  item->expandTree();
 }

void FileTree::collapsedTree(int index)
 {
  FileTreeItem *item;
  item=itemAt(index);
  item->collapseTree();
 }

void FileTree::changeSelect(int index)
 {
  currentItem=itemAt(index);
 }

bool FileTree::getSelected(QString *fname)
 {
  if (currentItem==0) return(false);

  return(currentItem->fileName(fname));
 }

bool FileTree::deleteObject(void)
 {
  FileTreeItem *ci;

  if (currentItem==0) return(false);
  ci=currentItem;
  currentItem=0;
  setCurrentItem(-1);
  if (!ci->deleteObject()) return(false);
  //FIXME: how do I delete // delete(ci);
  return(true);
 }

bool FileTree::addRealObject(QString *fname)
 {
  QString path;
  QString file;
  QString complete;

  KFileInfo *kfi;
  ISOFile::ISOType itype;
  int index;
  ISOFile *ifil;

  path=fname->data();
  file=fname->data();
  if (currentItem==0) return(false);

  index=fname->findRev('/');
  if (index==-1) return(false);
  path.remove(index+1,file.length()-(index+1));
  file.remove(0,index+1);
  if (strcmp(path.right(2).data(),"//")==0)
   {
    path.truncate(path.length()-1);
   }
  if (strcmp(path.right(1).data(),"/")!=0)
   {
    path+="/";
   }
  kfi=new KFileInfo(path,file);
  if (kfi==0) return(false);

  if (!kfi->isReadable())
   {
    delete(kfi);
    return(false);
   }

  if (kfi->isDir())
   {
    itype=ISOFile::ISO_RealDir;
   }
   else
   {
    itype=ISOFile::ISO_RealFile;
   }


  complete=path.data();
  complete+=file.data();

  ifil=new ISOFile(itype,&file,&complete);

  if (!currentItem->addISOObject(ifil))
   {
    delete(ifil);
    delete(kfi);
    return(false);
   }

  delete(kfi);
  return(true);
 }

bool FileTree::createISODir(const char *dirname)
 {
  ISOFile *isofil;
  QString qs;

  if (currentItem==0) return(false);
  qs=dirname;

  isofil=new ISOFile(ISOFile::ISO_ISODir,&qs);
  if (!currentItem->addISOObject(isofil))
   {
    delete(isofil);
    return(false);
   }
  return(true);
 }

FileTreeItem *FileTree::itemAt(int index)
 {
  return((FileTreeItem *) KTreeList::itemAt(index));
 }


