/***************************************************************************
 *   Copyright (C) 2007-2010 by Peter Semiletov                            *
 *   peter.semiletov@gmail.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 3 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 <QtGui>
#include "utils.h"


bool file_exists (const QString &fileName)
{
  if (fileName.isNull() || fileName.isEmpty())
     return false;

  return QFile::exists (fileName);
}


QString qstring_load (const QString &fileName, const char *enc)
{
  QFile file (fileName);

  if (! file.open (QFile::ReadOnly | QFile::Text))
     return QString();

  QTextStream in(&file);
  in.setCodec (enc);

  return in.readAll();
}


void qstring_list_print (const QStringList &l)
{
  foreach (QString s, l)
          qDebug() << s;
}


bool qstring_save (const QString &fileName, const QString &data, const char *enc)
{
  QFile file (fileName);
  if (! file.open (QFile::WriteOnly | QFile::Text))
      return false;

  QTextStream out (&file);
  out.setCodec (enc);
  out << data;

  return true;
}


void create_menu_from_list (QObject *handler,
                            QMenu *menu,
                            const QStringList &list,
                            const char *method
                           )
{
  foreach (QString s, list)
          {
           if (! s.startsWith("#"))
              {
               QAction *act = new QAction (s, menu->parentWidget());
               handler->connect (act, SIGNAL(triggered()), handler, method);
               menu->addAction (act);
              } 
          }
}


QString mod_to_string (Qt::KeyboardModifiers k)
{
  QString s;   
   
  if (k & Qt::ShiftModifier)
     s += "Shift+";

  if (k & Qt::ControlModifier)
     s += "Ctrl+";

  if (k & Qt::AltModifier)
     s += "Alt+";

  if (k & Qt::MetaModifier)
     s+= "Meta+";

  return s;
}


QString keycode_to_string (int k)
{
 // return QKeySequence(k).toString();

  QString s;

  switch (k)
         {
          case Qt::Key_F1:
                          s = "F1";
                          break;

          case Qt::Key_F2:
                          s = "F2";
                          break;

          case Qt::Key_F3:
                          s = "F3";
                          break;

          case Qt::Key_F4:
                          s = "F4";
                          break;

          case Qt::Key_F5:
                          s = "F5";
                          break;

          case Qt::Key_F6:
                          s = "F6";
                          break;

          case Qt::Key_F7:
                          s = "F7";
                          break;

          case Qt::Key_F8:
                          s = "F8";
                          break;

          case Qt::Key_F9:
                          s = "F9";
                          break;

          case Qt::Key_F10:
                          s = "F10";
                          break;

          case Qt::Key_F11:
                          s = "F11";
                          break;

          case Qt::Key_F12:
                          s = "F12";
                          break;

          default:
                  s = QChar (k);
         }

  return s;
}


QStringList read_dir_entries (const QString &path)
{
  QDir dir (path);
  return dir.entryList (QDir::AllEntries | QDir::NoDotAndDotDot);
}


QString file_get_ext (const QString &file_name)
{
  if (file_name.isNull() || file_name.isEmpty())
      return QString();

  int i = file_name.lastIndexOf (".");
  if (i == -1)
      return QString();

  return file_name.mid (i + 1).toLower();
}


QStringList html_get_by_patt (const QString &s, const QString &spatt)
{
  QStringList result;

  int c = s.size();
  int i = 0;

  while (i < c)
        {
         int start = s.indexOf (spatt, i, Qt::CaseInsensitive);

         if (start == -1)
             break;

         int end = s.indexOf ('"', start + spatt.size());
         if (end == -1)
             break;

         result.prepend (s.mid (start + spatt.size(), (end - start) - spatt.size()));

         i = end + 1;
        }

  return result;
}


QHash <QString, QString> stringlist_to_hash (const QStringList &l)
{
  QHash <QString, QString> result;

  if (l.empty())
     return result;

  foreach (QString s, l)
          result.insert (s, s);

  return result;
}


QHash <QString, QString> hash_load (const QString &fname)
{
  QHash<QString, QString> result;

  if (! file_exists (fname))
     return result;

  result = stringlist_to_hash (qstring_load (fname).split ("\n"));
  return result;
}


QString toggle_fname_header_source (const QString &fileName)
{
  QFileInfo f (fileName);

  QString ext = f.suffix();

  if (ext == "c" || ext == "cpp" || ext == "cxx" || ext == "cc" || ext == "c++")
     {
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".h"))
        return f.absolutePath() + "/" + f.baseName () + ".h";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".hxx"))
        return f.absolutePath() + "/" + f.baseName () + ".hxx";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".h++"))
        return f.absolutePath() + "/" + f.baseName () + ".h++";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".hh"))
        return f.absolutePath() + "/" + f.baseName () + ".hh";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".hpp"))
        return f.absolutePath() + "/" + f.baseName () + ".hpp";
     }
  else
  if (ext == "h" || ext == "h++" || ext == "hxx" || ext == "hh" || ext == "hpp")
     {
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".c"))
        return f.absolutePath() + "/" + f.baseName () + ".c";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".cpp"))
        return f.absolutePath() + "/" + f.baseName () + ".cpp";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".cxx"))
        return f.absolutePath() + "/" + f.baseName () + ".cxx";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".c++"))
        return f.absolutePath() + "/" + f.baseName () + ".c++";
      else
      if (file_exists (f.absolutePath() + "/" + f.baseName () + ".cc"))
        return f.absolutePath() + "/" + f.baseName () + ".cc";
     }

  return fileName;
}


QHash <QString, QString> hash_load_keyval (const QString &fname)
{
  QHash <QString, QString> result;

  if (! file_exists (fname))
     return result;

  QStringList l = qstring_load (fname).split ("\n");

  foreach (QString s, l)
          {
           QStringList sl = s.split ("=");
           if (sl.size() > 1)
               result.insert (sl[0], sl[1]);
          }

  return result;
}


bool is_image (const QString &filename)
{
  QList <QByteArray> a = QImageReader::supportedImageFormats();

  foreach (QByteArray x, a)
          {
           QString t (x.data());
           if (filename.endsWith (t.prepend ("."), Qt::CaseInsensitive))
              return true;
          } 

  return false;
}


QString hash_keyval_to_string (const QHash <QString, QString> &h)
{
  QStringList l;

  foreach (QString s, h.keys())
          l.prepend (s.append ("=").append (h.value (s)));

  return l.join ("\n").trimmed();
}


QString hash_get_val (QHash<QString, QString> &h,
                      const QString &key,
                      const QString &def_val)
{
  QString result = h.value (key);
  if (result.isNull() || result.isEmpty())
     {
      result = def_val;
      h.insert (key, def_val);
     }

  return result;
}


QString string_between (const QString &source,
                        const QString &sep1,
                        const QString &sep2)
{
  QString result;
  int pos1 = source.indexOf (sep1);
  if (pos1 == -1)
     return result;

  int pos2 = source.indexOf (sep2, pos1 + sep1.size());
  if (pos2 == -1)
     return result;

  pos1 += sep1.size();
  
  result = source.mid (pos1, pos2 - pos1);
  return result;
}


QByteArray file_load (const QString &fileName)
{
  QFile file (fileName);
  QByteArray b;

  if (! file.open (QFile::ReadOnly))
      return b;

  b = file.readAll();
  return b;
}


bool char_is_shit (const QChar &c)
{
  if (! c.isNull() && ! c.isLetter())
     return true;
  
  return false;
}


void create_menu_from_dir (QObject *handler,
                           QMenu *menu,
                           const QString &dir,
                           const char *method
                           )
{
  menu->setTearOffEnabled (true); 
  QDir d (dir);
  QFileInfoList lst_fi = d.entryInfoList (QDir::NoDotAndDotDot | QDir::AllEntries,
                                          QDir::DirsFirst | QDir::IgnoreCase |
                                          QDir::LocaleAware | QDir::Name);

  foreach (QFileInfo fi, lst_fi)
         {
          if (fi.isDir())
             {
              QMenu *mni_temp = menu->addMenu (fi.fileName());
              create_menu_from_dir (handler, mni_temp,
                                    fi.filePath(), method);
             }
          else
              {
               QAction *act = new QAction (fi.fileName(), menu->parentWidget());
               act->setData (fi.filePath());
               handler->connect (act, SIGNAL(triggered()), handler, method);
               menu->addAction (act);
              }
         }
}


QImage image_scale_by (const QImage &source,
                       bool by_side,
                       int value,
                       Qt::TransformationMode mode)
{
  bool horisontal = (source.width() > source.height());

  int width;
  int height;

  if (by_side)
     {
      width = value;
      height = value;
     }
   else
       {
        width = get_value (source.width(), value);
        height = get_value (source.height(), value);
       }

  if (horisontal)
     return source.scaledToWidth (width, mode);
  else
      return source.scaledToHeight (height, mode);
}


QString change_file_ext (const QString &s, const QString &ext)
{
  int i = s.lastIndexOf (".");
  if (i == -1)
      return s;

  QString r (s);
  r.truncate (++i);
  r.append (ext);
  return r;
}


QStringList bytearray_to_stringlist (QList<QByteArray> a)
{
  QStringList r;
  foreach (QByteArray i, a)
          r.append (i.data());

  return r;
}


QString qstring_get_last_after (const QString &s, const QString &sep)
{
  int i = s.lastIndexOf (sep);
  if (i == -1)
     return QString();

  i++;

  QString t = s.right (s.size() - i);
  return t;
}


bool file_is_writable (const QString &fname)
{
  QFile f (fname);  
  return f.isWritable();
}


bool file_is_readable (const QString &fname)
{
  QFile f (fname);  
  return f.isReadable();
}


bool str_check (char *s1, char *s2, int size)
{
  for (int i = 0; i < size; i++)
       if (s1[i] != s2[i])
          return false;
   
  return true;
}


bool CWavReader::get_info (const QString fname)
{
  QFile fl (fname);
  
  if (! fl.open(QIODevice::ReadOnly))
     return false;
  
  QDataStream ds (&fl);

  t_wav_chunk_hdr hdr;

  while (! ds.atEnd())
        {
         ds.readRawData ((char *)&hdr, sizeof (hdr));
         
         if (str_check (hdr.chunk_id, (char*)"RIFF", 4))
            {
             char riff_type[4];
             ds.readRawData ((char *)&riff_type, sizeof (riff_type));
           } 
         else
         if (str_check (hdr.chunk_id, (char*)"fmt ", 4))
            {
             ds.readRawData ((char *)&wav_chunk_fmt, sizeof (wav_chunk_fmt));
             if (wav_chunk_fmt.bits_per_sample != 16)
                return false; 
            } 
         else
         if (str_check (hdr.chunk_id, (char*)"data", 4))
            {
             int nsamples = hdr.chunk_size / (wav_chunk_fmt.bits_per_sample / 8);
             
             double sqr_sum = 0.0;

             qint16 *ch_both = new qint16[nsamples];
             ds.readRawData ((char *)ch_both, hdr.chunk_size);
             for (int i = 0; i < nsamples; i++)
                  sqr_sum += (ch_both[i] * ch_both[i]);
         
             double srms = sqrt (sqr_sum / nsamples);
             rms = 20 * log10 (srms / SHRT_MAX);
                          
             delete [] ch_both;
             return true;
            }
        else
            ds.skipRawData (hdr.chunk_size); 
       }
  
  return false;  
}


QString int_to_binary (int n)
{
  QString result;
  int sz = sizeof (n) * 8 - 1;
  
  for (int i = sz; i > -1; i--)
      {
       if (n & (1 << i))
           result.append ("1");
       else
          result.append ("0");
       
       if (i % 4 == 0)
          result.append (" "); 
      } 
  
  return result;
}


QString string_reverse (const QString &s)
{
  QString sn;

  int c = s.length() - 1;
  int x = 0;

  for (int i = c; i > -1; i--)
      sn[x++] = s.at(i);

 return sn; 
}


unsigned int bin_to_decimal (const QString &s)
{
  unsigned int table[31];
  unsigned int c = 1;
  unsigned int result = 0;
  QString sn = string_reverse (s);

  table[0] = 1;
  
  for (int i = 1; i < 32; i++)
      {
       c *= 2;
       table[i] = c;
      }

  for (int i = 0; i < sn.size(); i++)
      if (sn[i] == '1')
         result += table[i];

  return result;
}


QString get_insert_image (const QString &file_name, const QString &full_path, const QString &markup_mode)
{
  if (! is_image (full_path))
     return QString();     

  QFileInfo inf (file_name);
  QDir dir (inf.absolutePath());

  QImage img;
  img.load (full_path);
  QString result;

  if (markup_mode == "HTML")
     result = QString ("<img src=\"%1\" border=\"0\" alt=\"\" width=\"%2\" height=\"%3\">").arg (
                       dir.relativeFilePath (full_path)).arg (img.width()).arg (img.height());
  else
  if (markup_mode == "XHTML")
     result = QString ("<img src=\"%1\" border=\"0\" alt=\"\" width=\"%2\" height=\"%3\" />").arg (
                       dir.relativeFilePath (full_path)).arg (img.width()).arg (img.height());
  else
  if (markup_mode == "Docbook")
     result = QString ("<mediaobject><imageobject>\n<imagedata fileref=\"%1\"/>\n</imageobject></mediaobject>").arg (
                        dir.relativeFilePath (full_path)) ;
  else
  if (markup_mode == "LaTeX")
      result = QString ("\\includegraphics{%1}").arg (dir.relativeFilePath (full_path));
  else
  if (markup_mode == "Lout")
      result = QString ("@IncludeGraphic {%1}").arg (dir.relativeFilePath (full_path));

   return result;
}


void CFilesList::iterate (QFileInfo &fi)
{
  if (fi.isDir())
     {
      QDir d (fi.absoluteFilePath());
      QFileInfoList lfi= d.entryInfoList (QDir::Dirs | QDir::Files |
                                          QDir::Readable | QDir::NoDotAndDotDot);
      for (int i = 0; i < lfi.count(); i++)
           iterate (lfi[i]);
     }
  else
  if (fi.isFile())
      list.append (fi.absoluteFilePath());
}


void CFilesList::get (const QString &path)
{
  list.clear();
  QDir d (path);
  QFileInfoList lfi= d.entryInfoList (QDir::Dirs | QDir::Files |
                                      QDir::Readable | QDir::NoDotAndDotDot);
  for (int i = 0; i < lfi.count(); i++)
      iterate (lfi[i]);
}


CFTypeChecker::CFTypeChecker (const QString &fnames, const QString &exts)
{
  lexts = qstring_load (exts).split ("\n");
  lnames = qstring_load (fnames).split ("\n");
}


bool CFTypeChecker::check (const QString &fname)
{
  bool result = lnames.contains (fname.toLower());
  if (! result)
     {
      QString ext = file_get_ext (fname);
      result = lexts.contains (ext.toLower());
     }

  return result;
}


QIcon load_icon (const QString &fname, int side)
{
  //QImage im (fname);
  //QIcon icon (im.scaledToWidth (side, Qt::SmoothTransformation).QPixmap::fromImage(im)); //Qt::FastTransformation
  QPixmap px (fname);
  QIcon icon (px.scaledToWidth (side, Qt::SmoothTransformation)); //Qt::FastTransformation

  return icon;
}
