#include "rubysupport_part.h"
#include "rubyconfigwidget.h"
#include "domutil.h"

#include "tqtdesignerrubyintegration.h"
#include "rubyimplementationwidget.h"

#include "kdevcore.h"
#include "kdevmainwindow.h"
#include "kdevlanguagesupport.h"
#include "kdevpartcontroller.h"
#include "kdevproject.h"
#include "kdevappfrontend.h"
#include "kdevplugininfo.h"
#include "kdevshellwidget.h"
#include "kdevquickopen.h"

#include <tqdir.h>
#include <tqwhatsthis.h>
#include <tqtimer.h>
#include <tqfileinfo.h>
#include <tqpopupmenu.h>
#include <tqregexp.h>

#include <kiconloader.h>
#include <tdelocale.h>
#include <kdevgenericfactory.h>
#include <kprocess.h>
#include <kdebug.h>
#include <tdeaction.h>
#include <tdeparts/part.h>
#include <kdialogbase.h>
#include <tdeapplication.h>
#include <klibloader.h>

#include <tdetexteditor/viewcursorinterface.h>

#include <codemodel_utils.h>

typedef KDevGenericFactory<RubySupportPart> RubySupportFactory;
static const KDevPluginInfo data("kdevrubysupport");
K_EXPORT_COMPONENT_FACTORY( libkdevrubysupport, RubySupportFactory( data ) )

RubySupportPart::RubySupportPart(TQObject *parent, const char *name, const TQStringList& )
  : KDevLanguageSupport (&data, parent, name ? name : "RubySupportPart" )
{
  setInstance(RubySupportFactory::instance());
  setXMLFile("kdevrubysupport.rc");

  TDEAction *action;
  action = new TDEAction( i18n("&Run"), "application-x-executable", SHIFT + Key_F9,
                        this, TQT_SLOT(slotRun()),
                        actionCollection(), "build_execute" );
  action->setToolTip(i18n("Run"));
  action->setWhatsThis(i18n("<b>Run</b><p>Starts an application."));
  action->setIcon("ruby_run.png");

  action = new TDEAction( i18n("Run Test Under Cursor"), "application-x-executable", ALT + Key_F9,
                        this, TQT_SLOT(slotRunTestUnderCursor()),
                        actionCollection(), "build_execute_test_function" );
  action->setToolTip(i18n("Run Test Under Cursor"));
  action->setWhatsThis(i18n("<b>Run Test Under Cursor</b><p>Runs the function under the cursor as test."));

  action = new TDEAction( i18n("Launch Browser"), "network", 0, this, TQT_SLOT(slotBrowse()), actionCollection(), "build_launch_browser" );
  action->setToolTip(i18n("Launch Browser"));
  action->setWhatsThis(i18n("<b>Launch Browser</b><p>Opens a web browser pointing to the Ruby Rails server") );

  action = new TDEAction( i18n("Switch To Controller"), 0, CTRL+ALT+Key_1, this, TQT_SLOT(slotSwitchToController()), actionCollection(), "switch_to_controller" );
  action = new TDEAction( i18n("Switch To Model"), 0, CTRL+ALT+Key_2, this, TQT_SLOT(slotSwitchToModel()), actionCollection(), "switch_to_model" );
  action = new TDEAction( i18n("Switch To View"), 0, CTRL+ALT+Key_3, this, TQT_SLOT(slotSwitchToView()), actionCollection(), "switch_to_view" );
  action = new TDEAction( i18n("Switch To Test"), 0, CTRL+ALT+Key_4, this, TQT_SLOT(slotSwitchToTest()), actionCollection(), "switch_to_test" );

  kdDebug() << "Creating RubySupportPart" << endl;

  m_shellWidget = new KDevShellWidget( 0, "irb console");
  m_shellWidget->setIcon( SmallIcon("ruby_config.png", TDEIcon::SizeMedium, TDEIcon::DefaultState, RubySupportPart::instance()));
  m_shellWidget->setCaption(i18n("Ruby Shell"));
  mainWindow()->embedOutputView( m_shellWidget, i18n("Ruby Shell"), i18n("Ruby Shell"));
  mainWindow()->raiseView( m_shellWidget );

  connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()) );
  connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) );
  connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
        this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) );
  connect( partController(), TQT_SIGNAL(savedFile(const KURL&)),
  	this, TQT_SLOT(savedFile(const KURL&)) );
  connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)),
        this, TQT_SLOT(projectConfigWidget(KDialogBase*)) );
}


