/***************************************************************************
    copyright            : (C) 2003-2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License as  *
 *   published by the Free Software Foundation;                            *
 *                                                                         *
 ***************************************************************************/

#include "tellico_utils.h"
#include "tellico_kernel.h"
#include "latin1literal.h"
#include "tellico_debug.h"

#include <kapplication.h>
#include <klocale.h>
#include <kglobal.h>
#include <klibloader.h>
#include <kstandarddirs.h>
#include <kcharsets.h>

#include <tqregexp.h>
#include <tqdir.h>
#include <tqcursor.h>
#include <tqscrollview.h>

namespace {
  static const int STRING_STORE_SIZE = 997; // too big, too small?
}

TQColor Tellico::contrastColor;

TQString Tellico::decodeHTML(TQString text) {
  TQRegExp rx(TQString::fromLatin1("&(.+);"));
  rx.setMinimal(true);
  int pos = rx.search(text);
  while(pos > -1) {
    TQChar c = KCharsets::fromEntity(rx.cap(1));
    if(!c.isNull()) {
      text.replace(pos, rx.matchedLength(), c);
    }
    pos = rx.search(text, pos+1);
  }
  return text;
}

TQString Tellico::uid(int l, bool prefix) {
  TQString uid;
  if(prefix) {
    uid = TQString::fromLatin1("Tellico");
  }
  uid.append(kapp->randomString(TQMAX(l - uid.length(), 0)));
  return uid;
}

uint Tellico::toUInt(const TQString& s, bool* ok) {
  if(s.isEmpty()) {
    if(ok) {
      *ok = false;
    }
    return 0;
  }

  uint idx = 0;
  while(s[idx].isDigit()) {
    ++idx;
  }
  if(idx == 0) {
    if(ok) {
      *ok = false;
    }
    return 0;
  }
  return s.left(idx).toUInt(ok);
}

TQString Tellico::i18nReplace(TQString text) {
  // Because TQDomDocument sticks in random newlines, go ahead and grab them too
  static TQRegExp rx(TQString::fromLatin1("(?:\\n+ *)*<i18n>([^<]*)</i18n>(?: *\\n+)*"));
  int pos = rx.search(text);
  while(pos > -1) {
    text.replace(pos, rx.matchedLength(), i18n(rx.cap(1).utf8()));
    pos = rx.search(text, pos+rx.matchedLength());
  }
  return text;
}

TQStringList Tellico::findAllSubDirs(const TQString& dir_) {
  if(dir_.isEmpty()) {
    return TQStringList();
  }

  // TODO: build in symlink checking, for now, prohibit
  TQDir dir(dir_, TQString(), TQDir::Name | TQDir::IgnoreCase, TQDir::Dirs | TQDir::Readable | TQDir::NoSymLinks);

  TQStringList allSubdirs; // the whole list

  // find immediate sub directories
  const TQStringList subdirs = dir.entryList();
  for(TQStringList::ConstIterator subdir = subdirs.begin(); subdir != subdirs.end(); ++subdir) {
    if((*subdir).isEmpty() || *subdir == Latin1Literal(".") || *subdir == Latin1Literal("..")) {
      continue;
    }
    TQString absSubdir = dir.absFilePath(*subdir);
    allSubdirs += findAllSubDirs(absSubdir);
    allSubdirs += absSubdir;
  }
  return allSubdirs;
}

// Based on TQGDict's hash functions, Copyright (C) 1992-2000 Trolltech AS
// and used from Juk, Copyright (C) 2003 - 2004 by Scott Wheeler
int Tellico::stringHash(const TQString& str) {
  uint h = 0;
  uint g = 0;
  for(uint i = 0; i < str.length(); ++i) {
    h = (h << 4) + str.unicode()[i].cell();
    if((g = h & 0xf0000000)) {
      h ^= g >> 24;
    }
    h &= ~g;
  }

  int index = h;
  return index < 0 ? -index : index;
}

TQString Tellico::shareString(const TQString& str) {
  static TQString stringStore[STRING_STORE_SIZE];

  const int hash = stringHash(str) % STRING_STORE_SIZE;
  if(stringStore[hash] != str) {
    stringStore[hash] = str;
  }
  return stringStore[hash];
}

void Tellico::updateContrastColor(const TQColorGroup& cg_) {
  // if the value difference between background and highlight is more than ???
  // use highlight, else go lighter or darker
  int h1, s1, v1, h2, s2, v2;
  cg_.background().getHsv(&h1, &s1, &v1);

  TQColor hl = cg_.highlight();
  hl.getHsv(&h2, &s2, &v2);
  h2 += 120;
  s2 = 255;
  hl.setHsv(h2, s2, v2);

  if(KABS(v2-v1) < 48) {
    if(v1 < 128) {
      contrastColor = hl.light();
    } else {
      contrastColor = hl.dark();
    }
  } else {
    contrastColor = hl;
  }
}

KLibrary* Tellico::openLibrary(const TQString& libName_) {
  TQString path = KLibLoader::findLibrary(TQFile::encodeName(libName_));
  if(path.isEmpty()) {
    kdWarning() << "Tellico::openLibrary() - Could not find library '" << libName_ << "'" << endl;
    kdWarning() << "ERROR: " << KLibLoader::self()->lastErrorMessage() << endl;
    return 0;
  }

  KLibrary* library = KLibLoader::self()->library(TQFile::encodeName(path));
  if(!library) {
    kdWarning() << "Tellico::openLibrary() - Could not load library '" << libName_ << "'" << endl;
    kdWarning() << " PATH: " << path << endl;
    kdWarning() << "ERROR: " << KLibLoader::self()->lastErrorMessage() << endl;
    return 0;
  }

  return library;
}

TQColor Tellico::blendColors(const TQColor& color1, const TQColor& color2, int percent) {
  const double factor2 = percent/100.0;
  const double factor1 = 1.0 - factor2;

  const int r = static_cast<int>(color1.red()   * factor1 + color2.red()   * factor2);
  const int g = static_cast<int>(color1.green() * factor1 + color2.green() * factor2);
  const int b = static_cast<int>(color1.blue()  * factor1 + color2.blue()  * factor2);

  return TQColor(r, g, b);
}

TQString Tellico::minutes(int seconds) {
  int min = seconds / 60;
  seconds = seconds % 60;
  return TQString::number(min) + ':' + TQString::number(seconds).rightJustify(2, '0');
}

TQString Tellico::saveLocation(const TQString& dir_) {
  return TDEGlobal::dirs()->saveLocation("appdata", dir_, true);
}

Tellico::GUI::CursorSaver::CursorSaver(const TQCursor& cursor_) : m_restored(false) {
  kapp->setOverrideCursor(cursor_);
}

Tellico::GUI::CursorSaver::~CursorSaver() {
  if(!m_restored) {
    kapp->restoreOverrideCursor();
  }
}

void Tellico::GUI::CursorSaver::restore() {
  kapp->restoreOverrideCursor();
  m_restored = true;
}