/***************************************************************************
 *   Copyright (C) 2007 by Marián Kyral                                    *
 *   mkyral@email.cz                                                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include "plugin_sort.h"
#include "sortdialog.h"

#include <tdeaction.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <map>
#include <tqregexp.h>

class PluginView : public KXMLGUIClient
{
  friend class KatePluginSort;

  public:
    Kate::MainWindow *win;
};

extern "C"
{
  void* init_katesortplugin()
  {
    TDEGlobal::locale()->insertCatalogue("katesort");
    return new KatePluginFactory;
  }
}

KatePluginFactory::KatePluginFactory()
{
  s_instance = new TDEInstance( "kate" );
}

KatePluginFactory::~KatePluginFactory()
{
  delete s_instance;
}

TQObject* KatePluginFactory::createObject( TQObject* parent, const char* name, const char*, const TQStringList & )
{
  return new KatePluginSort( parent, name );
}

TDEInstance* KatePluginFactory::s_instance = 0L;

KatePluginSort::KatePluginSort( TQObject* parent, const char* name )
    : Kate::Plugin ( (Kate::Application*)parent, name )
{
}

KatePluginSort::~KatePluginSort()
{
}

void KatePluginSort::addView(Kate::MainWindow *win)
{
    /// @todo doesn't this have to be deleted?
    PluginView *view = new PluginView ();

     (void) new TDEAction ( i18n("Sort"), 0, this,
                      SLOT( slotSort() ), view->actionCollection(),
                      "edit_insert_sort" );

    view->setInstance (new TDEInstance("kate"));
    view->setXMLFile("plugins/sort/plugin_sort.rc");
    win->guiFactory()->addClient (view);
    view->win = win;

   m_views.append (view);
}
void KatePluginSort::removeView(Kate::MainWindow *win)
{
  for (uint z=0; z < m_views.count(); z++)
    if (m_views.at(z)->win == win)
    {
      PluginView *view = m_views.at(z);
      m_views.remove (view);
      win->guiFactory()->removeClient (view);
      delete view;
    }
}

void KatePluginSort::slotSort()
{
  Kate::View *kv = application()->activeMainWindow()->viewManager()->activeView();

  if (! kv)
    return;

  // display dialog part
  SortDialog m_sortDialog;
  
  // if only part of one row is selected. update By column part of dialog
  if (! kv->getDoc()->selection().isEmpty())
  {
    if (kv->getDoc()->selStartLine() == kv->getDoc()->selEndLine() &&
        kv->getDoc()->lineLength(kv->getDoc()->selStartLine()) != -1 )
    {
      uint sel_sc = kv->getDoc()->selStartCol() + 1;
      uint sel_ec = kv->getDoc()->selEndCol() + 1;
      if (! (sel_sc == 0 && (int) sel_ec == kv->getDoc()->lineLength(kv->getDoc()->selStartLine())))
      {
        m_sortDialog.m_checkBoxByCol->setChecked(true);
        m_sortDialog.m_lineEditStartCol->setText(TQString::number(sel_sc,10));
        m_sortDialog.m_lineEditEndCol->setText(TQString::number(sel_ec,10));
      }
    }
    kv->getDoc()->clearSelection();
  }
  if (m_sortDialog.exec() == TQDialog::Rejected)
  return;

  if (kv->getDoc()->selection().isEmpty())
    kv->getDoc()->selectAll();
  if (kv->getDoc()->selection().isEmpty())
    return; // Nothing to sort !

  uint sel_sl = kv->getDoc()->selStartLine();
  uint sel_el = kv->getDoc()->selEndLine();
  if (kv->getDoc()->selStartCol() > 0 && kv->getDoc()->selStartCol() == kv->getDoc()->lineLength(sel_sl))
    sel_sl++ ;
  if (kv->getDoc()->selEndCol() == 0 && kv->getDoc()->lineLength(sel_el) >0)
    sel_el-- ;

  // split string to lines
  TQStringMultiMap strMLines; // alphabetical sort multimap
  LongMultiMap longMLines;     // numerical sort multimap


  // Map filling...
  TQString skey;
  TQString sdata;
  int ikey, non_num_ind;
  TQRegExp rx("[^0-9]"); // Search regexp for not number character
  for (uint i = sel_sl; i <= sel_el; i++)
  {
    sdata = kv->getDoc()->textLine(i);
    skey  = sdata;
    if (m_sortDialog.m_checkBoxByCol->isChecked())
    {
      skey = skey.mid(m_sortDialog.m_lineEditStartCol->text().toInt() - 1,
                      m_sortDialog.m_lineEditEndCol->text().toInt() - m_sortDialog.m_lineEditStartCol->text().toInt());
//       tqDebug("skey: %s", skey.ascii());
    }

//      tqDebug("\tLine: %d",i);
//      tqDebug("Key: %s, Line content: %s", skey.ascii(),sdata.ascii());
    if (m_sortDialog.m_radioButtonAlphaSort->isChecked())
    {
      if (m_sortDialog.m_checkBoxCase->isChecked())
      {
        // Case sensitive sort
          strMLines.insert(std::pair<TQString, TQString>(skey ,sdata));
      }
      else
      {
        // Case insensitive sort
          strMLines.insert(std::pair<TQString, TQString>(skey.lower(), sdata));
      }
    }
    else
    {
      // Numeric sort
      skey = skey.stripWhiteSpace();
      if (skey.toLong() == 0)
      { // key is not number
        non_num_ind = skey.find(rx,0);
        if (non_num_ind != -1)
        { // beginning of key is number
//           tqDebug("non_num_ind: %d",non_num_ind);
          skey.truncate(non_num_ind);
        }
        else
        {
          skey = "0";
        }
      }
//       tqDebug("Key: %s",skey.ascii());
      longMLines.insert(std::pair<long, TQString>(skey.toLong(), sdata));
    }
  }

  // Insert result back to document
  // Remove selection
  kv->getDoc()->removeText(sel_sl,0,sel_el,kv->getDoc()->lineLength(sel_el));
//  kv->updateView(false);

  TQStringMultiMap::iterator smit, smsit, emsit;
  LongMultiMap::iterator lmit, slmit, elmit;
  bool fasc;
  uint i=sel_sl; // insert start line
  bool first=true; // First line flag
  TQString prevLine; // Store previous line (for unique purpose)

  if (m_sortDialog.m_radioButtonAlphaSort->isChecked())
  {
    if (m_sortDialog.m_radioButtonAsc->isChecked())
    {
    // Ascendent
      smsit=strMLines.begin();
      emsit=strMLines.end();
      fasc=true;
    } //m_sortDialog.m_radioButtonAsc->isChecked()
    else
    {
    // Descendent
      smsit=strMLines.end();
      smsit++;
      emsit=strMLines.begin();
      emsit--;
      fasc=false;
    } //m_sortDialog.m_radioButtonAsc->isChecked()
    for( smit=smsit; smit != emsit; fasc ? ++smit : --smit )
    {
      sdata = smit->second;
      skey = smit->first;
//       tqDebug("Key: %s, Line content: %s", skey.ascii(),sdata.ascii());

      if (m_sortDialog.m_checkBoxUnique->isChecked())
      {
        if ( prevLine.compare(sdata) != 0 || first ) //remove duplicities
        {
//           tqDebug("Inserting line: %d",i);
          first = false;
          prevLine = sdata;
          kv->getDoc()->insertLine(i, sdata);
          i++;
        }
      } // m_sortDialog.m_checkBoxUnique->isChecked()
      else
      {
        prevLine = sdata;
//         tqDebug("Inserting line: %d",i);
        kv->getDoc()->insertLine(i, sdata);
        i++;
      } // m_sortDialog.m_checkBoxUnique->isChecked()
    } //for
  } //m_sortDialog.m_radioButtonAlphaSort->isChecked()
  else
  {
    if (m_sortDialog.m_radioButtonAsc->isChecked())
    {
    // Ascendent
      slmit=longMLines.begin();
      elmit=longMLines.end();
      fasc=true;
    } //m_sortDialog.m_radioButtonAsc->isChecked()
    else
    {
    // Descendent
      slmit=longMLines.end();
      ++slmit;
      elmit=longMLines.begin();
      --elmit;
      fasc=false;
    } //m_sortDialog.m_radioButtonAsc->isChecked()
    for( lmit=slmit; lmit != elmit; fasc ? ++lmit : --lmit )
    {
      sdata = lmit->second;
      ikey = lmit->first;

//       tqDebug("Key: %d, Line content: %s", ikey,sdata.ascii());
      if (m_sortDialog.m_checkBoxUnique->isChecked())
      {
        if ( prevLine.compare(sdata) != 0 || first ) //remove duplicities
        {
//           tqDebug("Inserting line: %d",i);
          first = false;
          prevLine = sdata;
          kv->getDoc()->insertLine(i, sdata);
          i++;
        }
      } // m_sortDialog.m_checkBoxUnique->isChecked()
      else
      {
        prevLine = sdata;
//         tqDebug("Inserting line: %d",i);
        kv->getDoc()->insertLine(i, sdata);
        i++;
      } // m_sortDialog.m_checkBoxUnique->isChecked()
    } //for
  }
  // Delete last blank line
  kv->getDoc()->removeLine(i);

}

#include "plugin_sort.moc"