RubySupportPart::~RubySupportPart()
{
    if ( m_shellWidget )
        mainWindow()->removeView( m_shellWidget );
    delete m_shellWidget;
}


void RubySupportPart::projectConfigWidget(KDialogBase *dlg)
{
    TQVBox *vbox = dlg->addVBoxPage(i18n("Ruby"), i18n("Ruby"), BarIcon("ruby_config.png", TDEIcon::SizeMedium, TDEIcon::DefaultState, RubySupportPart::instance()));
    RubyConfigWidget *w = new RubyConfigWidget(*projectDom(), (TQWidget *)vbox, "ruby config widget");
    connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) );
}

void RubySupportPart::projectOpened()
{
  kdDebug() << "projectOpened()" << endl;

  TQStrList l;
  l.append( shell().latin1() ) ;
  m_shellWidget->setShell( shell().latin1(), l );
  m_shellWidget->activate();
  m_shellWidget->setAutoReactivateOnClose( true );

  connect( project(), TQT_SIGNAL(addedFilesToProject(const TQStringList &)),
  	this, TQT_SLOT(addedFilesToProject(const TQStringList &)) );
  connect( project(), TQT_SIGNAL(removedFilesFromProject(const TQStringList &)),
  	this, TQT_SLOT(removedFilesFromProject(const TQStringList &)) );

  TQFileInfo program(mainProgram());

  // If it's a Rails project, create the project files if they're missing
  if (mainProgram().endsWith("script/server")) {
    TQString cmd;
    TQFileInfo server(project()->projectDirectory() + "/script/server");
    if (! server.exists()) {
      cmd += "rails " + project()->projectDirectory();
      if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("TDevelop/AppFrontend"))
        appFrontend->startAppCommand(project()->projectDirectory(), cmd, false);
    }
  }

  // We want to parse only after all components have been
  // properly initialized
  TQTimer::singleShot(0, this, TQT_SLOT(initialParse()));
}

void RubySupportPart::maybeParse(const TQString fileName)
{
  TQFileInfo fi(fileName);

  if (fi.extension() == "rb") {
    if (codeModel()->hasFile(fileName)) {
      emit aboutToRemoveSourceInfo(fileName);
      codeModel()->removeFile(codeModel()->fileByName(fileName));
    }

    parse(fileName);
    emit addedSourceInfo( fileName );
  }
}

void RubySupportPart::initialParse()
{
  kdDebug() << "initialParse()" << endl;

  if (project()) {
    kapp->setOverrideCursor(waitCursor);
    TQStringList files = project()->allFiles();
    for (TQStringList::Iterator it = files.begin(); it != files.end() ;++it) {
      kdDebug() << "maybe parse " << project()->projectDirectory() + "/" + (*it) << endl;
      maybeParse(project()->projectDirectory() + "/" + *it);
    }

    emit updatedSourceInfo();
    kapp->restoreOverrideCursor();
  } else {
    kdDebug() << "No project" << endl;
  }
}

void RubySupportPart::addedFilesToProject(const TQStringList &fileList)
{
  kdDebug() << "addedFilesToProject()" << endl;

  TQStringList::ConstIterator it;

  for ( it = fileList.begin(); it != fileList.end(); ++it )
  {
    TQString fileName = project()->projectDirectory() + "/" + ( *it );
    maybeParse( fileName );
  }
}

void RubySupportPart::removedFilesFromProject(const TQStringList &fileList)
{
  kdDebug() << "removedFilesFromProject()" << endl;

  TQStringList::ConstIterator it;

  for ( it = fileList.begin(); it != fileList.end(); ++it )
  {
    TQString fileName = project()->projectDirectory() + "/" + ( *it );

    if( codeModel()->hasFile(fileName) ){
      emit aboutToRemoveSourceInfo( fileName );
      codeModel()->removeFile( codeModel()->fileByName(fileName) );
    }
  }
}

void RubySupportPart::savedFile(const KURL &fileName)
{
  kdDebug() << "savedFile()" << endl;

  if (project()->allFiles().contains(fileName.path().mid ( project()->projectDirectory().length() + 1 ))) {
    maybeParse(fileName.path());
    emit addedSourceInfo( fileName.path() );
  }
}

KDevLanguageSupport::Features RubySupportPart::features()
{
  return Features(Classes | Functions | Variables | Declarations | Signals | Slots);
}

