/***************************************************************************
 *   Copyright (C) 2005 by Florian Roth   *
 *   florian@synatic.net   *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#include "metabarwidget.h"
#include "configdialog.h"

#include "defaultplugin.h"
#include "settingsplugin.h"
#include "remoteplugin.h"
#include "httpplugin.h"

#include <tqwidget.h>
#include <tqlayout.h>
#include <tqdir.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqvaluelist.h>
#include <tqurl.h>
#include <tqbuffer.h>

#include <khtmlview.h>
#include <kapplication.h>
#include <kstandarddirs.h>
#include <kurl.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <kicontheme.h>
#include <ktrader.h>
#include <klocale.h>
#include <krun.h>
#include <kdesktopfile.h>
#include <kpropertiesdialog.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <kshortcut.h>
#include <kmimetype.h>
#include <kcmoduleinfo.h>
#include <kmdcodec.h>

#include <kparts/browserextension.h>

#include <dom2_events.h>
#include <dom2_views.h>
#include <dom_doc.h>
#include <dom_element.h>
#include <dom_string.h>
#include <html_element.h>
#include <html_misc.h>
#include <html_image.h>
#include <css_value.h>

#include <dcopref.h>
#include <dcopclient.h>

#define EVENT_TYPE DOM::DOMString("click")
#define ACTIVATION 1

MetabarWidget::MetabarWidget(TQWidget *parent, const char *name) : TQWidget(parent, name)
{
  skip = false;
  loadComplete = false;

  currentItems = new KFileItemList;
  currentItems->setAutoDelete(true);
  
  config = new KConfig("metabarrc");
  
  dir_watch = new KDirWatch();
  connect(dir_watch, TQT_SIGNAL(dirty(const TQString&)), this, TQT_SLOT(slotUpdateCurrentInfo(const TQString&)));
  connect(dir_watch, TQT_SIGNAL(created(const TQString&)), this, TQT_SLOT(slotUpdateCurrentInfo(const TQString&)));
  connect(dir_watch, TQT_SIGNAL(deleted(const TQString&)), this, TQT_SLOT(slotDeleteCurrentInfo(const TQString&)));

  html = new KHTMLPart(this, "metabarhtmlpart");
  html->setJScriptEnabled(true);
  html->setPluginsEnabled(true);
  html->setCaretVisible(false);
  html->setDNDEnabled(false);
  html->setJavaEnabled(false);
  html->view()->tqsetSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding);
  html->view()->hide();
  
  connect(html->browserExtension(), TQT_SIGNAL(openURLRequest( const KURL &, const KParts::URLArgs & )), this, TQT_SLOT(handleURLRequest(const KURL &, const KParts::URLArgs &)));
  connect(html, TQT_SIGNAL(completed()), this, TQT_SLOT(loadCompleted()));
  connect(html, TQT_SIGNAL(popupMenu(const TQString &, const TQPoint &)), this, TQT_SLOT(slotShowPopup(const TQString&, const TQPoint &)));
  
  functions = new MetabarFunctions(html, TQT_TQOBJECT(this));
  
  currentPlugin = 0;
  defaultPlugin = new DefaultPlugin(html, functions);  
  HTTPPlugin *httpPlugin = new HTTPPlugin(html, functions);
  
  //plugins.setAutoDelete(true);
  plugins.insert("settings", new SettingsPlugin(html, functions));
  plugins.insert("remote", new RemotePlugin(html, functions));
  //plugins.insert("trash", new TrashPlugin(html, functions));
  plugins.insert("http", httpPlugin);
  plugins.insert("https", httpPlugin);
  
  TQVBoxLayout *tqlayout = new TQVBoxLayout(this);
  tqlayout->addWidget(html->view());
  
  popup = new KPopupMenu(0);
  KAction *configAction = new KAction(i18n("Configure %1...").tqarg("Metabar"), "configure", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotShowConfig()), html->actionCollection(), "configure");
  configAction->plug(popup);
  
  KAction *reloadAction = new KAction(i18n("Reload Theme"), "reload", KShortcut(), TQT_TQOBJECT(this), TQT_SLOT(setTheme()), html->actionCollection(), "reload");
  reloadAction->plug(popup);

  setTheme();
}

MetabarWidget::~MetabarWidget()
{
  config->sync();
  delete config;

  delete dir_watch;
  delete currentItems;
}

void MetabarWidget::setFileItems(const KFileItemList &items, bool check)
{
  if(!loadComplete){
    return;
  }

  if(skip){
    skip = false;
    return;
  }

  if(check){
    int count = items.count();
    
    KURL url(getCurrentURL());
    
    KFileItem *newItem = items.getFirst();
    KFileItem *oldItem = currentItems->getFirst();
  
    if(count == 0){
      if(oldItem){
        if(oldItem->url() == url){
          return;
        }
      }
      
      currentItems->clear();
      currentItems->append(new KFileItem(S_IFDIR, KFileItem::Unknown, url, true));
    }
    
    else if(count == 1){
      if(newItem){
        if(newItem->url().isEmpty()){
          return;
        }
        
        if(currentItems->count() == items.count() && oldItem){
          if(newItem->url() == oldItem->url()){
            return;
          }
        }
      }
          
      currentItems = new KFileItemList(items);
    }
    
    else{
      if(currentItems && *currentItems == items){
        return;
      }
      currentItems = new KFileItemList(items);
    }
  }
  else{
    currentItems = new KFileItemList(items);
  }
  
  if(currentPlugin){
    currentPlugin->deactivate();
  }
  
  TQString protocol = currentItems->getFirst()->url().protocol();
  currentPlugin = plugins[protocol];
  
  if(!currentPlugin){
    currentPlugin = defaultPlugin;
  }
  
  ProtocolPlugin::activePlugin = currentPlugin;
  currentPlugin->setFileItems(*currentItems);
}

TQString MetabarWidget::getCurrentURL()
{
  DCOPRef ref(kapp->dcopClient()->appId(), this->tqtopLevelWidget()->name());
  DCOPReply reply = ref.call("currentURL()");
    
  if (reply.isValid()) {
    TQString url;
    reply.get(url, TQSTRING_OBJECT_NAME_STRING);
      
    if(!url.isNull() && !url.isEmpty()){
      return url;
    }
  }
  return 0;
}

void MetabarWidget::openURL(const TQString &url)
{
  DCOPRef ref(kapp->dcopClient()->appId(), this->tqtopLevelWidget()->name());
  DCOPReply reply = ref.call("openURL", url);
}

void MetabarWidget::openTab(const TQString &url)
{
  DCOPRef ref(kapp->dcopClient()->appId(), this->tqtopLevelWidget()->name());
  DCOPReply reply = ref.call("newTab", url);
}

void MetabarWidget::callAction(const TQString &action)
{
  DCOPRef ref(kapp->dcopClient()->appId(), TQString(TQString(this->tqtopLevelWidget()->name()).append("/action/").append(action)).utf8());
  if(ref.call("enabled()")){
    ref.call("activate()");
  }
}

void MetabarWidget::loadLinks()
{
  config->setGroup("General");
  TQStringList links = config->readListEntry("Links");
  
  if(links.count() == 0){
    functions->hide("links");
  }
  else{
    functions->show("links");
  
    DOM::HTMLDocument doc = html->htmlDocument();
    DOM::HTMLElement node = doc.getElementById("links");
    
    if(!node.isNull()){
      DOM::DOMString innerHTML;
    
      for(TQStringList::Iterator it = links.begin(); it != links.end(); ++it){
        config->setGroup("Link_" + (*it));     
        addEntry(innerHTML, config->readEntry("Name"), config->readEntry("URL"), config->readEntry("Icon", "folder"));
      }
  
      node.setInnerHTML(innerHTML);
    }
    
    functions->adjustSize("links");
  }
}

void MetabarWidget::loadCompleted()
{
  DOM::HTMLDocument doc = html->htmlDocument();

  DOM::NodeList i18n_a_list = doc.getElementsByTagName("a");
  for(uint i = 0; i < i18n_a_list.length(); i++){
    DOM::HTMLElement node = static_cast<DOM::HTMLElement>(i18n_a_list.item(i));
    if(!node.isNull()){
      if(node.hasAttribute("i18n")){
        TQString text = node.innerText().string();
        node.setInnerText(DOM::DOMString(i18n(text.utf8().data())));
      }
      
      if(node.hasAttribute("image")){
        TQString icon = node.getAttribute("image").string();
        TQString url = getIconPath(icon);
        TQString style = TQString("background-image: url(%1);").tqarg(url);
        
        node.setAttribute("style", style);
      }
    }
  }
  
  DOM::NodeList i18n_ul_list = doc.getElementsByTagName("ul");
  for(uint i = 0; i < i18n_ul_list.length(); i++){
    DOM::HTMLElement node = static_cast<DOM::HTMLElement>(i18n_ul_list.item(i));
    if(!node.isNull()){
      if(node.hasAttribute("i18n")){
        TQString text = node.innerText().string();
        node.setInnerText(DOM::DOMString(i18n(text.utf8().data())));
      }
    }
  }

  config->setGroup("General");
  TQString file = locate("data", TQString("metabar/themes/%1/default.css").tqarg(config->readEntry("Theme", "default")));
  if(file.isNull()){
    file = locate("data", TQString("metabar/themes/default/default.css"));
  }
  
  TQFile cssfile(file);
  if(cssfile.open(IO_ReadOnly)){  
    TQTextStream stream( &cssfile );
    TQString tmp = stream.read();
    cssfile.close();
    
    tmp.replace("./", KURL::fromPathOrURL(file).directory(false));    
    html->setUserStyleSheet(tmp);
  }
  
  loadComplete = true;
  html->view()->setFrameShape(config->readBoolEntry("ShowFrame", true) ? TQFrame::StyledPanel : TQFrame::NoFrame);
  html->view()->show();
  
  if(currentItems && !currentItems->isEmpty()){
    setFileItems(*currentItems, false);
  }
  else{
    TQString url = getCurrentURL();
    KFileItem *item = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, KURL(url), true);
    KFileItemList list;
    list.append(item);
    setFileItems(list, false);
  }
  
  loadLinks();
}

void MetabarWidget::handleURLRequest(const KURL &url, const KParts::URLArgs &args)
{  
  if(!currentPlugin){
    return;
  }

  TQString protocol = url.protocol();
  
  if(currentPlugin->handleRequest(url)){
    return;
  }
  
  if(protocol == "desktop"){
    TQString path = url.path();
    
    if(KDesktopFile::isDesktopFile(path)){
      KRun::run(new KDesktopFile(path, true), KURL::List());
    }
  }
  
  else if(protocol == "kcmshell"){
    TQString module = url.path().remove('/');
    
    KRun::runCommand("kcmshell " + module);
  }
  
  else if(protocol == "action"){
    TQString action = url.url().right(url.url().length() - 9);    
    if(action.startsWith("metabar/")){
      TQString newact = action.right(action.length() - 8);
      
      if(newact == "share"){
        slotShowSharingDialog();
      }
    }
    else{
      callAction(action);
    }
  }
  
  else if(protocol == "preview"){
    if(currentItems && !currentItems->isEmpty()){
      KFileItem *item = currentItems->getFirst();
      
      DOM::HTMLDocument doc = html->htmlDocument();
      DOM::HTMLElement node = static_cast<DOM::HTMLElement>(doc.getElementById("preview"));
      DOM::HTMLImageElement image = static_cast<DOM::HTMLImageElement>(doc.getElementById("previewimage"));
      
      if(!node.isNull()){
        skip = true; //needed to prevent some weired reload
      
        DOM::DOMString innerHTML;
        innerHTML += TQString("<ul style=\"width: %1px; height: %1px\">").tqarg(image.width(), image.height());
        innerHTML += "<object class=\"preview\" type=\"";
        innerHTML += item->mimetype();
        innerHTML += "\" data=\"";
        innerHTML += item->url().url();
        innerHTML += "\" width=\"";
        innerHTML += TQString(TQString().setNum(image.width()));
        innerHTML += "\" height=\"";
        innerHTML += TQString(TQString().setNum(image.height()));
        innerHTML += "\" /></ul>";
        node.setInnerHTML(innerHTML);
      }
    }
  }
  
  else if(protocol == "more"){
    TQString name = url.host();
    
    DOM::HTMLDocument doc = html->htmlDocument();
    DOM::NodeList list = doc.getElementsByName(name);
    DOM::HTMLElement element = static_cast<DOM::HTMLElement>(doc.getElementById(name));
    bool showMore = true;
    
    for(uint i = 0; i < list.length(); i++){
      DOM::HTMLElement node = static_cast<DOM::HTMLElement>(list.item(i));
      if(!node.isNull()){   
        DOM::HTMLElement parent = static_cast<DOM::HTMLElement>(node.parentNode());
        DOM::CSSStyleDeclaration style = parent.style();
        DOM::DOMString display = style.getPropertyValue("display");
        DOM::DOMString newDisplay = display == "none" ? "block" : "none";
        
        style.setProperty("display", newDisplay, "important");
        
        showMore = display == "block";
      }
    }
    
    if(element.id().string().startsWith("hidden")){
      TQString style = TQString("background-image: url(%1);").tqarg(getIconPath(showMore ? "1downarrow" : "1uparrow"));
      element.setInnerText( showMore ? i18n("More") : i18n("Less") );
      element.setAttribute("style", style);
    }
    
    DOM::HTMLElement parent = static_cast<DOM::HTMLElement>(element.parentNode().parentNode());
    functions->adjustSize(parent.id());
  }
  
  else if(protocol == "function"){
    functions->handleRequest(url);
  }

  else if(protocol == "configure"){
    slotShowConfig();
  }
  
  else if(protocol == "openwith"){
    if(currentItems && !currentItems->isEmpty()){
      KFileItem *item = currentItems->getFirst();
      
      KRun::displayOpenWithDialog(KURL::List(item->url()), false);
    }
  }
  
  else{
    if(args.newTab()){
      openTab(url.url());
    }
    else{
      openURL(url.url());
    }
  }
}

TQString MetabarWidget::getIconPath(const TQString &name)
{
  TQPixmap icon = SmallIcon(name);
  
  TQByteArray data;
  TQBuffer buffer(data);
  buffer.open(IO_WriteOnly);
  icon.save(&buffer, "PNG");
  
  return TQString::tqfromLatin1("data:image/png;base64,%1").tqarg(KCodecs::base64Encode(data).data());
}

void MetabarWidget::slotShowSharingDialog()
{
  if(currentItems && currentItems->count() == 1){
    KPropertiesDialog *dialog = new KPropertiesDialog(currentItems->first(), 0, 0, true);
    dialog->showFileSharingPage();
  }
}

void MetabarWidget::slotShowConfig()
{
  ConfigDialog *config_dialog = new ConfigDialog(this);
  if(config_dialog->exec() == TQDialog::Accepted){
    config->reparseConfiguration();
    
    setFileItems(*currentItems, false);
    loadLinks();
    
    setTheme();
    
    html->view()->setFrameShape(config->readBoolEntry("ShowFrame", true) ? TQFrame::StyledPanel : TQFrame::NoFrame);
  }
  
  delete config_dialog;
}

void MetabarWidget::slotShowPopup(const TQString &url, const TQPoint &point)
{
  popup->exec(point);
}

void MetabarWidget::slotUpdateCurrentInfo(const TQString &path)
{
  if(currentItems){
    KFileItem *item = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, KURL(path), true);
    
    if(currentItems->count() == 1){
      currentItems->clear();
      currentItems->append(item);
    }
    
    setFileItems(*currentItems, false);
  }
}

void MetabarWidget::slotDeleteCurrentInfo(const TQString&)
{
  if(currentItems && currentItems->count() == 1){
    TQString url = getCurrentURL();
    KURL currentURL;
     
    if(currentItems){
      currentURL = currentItems->getFirst()->url();
    }
    
    if(!currentURL.isEmpty() && KURL(url) != currentURL){
      if(dir_watch->contains(currentURL.path())){
        dir_watch->removeDir(currentURL.path());
      }
      dir_watch->addDir(url);
          
      KFileItem *item = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true);
      
      currentItems->clear();
      currentItems->append(item);
        
      setFileItems(*currentItems, false);
    }
  }
}

void MetabarWidget::addEntry(DOM::DOMString &html, const TQString name, const TQString url, const TQString icon, const TQString id, const TQString nameatt, bool hidden)
{
  html += "<ul";
  
  if(hidden){
    html += " style=\"display: none;\"";
  }
  
  html += "><a";
  
  if(!id.isNull() && !id.isEmpty()){
    html += " id=\"";
    html += id;
    html += "\"";
  }
  
  if(!nameatt.isNull() && !nameatt.isEmpty()){
    html += " name=\"";
    html += nameatt;
    html += "\"";
  }
  
  html += " href=\"";
  html += url;
  html += "\" onClick=\"this.blur();\" style=\"background-image: url(";
  html += getIconPath(icon);
  html += ");\">";
  html += name;
  html += "</a></ul>";
}

void MetabarWidget::setTheme()
{
  loadComplete = false;

  config->setGroup("General");
  TQString file = locate("data", TQString("metabar/themes/%1/tqlayout.html").tqarg(config->readEntry("Theme", "default")));
  
  html->openURL(KURL(file));
}

#include "metabarwidget.moc"
