/***************************************************************************
 *   Copyright (C) 2003 by Julian Rockey                                   *
 *   linux@jrockey.com                                                     *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/
#include "filecreate_part.h"

#include <tqwhatsthis.h>
#include <tqdom.h>
#include <tqdir.h>
#include <tqfileinfo.h>
#include <tqvbox.h>
#include <tqtimer.h>

#include <tdeversion.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <kdevgenericfactory.h>
#include <tdefiledialog.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kstdaction.h>
#include <tdeaction.h>
#include <tdeapplication.h>
#include <tdeactionclasses.h>
#include <tdepopupmenu.h>
#include <tdemessagebox.h>

#include "kdevcore.h"
#include "kdevmainwindow.h"
#include "kdevproject.h"
#include "kdevpartcontroller.h"
#include "configwidgetproxy.h"

#include "filetemplate.h"
#include "domutil.h"
#include "urlutil.h"

#include "filecreate_widget2.h"
#include "filecreate_widget3.h"
#include "filecreate_filetype.h"
#include "filecreate_filedialog.h"
#include "filecreate_newfile.h"
#include "fcconfigwidget.h"

#define PROJECTSETTINGSPAGE 1
#define GLOBALSETTINGSPAGE 2

#include "kdevplugininfo.h"

#include "config.h"

static const KDevPluginInfo pluginData("kdevfilecreate");

typedef KDevGenericFactory<FileCreatePart> FileCreateFactory;
K_EXPORT_COMPONENT_FACTORY( libkdevfilecreate, FileCreateFactory( pluginData ) )

using namespace FileCreate;

FileCreatePart::FileCreatePart(TQObject *parent, const char *name, const TQStringList & )
//    : KDevCreateFile(&pluginData, parent, name ? name : "FileCreatePart"), m_selectedWidget(-1), m_useSideTab(true), m_subPopups(0)
    : KDevCreateFile(&pluginData, parent, name ? name : "FileCreatePart"), m_subPopups(0)
{
  setInstance(FileCreateFactory::instance());
  setXMLFile("kdevpart_filecreate.rc");

  connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(slotProjectOpened()) );
  connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(slotProjectClosed()) );

	_configProxy = new ConfigWidgetProxy( core() );
	_configProxy->createProjectConfigPage( i18n("File Templates"), PROJECTSETTINGSPAGE, info()->icon() );
	_configProxy->createGlobalConfigPage( i18n("File Templates"), GLOBALSETTINGSPAGE, info()->icon() );
	connect( _configProxy, TQT_SIGNAL(insertConfigWidget(const KDialogBase*, TQWidget*, unsigned int )),
		this, TQT_SLOT(insertConfigWidget(const KDialogBase*, TQWidget*, unsigned int )) );


  TDEToolBarPopupAction * newAction = new TDEToolBarPopupAction( i18n("&New"), "document-new", CTRL+TQt::Key_N, this, TQT_SLOT(slotNewFile()), actionCollection(), "file_new");
  newAction->setWhatsThis( i18n("<b>New file</b><p>Creates a new file. Also adds it the project if the <b>Add to project</b> checkbox is turned on.") );
  newAction->setToolTip( i18n("Create a new file") );
  m_newPopupMenu = newAction->popupMenu();
  connect(m_newPopupMenu, TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotAboutToShowNewPopupMenu()));

  TQTimer::singleShot( 0, this, TQT_SLOT(slotGlobalInitialize()) );
}


FileCreatePart::~FileCreatePart()
{
  delete _configProxy;

  m_newPopupMenu->clear();
  delete m_subPopups;
}

void FileCreatePart::insertConfigWidget( const KDialogBase * dlg, TQWidget * page, unsigned int pagenumber )
{
	kdDebug() << k_funcinfo << endl;

	switch( pagenumber )
	{
		case PROJECTSETTINGSPAGE:
		{
			FCConfigWidget* w = new FCConfigWidget( this, false, page, "filecreate config widget" );
			connect( dlg, TQT_SIGNAL( okClicked( ) ), w, TQT_SLOT( accept( ) ) );
		}
		break;

		case GLOBALSETTINGSPAGE:
		{
			FCConfigWidget *w = new FCConfigWidget( this, true, page, "filecreate config widget" );
			connect(dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()));
		}
		break;
	}
}

void FileCreatePart::slotAboutToShowNewPopupMenu()
{
	TDEIconLoader * m_iconLoader = TDEGlobal::iconLoader();
	m_newPopupMenu->clear();
	delete m_subPopups;
	m_subPopups = NULL;
	int id = 0;
	FileType * filetype = m_filetypes.first();
	for(; filetype; filetype=m_filetypes.next())
	{
		if (filetype->enabled())
		{
			if (filetype->subtypes().count()==0)
			{
				TQPixmap iconPix = m_iconLoader->loadIcon(
					filetype->icon(), TDEIcon::Desktop, TDEIcon::SizeSmall,
					TDEIcon::DefaultState, NULL, true);
				m_newPopupMenu->insertItem(iconPix, filetype->name(), this,
					TQT_SLOT(slotNewFilePopup(int)), 0, ++id );
				m_newPopupMenu->setItemParameter( id, filetype->id() );
			} else
			{
				TDEPopupMenu* subMenu = NULL;
				TQPtrList<FileType> subtypes = filetype->subtypes();
				for(FileType * subtype = subtypes.first(); subtype; subtype=subtypes.next())
				{
					if (subtype->enabled()){
						if( !subMenu )
							subMenu = new TDEPopupMenu(0,0);
						TQPixmap iconPix = m_iconLoader->loadIcon(
							subtype->icon(), TDEIcon::Desktop, TDEIcon::SizeSmall,
							TDEIcon::DefaultState, NULL, true);
						subMenu->insertItem(iconPix, subtype->name(), this,
							TQT_SLOT(slotNewFilePopup(int)), 0, ++id );
						subMenu->setItemParameter( id, subtype->id() );
					}
				}
				if( subMenu )
				{
					if( !m_subPopups )
					{
						m_subPopups = new TQPtrList<TDEPopupMenu>;
						m_subPopups->setAutoDelete(true);
					}
					m_subPopups->append( subMenu );
					m_newPopupMenu->insertItem( filetype->name(), subMenu );
				}
			}

		}

	}
}

void FileCreatePart::slotNewFilePopup( int fileTypeId )
{
	const FileType* filetype = getType(fileTypeId);
	slotFiletypeSelected( filetype );
}

void FileCreatePart::slotNewFile() {
  KDevCreateFile::CreatedFile createdFile = createNewFile();
  if (createdFile.status == KDevCreateFile::CreatedFile::STATUS_NOTCREATED)
    KMessageBox::error(0, i18n("Cannot create file. Check whether the directory and filename are valid."));
  else if (createdFile.status != KDevCreateFile::CreatedFile::STATUS_CANCELED)
    openCreatedFile(createdFile);
}

void FileCreatePart::slotProjectOpened() {
    TQTimer::singleShot( 0, this, TQT_SLOT(slotInitialize()) );
}

void FileCreatePart::addFileType(const TQString & filename) {
  FileType * filetype = getType(filename);
  if (!filetype) {
    FileType* lastFiletype = m_filetypes.last();
    int lastTypeId = (lastFiletype && lastFiletype->id() < 0 ? lastFiletype->id() : 0);
    filetype = new FileType;
    filetype->setName( filename + " files" );
    filetype->setExt( filename );
    filetype->setCreateMethod("template");
    filetype->setId(--lastTypeId);
    m_filetypes.append(filetype);
  }
  filetype->setEnabled(true);
}

void FileCreatePart::slotProjectClosed() {
  m_filetypes.clear();
  TQTimer::singleShot( 0, this, TQT_SLOT(slotGlobalInitialize()) );
}

void FileCreatePart::slotFiletypeSelected(const FileType * filetype) {

  KDevCreateFile::CreatedFile createdFile = createNewFile(filetype->ext(),
                                                          TQString(),
                                                          TQString(),
                                                          filetype
                                                          ? filetype->subtypeRef()
                                                          : TQString());

  openCreatedFile(createdFile);
}

void FileCreatePart::openCreatedFile(const KDevCreateFile::CreatedFile & createdFile)
{
  if ( createdFile.status == KDevCreateFile::CreatedFile::STATUS_OK )
  {
    KURL uu( createdFile.dir + "/" + createdFile.filename );
    partController()->editDocument ( uu );
  }
}

int FileCreatePart::readTypes(const TQDomDocument & dom, TQPtrList<FileType> &m_filetypes, bool enable) {
  int numRead = 0;
  int typeId = 0;
  TQDomElement fileTypes = DomUtil::elementByPath(dom,"/kdevfilecreate/filetypes");
  if (!fileTypes.isNull()) {
    for(TQDomNode node = fileTypes.firstChild();!node.isNull();node=node.nextSibling()) {

      if (node.isElement() && node.nodeName()=="type") {
        TQDomElement element = node.toElement();
        FileType * filetype = new FileType;
        filetype->setName( element.attribute("name") );
        filetype->setExt( element.attribute("ext") );
        filetype->setCreateMethod( element.attribute("create") );

        filetype->setIcon( element.attribute("icon") );
        filetype->setDescr( (DomUtil::namedChildElement(element, "descr")).text() );
        filetype->setEnabled(enable || (filetype->ext()==""));
        filetype->setId(++typeId);
        m_filetypes.append(filetype);
        numRead++;

        kdDebug(9034) << "node: " << filetype->name().latin1() << endl;

        if (node.hasChildNodes()) {
          for(TQDomNode subnode = node.firstChild();!subnode.isNull();subnode=subnode.nextSibling()) {
            kdDebug(9034) << "subnode: " << subnode.nodeName().latin1() << endl;
            if (subnode.isElement() && subnode.nodeName()=="subtype") {
              TQDomElement subelement = subnode.toElement();
              FileType * subtype = new FileType;
              subtype->setExt( filetype->ext() );
              subtype->setCreateMethod( filetype->createMethod() );
              subtype->setSubtypeRef( subelement.attribute("ref") );
              subtype->setIcon( subelement.attribute("icon") );
              subtype->setName( subelement.attribute("name") );
              subtype->setDescr( (DomUtil::namedChildElement(subelement, "descr")).text() );
              subtype->setEnabled(enable);
              subtype->setId(++typeId);
              filetype->addSubtype(subtype);
            }
          }
        }
      }
    }
  }
  return numRead;
}

FileType * FileCreatePart::getType(const TQString & ex, const TQString subtRef) {

  TQString subtypeRef = subtRef;
  TQString ext = ex;
  int dashPos = ext.find('-');
  if (dashPos>-1 && subtRef.isNull()) {
    ext = ex.left(dashPos);
    subtypeRef = ex.mid(dashPos+1);
  }

  TQPtrList<FileType> filetypes = getFileTypes();
  for(FileType * filetype = filetypes.first();
      filetype;
      filetype=filetypes.next()) {
    if (filetype->ext()==ext) {
      if (subtypeRef.isNull()) return filetype;
      TQPtrList<FileType> subtypes = filetype->subtypes();
      for(FileType * subtype = subtypes.first();
          subtype;
          subtype=subtypes.next()) {
        if (subtypeRef==subtype->subtypeRef()) return subtype;
      }
    }
  }
  return NULL;
}

FileType * FileCreatePart::getType(int id) {

    TQPtrList<FileType> filetypes = getFileTypes();
    for(FileType* filetype = filetypes.first();
        filetype;
        filetype = filetypes.next())
    {
        if (filetype->id() == id) return filetype;
        TQPtrList<FileType> subtypes = filetype->subtypes();
        for(FileType* subtype = subtypes.first();
            subtype;
            subtype = subtypes.next())
        {
            if (subtype->id() == id) return subtype;
        }
    }
    return NULL;
}

FileType * FileCreatePart::getEnabledType(const TQString & ex, const TQString subtRef) {

  TQString subtypeRef = subtRef;
  TQString ext = ex;
  int dashPos = ext.find('-');
  if (dashPos>-1 && subtRef.isNull()) {
    ext = ex.left(dashPos);
    subtypeRef = ex.mid(dashPos+1);
  }

  TQPtrList<FileType> filetypes = getFileTypes();
  for(FileType * filetype = filetypes.first();
      filetype;
      filetype=filetypes.next()) {
    if (filetype->ext()==ext) {
      if ( (subtypeRef.isNull()) && (filetype->enabled()) ) return filetype;
      TQPtrList<FileType> subtypes = filetype->subtypes();
      for(FileType * subtype = subtypes.first();
          subtype;
          subtype=subtypes.next()) {
        if ( (subtypeRef==subtype->subtypeRef()) && (filetype->enabled()) ) return subtype;
      }
    }
  }
  return NULL;
}

// KDevFileCreate interface

KDevCreateFile::CreatedFile FileCreatePart::createNewFile(TQString ext, TQString dir, TQString name, TQString subtype)
{
  KDevCreateFile::CreatedFile result;

  KURL projectURL;
  if ( !project() )
  {
    //result.status = KDevCreateFile::CreatedFile::STATUS_NOTCREATED;
    //return result;
  }
  else
  {
    projectURL = project()->projectDirectory();
  }

  KURL selectedURL;

  NewFileChooser dialog;
  dialog.setFileTypes(m_filetypes);
  const FileType *filetype = getEnabledType(ext,subtype);
  kdDebug(9034) << "Looking for filetype pointer for " << ext << "/" << subtype << endl;
  if (filetype) {
    kdDebug(9034) << "found filetype" << endl;
  } else {
    kdDebug(9034) << "could not find filetype" << endl;
  }
  if (!project())
    dialog.setInProjectMode(false);

  if (!dir.isNull())
    dialog.setDirectory(dir);
  else if (!project())
    dialog.setDirectory(TQDir::currentDirPath());
  else
  {
    TQString activeDir = project()->activeDirectory();
    dialog.setDirectory( project()->projectDirectory() +
        ( activeDir[0] == '/' ? "" : "/" )
        + activeDir );
  }
  if (!name.isNull()) dialog.setName(name);
  if (filetype) dialog.setCurrent(filetype);

  dialog.setInitialSize(TQSize(500, 200));
  int dialogResult = dialog.exec();

  if (dialogResult == KDialogBase::Rejected) {
    result.status = KDevCreateFile::CreatedFile::STATUS_CANCELED;
    return result;
  }

  // OK was pressed

  result.addToProject = dialog.addToProject();
  selectedURL = dialog.url();
  const FileType *selectedFileType = dialog.selectedType();

  if (dialog.addToProject() && !projectURL.isParentOf(selectedURL) && !(project()->options() & KDevProject::UsesTQMakeBuildSystem) ) {
    result.status = KDevCreateFile::CreatedFile::STATUS_NOTWITHINPROJECT;
    return result;
  }

  if (selectedFileType) {
    ext = selectedFileType->ext();
    subtype = selectedFileType->subtypeRef();
  }

  TQString fullPath = selectedURL.path();
  // add appropriate extension, if not already there
  if ( !ext.isEmpty() && !fullPath.endsWith("." + ext)) fullPath+="." + ext;

  TQString filename = URLUtil::filename(fullPath);
  kdDebug(9034) << "full path = " << fullPath << endl;

  // add in subtype, if specified
  if (!subtype.isEmpty())
      ext += "-" + subtype;

  // create file from template
  bool created = false;
  if (FileTemplate::exists(this, ext))
      created = FileTemplate::copy(this, ext, fullPath);
  else {
      // no template, create a blank file instead
      TQFile f(fullPath);
      created = f.open( IO_WriteOnly );
      f.close();
  }
  if (!created)
  {
      result.status = KDevCreateFile::CreatedFile::STATUS_NOTCREATED;
      return result;
  }

  if (dialog.addToProject())
  {
    // work out the path relative to the project directory
//    TQString relToProj = URLUtil::relativePath(projectURL, selectedURL, URLUtil::SLASH_PREFIX );
	  TQString relToProj;
	  if( project()->options() & KDevProject::UsesTQMakeBuildSystem )
	  {
		relToProj = URLUtil::relativePathToFile( project()->projectDirectory(), fullPath );
		  project()->addFile(relToProj);
	  }else
	  {
        relToProj = URLUtil::relativePath(projectURL.path(), fullPath, URLUtil::SLASH_PREFIX );
        project()->addFile(relToProj.mid(1));
	  }
  }

  KURL url;
  url.setPath(fullPath);
  partController()->editDocument(url);

  TQString fileName = URLUtil::filename(fullPath);
  kdDebug(9034) << "file name = " << filename << endl;

  result.filename = fileName;
  result.dir = URLUtil::directory(fullPath);
  result.status = KDevCreateFile::CreatedFile::STATUS_OK;

  return result;
}

void FileCreatePart::slotNoteFiletype(const FileType * filetype) {
  kdDebug(9034) << "Noting file type: " << (filetype ? filetype->ext() : TQString::fromLatin1("Null") ) << endl;
  m_filedialogFiletype = filetype;
}

void FileCreatePart::slotInitialize( )
{
  m_filetypes.clear();

  //read global configuration
  slotGlobalInitialize();

  // read in which global templates are to be used for this project
  TQDomElement useGlobalTypes =
    DomUtil::elementByPath(*projectDom(),"/kdevfilecreate/useglobaltypes");
  for(TQDomNode node = useGlobalTypes.firstChild();
      !node.isNull();node=node.nextSibling()) {

    if (node.isElement() && node.nodeName()=="type") {
      TQDomElement element = node.toElement();
      TQString ext = element.attribute("ext");
      TQString subtyperef = element.attribute("subtyperef");
      // if an extension has been specified as enabled, ensure it
      // and all its subtypes are enabled
      if (subtyperef.isNull()) {
        FileType * filetype = getType(ext);
        if (filetype) {
          filetype->setEnabled(true);
          if (filetype->subtypes().count())
            filetype->setSubtypesEnabled(true);
        }
      } else {
        // if an extension + subtype have been specified, enable
        // the subtype and the extension (the 'parent')
        FileType * filetype = getType(ext);
        FileType * subtype = getType(ext,subtyperef);
        if (filetype && subtype) {
          filetype->setEnabled(true);
          subtype->setEnabled(true);
        }
      }
    }
  }

  // read in the list of file types for this project
  if ( project() && readTypes( *projectDom(), m_filetypes, true )==0  ) {
    // default by scanning the templates directory if no template info
    // found in project file
    TQDir templDir( project()->projectDirectory() + "/templates/" );
    if (templDir.exists()) {
      templDir.setFilter( TQDir::Files );
      const TQFileInfoList * list = templDir.entryInfoList();
      if( list ){
        TQFileInfoListIterator it( *list );
        TQFileInfo *fi;
        while ( (fi = it.current()) != 0 ) {
          addFileType(fi->fileName());
          ++it;
        }
      }
    }
/*    else { // it was probably an imported project
      // KLUDGE: we need a better way to determine file types
      // the current method looks a bit too restrictive
      addFileType( "cpp" );
      addFileType( "h" );
    }*/
  }
}

TQString FileCreatePart::findGlobalXMLFile() const
{
  int version = 0;
  TQString filename;
  TQStringList filenames = TDEGlobal::instance()->dirs()->findAllResources("data", "kdevfilecreate/template-info.xml");
  for( TQStringList::const_iterator it = filenames.begin(); it != filenames.end(); ++it )
  {
    TQDomDocument globalDom;
    DomUtil::openDOMFile(globalDom,*it);
    TQDomElement e = globalDom.documentElement();
    if( !e.hasAttribute( "version" ) && e.attribute( "version" ).toInt() < version )
    {
      continue;
    }else
    {
      version = e.attribute( "version" ).toInt();
      filename = *it;
    }
  }
  return filename;
}

void FileCreatePart::slotGlobalInitialize( )
{
  // read in global template information
  TQString globalXMLFile = findGlobalXMLFile();
  kdDebug(9034) << "Found global template info info " << globalXMLFile << endl;
  TQDomDocument globalDom;
  if (!globalXMLFile.isNull() && DomUtil::openDOMFile(globalDom,globalXMLFile))
  {
    kdDebug(9034) << "Reading global template info..." << endl;

    readTypes(globalDom, m_filetypes, false);

  }
}

#include "filecreate_part.moc"