void RubySupportPart::parse(const TQString &fileName)
{
  TQFile f(TQFile::encodeName(fileName));
  if (!f.open(IO_ReadOnly))
    return;
  TQTextStream stream(&f);

	TQRegExp classre("^\\s*(class|module)\\s+([A-Z][A-Za-z0-9_]+::)*([A-Z][A-Za-z0-9_]+)\\s*(<\\s*([A-Z][A-Za-z0-9_:]+))?$");
  TQRegExp methodre("^(\\s*)def\\s+(([A-Z][A-Za-z0-9_:]+|self)\\.)?([A-Za-z0-9_]+[!?=]?|\\[\\]=?|\\*\\*||\\-|[!~+*/%&|><^]|>>|<<||<=>|<=|>=|==|===|!=|=~|!~).*$");
  TQRegExp accessre("^\\s*(private|protected|public)\\s*((:([A-Za-z0-9_]+[!?=]?|\\[\\]=?|\\*\\*||\\-|[!~+*/%&|><^]|>>|<<||<=>|<=|>=|==|===|!=|=~|!~),?\\s*)*)$");
  TQRegExp attr_accessorre("^\\s*(attr_accessor|attr_reader|attr_writer)\\s*((:([A-Za-z0-9_]+),?\\s*)*)$");
  TQRegExp symbolre(":([^,]+),?");
  TQRegExp line_contre(",\\s*$");
  TQRegExp slot_signalre("^\\s*(slots|signals|k_dcop|k_dcop_signals)\\s*(('[^)]+\\)',?\\s*)*)$");
  TQRegExp memberre("'([A-Za-z0-9_ &*]+\\s)?([A-Za-z0-9_]+)\\([^)]*\\)',?");
  TQRegExp begin_commentre("^*=begin");
  TQRegExp end_commentre("^*=end");
  TQRegExp variablere("(@@?[A-Za-z0-9_]+)\\s*=\\s*((?:([A-Za-z0-9_:.]+)\\.new)|[\\[\"'%:/\\?\\{]|%r|<<|true|false|^\\?|0[0-7]+|[-+]?0b[01]+|[-+]?0x[1-9a-fA-F]+|[-+]?[0-9_\\.e]+|nil)?");
  TQRegExp endre("^(\\s*)end\\s*$");

  FileDom m_file = codeModel()->create<FileModel>();
  m_file->setName(fileName);

  ClassDom lastClass;
  FunctionDom lastMethod;
  TQString lastMethodIndentation;
  int lastAccess = CodeModelItem::Public;
  TQString rawline;
  TQCString line;
  int lineNo = 0;

  while (!stream.atEnd()) {
    rawline = stream.readLine();
    line = rawline.stripWhiteSpace().local8Bit();
    if (classre.search(line) != -1) {
      if (m_file->hasClass(classre.cap(3))) {
        lastClass = m_file->classByName( classre.cap(3) )[ 0 ];
	  } else {
        lastClass = codeModel()->create<ClassModel>();
        lastClass->setName(classre.cap(3));
        lastClass->setFileName( fileName );
        lastClass->setStartPosition( lineNo, 0 );
        m_file->addClass( lastClass );
	  }

      TQString parent = classre.cap(5);
      if (!parent.isEmpty())
      {
        kdDebug() << "Add parent " << parent << endl;
        lastClass->addBaseClass( parent );
      }

	  lastAccess = CodeModelItem::Public;
    } else if (methodre.search(line) != -1) {
      FunctionDom methodDecl;
      if ( lastClass != 0 && lastClass->hasFunction( methodre.cap(4) ) ) {
        FunctionList methods = lastClass->functionByName( methodre.cap(4) );
	    methodDecl = methods[0];
	  } else {
        methodDecl = codeModel()->create<FunctionModel>();
        methodDecl->setFileName( fileName );
        methodDecl->setStartPosition( lineNo, 0 );
        methodDecl->setName(methodre.cap(4));
	  }
      FunctionDefinitionDom method = codeModel()->create<FunctionDefinitionModel>();
      method->setName(methodre.cap(4));
      kdDebug() << "Add method: " << method->name() << endl;
      method->setFileName( fileName );
      method->setStartPosition( lineNo, 0 );
	  if (methodDecl->name() == "initialize") {
	    // Ruby constructors are alway private
	    methodDecl->setAccess( CodeModelItem::Private );
	  } else {
	    methodDecl->setAccess( lastAccess );
	  }
	  if (methodre.cap(2) != "") {
	    // A ruby class/singleton method of the form <classname>.<methodname>
	  	methodDecl->setStatic( true );
	  }

    lastMethodIndentation = methodre.cap(1);

	  lastMethod = method;

      if (lastClass != 0) {
		TQStringList scope( lastClass->name() );
		method->setScope( scope );
		methodDecl->setScope( scope );
        if( !lastClass->hasFunction(methodDecl->name()) ) {
          lastClass->addFunction( methodDecl );
		}
        if( !lastClass->hasFunctionDefinition(method->name()) ) {
          lastClass->addFunctionDefinition( method );
		}
      } else if( !m_file->hasFunctionDefinition(method->name()) ){
        m_file->addFunction( methodDecl );
        m_file->addFunctionDefinition( method );
        lastClass = 0;
      }
    } else if (endre.search(line) != -1 && lastMethod != 0 && endre.cap(1) == lastMethodIndentation ) {
        int endCol, endLine;
        lastMethod->getEndPosition(&endCol, &endLine);
        if (endLine == 0) {
            //hack to set end position of the previous method to the line
            //where its corresponding "end" is found
            //there's an assumption that method's "def" statement will have the same
            //indentation level as method's "end"
            lastMethod->setEndPosition(lineNo, 0);
        }
    }
    else if (accessre.search(line) != -1 && lastClass != 0) {
	  int currentAccess = lastAccess;
	  if (accessre.cap(1) == "public") {
	    currentAccess = CodeModelItem::Public;
	  } else if (accessre.cap(1) == "protected") {
	    currentAccess = CodeModelItem::Protected;
	  } else if (accessre.cap(1) == "private") {
	    currentAccess = CodeModelItem::Private;
	  }

	  if (accessre.cap(2) == "") {
	  	lastAccess = currentAccess;
	  } else {
		TQString symbolList( accessre.cap(2) );
        int pos = 0;

        while ( pos >= 0 ) {
          pos = symbolre.search( symbolList, pos );
		  if (pos == -1) {
			if (line_contre.search(line) != -1) {
              rawline = stream.readLine();
			  if (!stream.atEnd()) {
                line = rawline.stripWhiteSpace().local8Bit();
                ++lineNo;
			    symbolList = line;
			    pos = 0;
			  }
			}
		  } else {
            if ( lastClass->hasFunction( symbolre.cap(1) ) ) {
              FunctionList methods = lastClass->functionByName( symbolre.cap(1) );
			  methods[0]->setAccess( currentAccess );
			}
            pos += symbolre.matchedLength();
          }
        }
	  }
    } else if (slot_signalre.search(line) != -1 && lastClass != 0) {
      TQString memberList( slot_signalre.cap(2) );
      int pos = 0;

      while ( pos >= 0 ) {
        pos = memberre.search( memberList, pos );
		if (pos == -1) {
	      if (line_contre.search(line) != -1) {
            rawline = stream.readLine();
			if (!stream.atEnd()) {
              line = rawline.stripWhiteSpace().local8Bit();
              ++lineNo;
			  memberList = line;
			  pos = 0;
			}
		  }
		} else {
          FunctionDom method;
          if ( lastClass->hasFunction( memberre.cap(2) ) ) {
            FunctionList methods = lastClass->functionByName( memberre.cap(2) );
	        method = methods[0];
	      } else {
            method = codeModel()->create<FunctionModel>();
		  }
		  TQStringList scope( lastClass->name() );
		  method->setScope( scope );
          method->setName(memberre.cap(2));
          method->setFileName( fileName );
          method->setStartPosition( lineNo, 0 );

		  if (slot_signalre.cap(1) == "slots" || slot_signalre.cap(1) == "k_dcop") {
		    method->setSlot( true );
		  } else {
		    method->setSignal( true );
		  }
          if ( !lastClass->hasFunction(method->name()) ) {
            lastClass->addFunction( method );
		  }
          pos += memberre.matchedLength();
        }
	  }
	} else if (attr_accessorre.search(line) != -1 && lastClass != 0) {
	  TQString attr( attr_accessorre.cap(1) );
	  TQString symbolList( attr_accessorre.cap(2) );
      int pos = 0;

      while ( pos >= 0 ) {
        pos = symbolre.search( symbolList, pos );
		if (pos == -1) {
		  if (line_contre.search(line) != -1) {
            rawline = stream.readLine();
			if (!stream.atEnd()) {
              line = rawline.stripWhiteSpace().local8Bit();
              ++lineNo;
			  symbolList = line;
			  pos = 0;
			}
		  }
		} else {
 		    TQStringList scope( lastClass->name() );
			if (	!lastClass->hasFunction(symbolre.cap(1))
					&& (attr == "attr_accessor" || attr == "attr_reader") )
			{
              FunctionDefinitionDom method = codeModel()->create<FunctionDefinitionModel>();
              method->setName(symbolre.cap(1));
              kdDebug() << "Add method: " << method->name() << endl;
              method->setFileName( fileName );
              method->setStartPosition( lineNo, 0 );
			  method->setScope(scope);
              lastClass->addFunction( model_cast<FunctionDom>(method) );
              lastClass->addFunctionDefinition( method );
			}

            if (	!lastClass->hasFunction(symbolre.cap(1) + "=")
					&& (attr == "attr_accessor" || attr == "attr_writer") )
			{
              FunctionDefinitionDom method = codeModel()->create<FunctionDefinitionModel>();
              method->setName(symbolre.cap(1) + "=");
              kdDebug() << "Add method: " << method->name() << endl;
              method->setFileName( fileName );
              method->setStartPosition( lineNo, 0 );
			  method->setScope(scope);
              lastClass->addFunction( model_cast<FunctionDom>(method) );
              lastClass->addFunctionDefinition( method );
			}

            pos  += symbolre.matchedLength();
        }
	  }
   } else if (variablere.search(line) != -1 && lastClass != 0) {
     VariableDom attr;
     if ( lastClass->hasVariable( variablere.cap(1) ) ) {
	   attr = lastClass->variableByName( variablere.cap(1) );
	 } else {
       attr = codeModel()->create<VariableModel>();
       attr->setName( variablere.cap(1) );
       attr->setFileName( fileName );
       attr->setStartPosition( lineNo, 0 );
 	   attr->setAccess( CodeModelItem::Private );
	   if (TQRegExp("^@@").search(attr->name()) != -1) {
	     attr->setStatic( true );
	   }
       lastClass->addVariable( attr );
	 }

	 // Give priority to any variable initialized in the constructor
	 // Otherwise, take the first one found in the source file
	 if (lastMethod != 0 && lastMethod->name() == "initialize") {
       attr->setFileName( fileName );
       attr->setStartPosition( lineNo, 0 );
	 }

	 if (TQRegExp("^(/|%r)").search(variablere.cap(2)) != -1) {
       attr->setType( "Regexp" );
	 } else if (TQRegExp("^[\"'%<]").search(variablere.cap(2)) != -1) {
       attr->setType( "String" );
	 } else if (TQRegExp("^\\[").search(variablere.cap(2)) != -1) {
       attr->setType( "Array" );
	 } else if (TQRegExp("^\\{").search(variablere.cap(2)) != -1) {
       attr->setType( "Hash" );
	 } else if (TQRegExp("^:").search(variablere.cap(2)) != -1) {
       attr->setType( "Symbol" );
	 } else if (TQRegExp("\\.\\.").search(variablere.cap(2)) != -1) {
       attr->setType( "Range" );
	 } else if (variablere.cap(2) == "true" || variablere.cap(2) == "false") {
       attr->setType( "Boolean" );
	 } else if (  TQRegExp("^[-+]?[0-9_]+").exactMatch(variablere.cap(2))
	              || TQRegExp("^[-+]?(0x|0|0b|\\?)").search(variablere.cap(2)) != -1 )
	 {
       attr->setType( "Integer" );
	 } else if (TQRegExp("[0-9._]+(e[-+0-9]+)?").exactMatch(variablere.cap(2))) {
       attr->setType( "Float" );
	 } else if (variablere.cap(2) != "nil" && variablere.cap(3) != "") {
       attr->setType( variablere.cap(3) );
	 }
   } else if (begin_commentre.search(line) != -1) {
     while (!stream.atEnd() && end_commentre.search(line) == -1) {
       rawline = stream.readLine();
       line = rawline.stripWhiteSpace().local8Bit();
       ++lineNo;
	 }
   }

    ++lineNo;
  }

  f.close();

  codeModel()->addFile( m_file );
}


void RubySupportPart::slotRun ()
{
    // if we can't save all parts, then the user canceled
    if ( partController()->saveAllFiles() == false )
        return;
    TQFileInfo program(mainProgram());
    if (mainProgram().endsWith("script/server")) {
         TQString cmd;
         TQFileInfo server(project()->projectDirectory() + "/script/server");

        // Starting WEBrick for a Rails app. Translate a SIGTERM signal sent by KDevelop
        // to a SIGINT expected by WEBrick (ie control&c) to terminate it.
        cmd += "script/server& \n trap \"kill -s SIGINT $!\" TERM \n wait \n exit 0";
        if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("TDevelop/AppFrontend"))
            appFrontend->startAppCommand(project()->projectDirectory(), cmd, false);
    } else {
        TQString cmd = TQString("%1 -K%2 -C\"%3\" -I\"%4\" \"%5\" %6")
                          .arg(interpreter())
                          .arg(characterCoding())
                          .arg(runDirectory())
                          .arg(program.dirPath())
                          .arg(program.fileName())
                          .arg(programArgs());
        startApplication(cmd);
    }
}

TQString RubySupportPart::interpreter() {
    TQString prog = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/interpreter");
    if (prog.isEmpty()) prog = "ruby";
    return prog;
}

TQString RubySupportPart::shell() {
    TQString shell = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/shell");
    if (shell.isEmpty()) shell = "irb";
    return shell;
}

TQString RubySupportPart::mainProgram() {
	TQString prog;
	int runMainProgram = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/runmainprogram");

	if (runMainProgram == 0) {
    	prog = project()->projectDirectory() + "/" + DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/mainprogram");
	} else {
		KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
		if (ro_part != 0) {
			prog = ro_part->url().path();
		}
	}

    return prog;
}

TQString RubySupportPart::runDirectory() {
    TQString cwd = DomUtil::readEntry(*projectDom(), "/kdevscriptproject/run/globalcwd");
    if (cwd.isEmpty())
    {
      TQString mainProg = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/mainprogram");
      KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
      if (mainProg.isEmpty() && ro_part)
        cwd = ro_part->url().directory();
      else
        cwd = project()->buildDirectory();
    }
    return cwd;
}

TQString RubySupportPart::programArgs() {
    TQString args = DomUtil::readEntry(*projectDom(), "/kdevrubysupport/run/programargs");
    return args;
}

TQString RubySupportPart::characterCoding() {
    int coding = DomUtil::readIntEntry(*projectDom(), "/kdevrubysupport/run/charactercoding");
	TQString code("A");

	switch (coding) {
	case 0:
		code = "A";
		break;
	case 1:
		code = "E";
		break;
	case 2:
		code = "S";
		break;
	case 3:
		code = "U";
		break;
	}
    return code;
}


void RubySupportPart::startApplication(const TQString &program) {
	bool inTerminal = DomUtil::readBoolEntry(*projectDom(), "/kdevrubysupport/run/terminal");
    if (KDevAppFrontend *appFrontend = extension<KDevAppFrontend>("TDevelop/AppFrontend"))
        appFrontend->startAppCommand(TQString(), program, inTerminal);
}


KMimeType::List RubySupportPart::mimeTypes( )
{
    KMimeType::List list;
    KMimeType::Ptr mime = KMimeType::mimeType( "text/x-ruby" );
    if( mime )
	list << mime;
    return list;
}

KDevDesignerIntegration *RubySupportPart::designer(KInterfaceDesigner::DesignerType type)
{
    KDevDesignerIntegration *des = 0;
    switch (type)
    {
        case KInterfaceDesigner::TQtDesigner:
            des = m_designers[type];
            if (des == 0)
            {
                RubyImplementationWidget *impl = new RubyImplementationWidget(this);
                des = new QtDesignerRubyIntegration(this, impl);
                des->loadSettings(*project()->projectDom(),
                    "kdevrubysupport/designerintegration");
                m_designers[type] = des;
            }
            break;
        case KInterfaceDesigner::Glade:
		    break;
    }
    return des;
}

void RubySupportPart::projectClosed( )
{
    for (TQMap<KInterfaceDesigner::DesignerType, KDevDesignerIntegration*>::const_iterator it =  m_designers.begin();
        it != m_designers.end(); ++it)
    {
        kdDebug() << "calling save settings fro designer integration" << endl;
        it.data()->saveSettings(*project()->projectDom(), "kdevrubysupport/designerintegration");
    }
}

void RubySupportPart::contextMenu( TQPopupMenu * popup, const Context * context )
{
    if (context->hasType(Context::FileContext)){
        const FileContext *fc = static_cast<const FileContext*>(context);
        //this is a .ui file and only selection contains only one such file
        KURL url = fc->urls().first();
        if (url.fileName().endsWith(".ui"))
        {
            m_contextFileName = url.fileName();
            int id = popup->insertItem(i18n("Create or Select Implementation..."), this, TQT_SLOT(slotCreateSubclass()));
            popup->setWhatsThis(id, i18n("<b>Create or select implementation</b><p>Creates or selects a subclass of selected form for use with integrated KDevDesigner."));
        }
    }
}

void RubySupportPart::slotCreateSubclass()
{
    TQFileInfo fi(m_contextFileName);
    if (fi.extension(false) != "ui")
        return;
    QtDesignerRubyIntegration *des = dynamic_cast<QtDesignerRubyIntegration*>(designer(KInterfaceDesigner::TQtDesigner));
    if (des)
        des->selectImplementation(m_contextFileName);
}

void RubySupportPart::slotBrowse()
{
    kapp->invokeBrowser("http://localhost:3000/");
}

void RubySupportPart::slotSwitchToController()
{
    KParts::Part *activePart = partController()->activePart();
    if (!activePart)
        return;
    KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
    if (!ropart)
        return;
    TQFileInfo file(ropart->url().path());
    if (!file.exists())
        return;
    TQString ext = file.extension();
    TQString name = file.baseName();
    TQString switchTo = "";
    if ((ext == "rb") && !name.endsWith("_controller"))
    {
        if (name.endsWith("_test"))
        {
            switchTo = name.remove(TQRegExp("_test$"));  //the file is the test
            switchTo = name.remove(TQRegExp("_controller$"));  //remove functional test name parts
        }
        else
            switchTo = name;
    }
    else if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
    {
        //this is a view, we need to find the directory of this view and try to find
        //the controller basing on the directory information
        switchTo = file.dir().dirName();
    }
    TQString controllersDir = project()->projectDirectory() + "/app/controllers/";
    if (!switchTo.isEmpty())
    {
        if (switchTo.endsWith("s"))
            switchTo = switchTo.mid(0, switchTo.length()-1);
        TQString singular = controllersDir + switchTo + "_controller.rb";
        TQString plural = controllersDir + switchTo + "s_controller.rb";
        KURL url = KURL::fromPathOrURL(TQFile::exists(singular) ? singular : plural);
        partController()->editDocument(url);
    }
}

void RubySupportPart::slotSwitchToTest()
{
    KParts::Part *activePart = partController()->activePart();
    if (!activePart)
        return;
    KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
    if (!ropart)
        return;
    TQFileInfo file(ropart->url().path());
    if (!file.exists())
        return;
    TQString ext = file.extension();
    TQString name = file.baseName();
    TQString switchTo = "";

    if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
    {
        //this is a view already, let's show the list of all views for this model
        switchTo = file.dir().dirName();
    }
    else if (ext == "rb")
        switchTo = name.remove(TQRegExp("_controller$")).remove(TQRegExp("_controller_test$")).remove(TQRegExp("_test$"));

    if (switchTo.isEmpty())
        return;

    if (switchTo.endsWith("s"))
        switchTo = switchTo.mid(0, switchTo.length() - 1);

    KURL::List urls;
    TQString testDir = project()->projectDirectory() + "/test/";
    TQString functionalTestS = testDir + "functional/" + switchTo + "_controller_test.rb";
    TQString functionalTestP = testDir + "functional/" + switchTo + "s_controller_test.rb";
    TQString integrationTestS = testDir + "integration/" + switchTo + "_test.rb";
    TQString integrationTestP = testDir + "integration/" + switchTo + "s_test.rb";
    TQString unitTestS = testDir + "unit/" + switchTo + "_test.rb";
    TQString unitTestP = testDir + "unit/" + switchTo + "s_test.rb";
    if (TQFile::exists(functionalTestP)) urls << KURL::fromPathOrURL(functionalTestP);
    if (TQFile::exists(integrationTestP)) urls << KURL::fromPathOrURL(integrationTestP);
    if (TQFile::exists(unitTestP)) urls << KURL::fromPathOrURL(unitTestP);
    if (TQFile::exists(functionalTestS)) urls << KURL::fromPathOrURL(functionalTestS);
    if (TQFile::exists(integrationTestS)) urls << KURL::fromPathOrURL(integrationTestS);
    if (TQFile::exists(unitTestS)) urls << KURL::fromPathOrURL(unitTestS);

    KDevQuickOpen *qo = extension<KDevQuickOpen>("TDevelop/QuickOpen");
    if (qo && !urls.isEmpty())
        qo->quickOpenFile(urls);
}

void RubySupportPart::slotSwitchToModel()
{
    KParts::Part *activePart = partController()->activePart();
    if (!activePart)
        return;
    KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
    if (!ropart)
        return;
    TQFileInfo file(ropart->url().path());
    if (!file.exists())
        return;
    TQString ext = file.extension();
    TQString name = file.baseName();
    TQString switchTo = "";

    if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
    {
        //this is a view already, let's show the list of all views for this model
        switchTo = file.dir().dirName();
    }
    else if (ext == "rb" && (name.endsWith("_controller") || name.endsWith("_test")))
    {
        switchTo = name.remove(TQRegExp("_controller$")).remove(TQRegExp("_controller_test$")).remove(TQRegExp("_test$"));
    }

    if (switchTo.isEmpty())
        return;

    if (switchTo.endsWith("s"))
        switchTo = switchTo.mid(0, switchTo.length() - 1);

    TQString modelsDir = project()->projectDirectory() + "/app/models/";
    TQString singular = modelsDir + switchTo + "_controller.rb";
    TQString plural = modelsDir + switchTo + "s_controller.rb";
    KURL url = KURL::fromPathOrURL(TQFile::exists(singular) ? singular : plural);

    partController()->editDocument(KURL::fromPathOrURL(modelsDir + switchTo + ".rb"));
}

void RubySupportPart::slotSwitchToView()
{
    KParts::Part *activePart = partController()->activePart();
    if (!activePart)
        return;
    KParts::ReadOnlyPart *ropart = dynamic_cast<KParts::ReadOnlyPart*>(activePart);
    if (!ropart)
        return;
    TQFileInfo file(ropart->url().path());
    if (!file.exists())
        return;
    TQString ext = file.extension();
    TQString name = file.baseName();
    TQString switchTo = "";

    if (ext == "rjs" || ext == "rxml" || ext == "rhtml" || ext == "js.rjs" || ext == "xml.builder" || ext == "html.erb")
    {
        //this is a view already, let's show the list of all views for this model
        switchTo = file.dir().dirName();
    }
    else if (ext == "rb")
        switchTo = name.remove(TQRegExp("_controller$")).remove(TQRegExp("_controller_test$")).remove(TQRegExp("_test$"));

    if (switchTo.isEmpty())
        return;

    if (switchTo.endsWith("s"))
        switchTo = switchTo.mid(0, switchTo.length() - 1);

    KURL::List urls;
    TQDir viewsDir;
    TQDir viewsDirS = TQDir(project()->projectDirectory() + "/app/views/" + switchTo);
    TQDir viewsDirP = TQDir(project()->projectDirectory() + "/app/views/" + switchTo + "s");
    if (viewsDirS.exists())
        viewsDir = viewsDirS;
    else if (viewsDirP.exists())
        viewsDir = viewsDirP;
    else
        return;

    TQStringList views = viewsDir.entryList();

    for (TQStringList::const_iterator it = views.begin(); it != views.end(); ++it)
    {
        TQString viewName = *it;
        if ( !(viewName.endsWith("~") || viewName == "." || viewName == "..") )
            urls << KURL::fromPathOrURL(viewsDir.absPath() + "/" + viewName);
    }
    KDevQuickOpen *qo = extension<KDevQuickOpen>("TDevelop/QuickOpen");
    if (qo)
        qo->quickOpenFile(urls);
}

void RubySupportPart::slotRunTestUnderCursor()
{
    // if we can't save all parts, then the user canceled
    if ( partController()->saveAllFiles() == false )
        return;

    KParts::ReadOnlyPart *ro_part = dynamic_cast<KParts::ReadOnlyPart*>(partController()->activePart());
    TQString prog;
    if (ro_part != 0) {
        prog = ro_part->url().path();
    } else
        return;

    KTextEditor::ViewCursorInterface* activeViewCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>( ro_part->widget() );
    if (!activeViewCursor) return;

    unsigned int line, column;
    activeViewCursor->cursorPositionReal(&line, &column);
    CodeModelUtils::CodeModelHelper hlp(codeModel(), codeModel()->fileByName(prog));
    FunctionDom fun = hlp.functionAt(line, column);
    if (fun == 0) return;

    TQFileInfo program(prog);
    TQString cmd = TQString("%1 -K%2 -C\"%3\" -I\"%4\" \"%5\" %6")
                          .arg(interpreter())
                          .arg(characterCoding())
                          .arg(runDirectory())
                          .arg(program.dirPath())
                          .arg(program.fileName())
                          .arg(" -n " + fun->name());
    startApplication(cmd);
}

#include "rubysupport_part.moc"