#include <qfile.h>
#include <qtextstream.h>
#include <qregexp.h>
#include <qstringlist.h>
#include <qdir.h>

#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>
#include <config.h>

// #include <X11/Xatom.h>
// #include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBrules.h>

#include "rules.h"

const QString X11_DIR( "/usr/X11R6/lib/X11/" );

KeyRules::KeyRules(QString rule):
    _layouts(80)
{
  loadRules(X11_DIR + QString("xkb/rules/%1").arg(rule));
  loadEncodings(X11_DIR + QString("locale/locale.alias"));
}


void KeyRules::loadRules(QString file)
{
// THIS IS TEMPORARY!!!
// This should be fixed in XFree86 (and actually is fixed in XFree 4.2)
// some handcoded ones, because the X11 rule file doesn't get them correctly, or in case
// the rule file wasn't found
static struct {
    const char * locale;
    const char * layout;
} fixedLayouts[] = {
    { "ben", "Bengali" },
    { "ar", "Arabic" },
    { "ir", "Farsi" },
    { 0, 0 }
};

  XkbRF_RulesPtr rules;

  rules = XkbRF_Load(QFile::encodeName(file).data(), KGlobal::locale()->language().utf8().data(), true, true);
  
  if (rules == NULL)
  {
     kdDebug() << "Unable to load rules" << endl;
     return;
  }
  
  int i;
  for (i = 0; i < rules->models.num_desc; ++i)
      _models.replace(rules->models.desc[i].name, qstrdup( rules->models.desc[i].desc ) );
  for (i = 0; i < rules->layouts.num_desc; ++i)
      _layouts.replace(rules->layouts.desc[i].name, qstrdup( rules->layouts.desc[i].desc ) );
  for (i = 0; i < rules->options.num_desc; ++i)
      _options.replace(rules->options.desc[i].name, qstrdup( rules->options.desc[i].desc ) );
  
  XkbRF_Free(rules, true);
  
  // Knowing which variants we have won't help us
  // We want to know which particular variants are available for each language

  for(int i=0; fixedLayouts[i].layout != 0; i++ ) {
      // shall we replace or skip when exists
      _layouts.replace(fixedLayouts[i].locale, fixedLayouts[i].layout);
  }
}

void KeyRules::loadEncodings(QString file)
{
// some handcoded ones, because the X11 locale file doesn't get them correctly, or in case
// the locale file wasn't found
static struct {
    const char * locale;
    const char * encoding;
    unsigned int initialGroup;
} encs[] = {
    { "ar", "ISO8859-6", 1 },
    { "be", "ISO8859-1", 0 },
    { "ben", "ISO10646-1", 0 },
    { "br", "ISO8859-1", 0 },
    { "bg", "ISO8859-5", 1 },
    { "by", "CP 1251", 1 },
    { "ca", "ISO8859-1", 0 },
    { "cs", "ISO8859-2", 0 },
    { "cz", "ISO8859-2", 0 },
    { "de", "ISO8859-1", 0 },
    { "de_CH", "ISO8859-1", 0 },
    { "dk", "ISO8859-1", 0 },
    { "ee", "ISO8859-15", 0 },
    { "en_US", "ISO8859-1", 0 },
    { "el", "ISO8859-7", 1 },
    { "es", "ISO8859-1", 0 },
    { "eo", "ISO8859-3", 0 },
    { "fi", "ISO8859-1", 0 },
    { "fr", "ISO8859-1", 0 },
    { "fr_CH", "ISO8859-1", 0 },
    { "he", "ISO8859-8-i", 1 },
    { "hu", "ISO8859-2", 0 },
    { "hr", "ISO8859-2", 0 },
    { "il", "ISO8859-8-i", 1 },
    { "ir", "UTF-8", 1 },
    { "it", "ISO8859-1", 0 },
    { "kl", "ISO8859-1", 0 },
    { "lt", "ISO8859-13", 1 },
    { "lv", "ISO8859-13", 0 },
    { "mk", "ISO8859-5", 1 },
    { "nl", "ISO8859-1", 0 },
    { "no", "ISO8859-1", 0 },
    { "pl", "ISO8859-2", 0 },
    { "pt", "ISO8859-1", 0 },
    { "ro", "ISO8859-2", 0 },
    { "ru", "KOI8-R", 1 },
    { "ru_UA", "KOI8-U", 0 },
    { "se", "ISO8859-1", 0 },
    { "sk", "ISO8859-2", 0 },
    { "th", "ISO8859-11", 1 },
    { "us", "ISO8859-1", 0 },
    { "ua", "KOI8-U", 1 },
    { 0, 0, 0 }
};

  QFile f(file);
  if (f.open(IO_ReadOnly))
    {
      QTextStream ts(&f);

      QString line;
      while (!ts.eof()) {
	  line = ts.readLine().simplifyWhiteSpace();

	    if ( line.isEmpty() || line[0] == '#' )
		continue;

	    int pos = line.find(' ');
	    if (pos > 0) {
		_encodings.remove(line.left(pos));
		int pos2 = line.find('.', pos);
		_encodings.insert(line.left(pos), strdup(line.mid(pos2+1).stripWhiteSpace().latin1()));
	    }
      }

      f.close();
    }

  int i = 0;
  while ( encs[i].encoding != 0 ) {
      _encodings.remove(encs[i].locale);
      _encodings.insert(encs[i].locale, encs[i].encoding);
      _initialGroup.insert(encs[i].locale, &encs[i].initialGroup);
      i++;
  }
}


QStringList KeyRules::rules(QString path)
{
  QStringList result;

  if (path.isEmpty())
    path = X11_DIR + "xkb/rules";
  
  QDir dir(path);
  dir.setFilter(QDir::Files);
  QStringList list = dir.entryList();
  for (QStringList::Iterator it = list.begin(); it != list.end(); ++it)
    if ((*it).right(4) != ".lst")
      result << *it;
  
  return result;
}


/* pretty simple algorithm - reads the layout file and 
    tries to find "xkb_symbols"
    also checks whether previous line contains "hidden" to skip it
*/
QStringList
KeyRules::getVariants(const QString& layout)
{
    if( layout.isEmpty() || !layouts().find(layout) )
	return QStringList();

  QStringList* result1 = _varLists[layout];
  if( result1 )
    return *result1;
	
  QStringList* result = new QStringList();
	
  QString file = X11_DIR + "xkb/symbols/" + layout;
  QFile f(file);
  if (f.open(IO_ReadOnly))
    {
      QTextStream ts(&f);

      QString line;
      QString prev_line;
      
      while (!ts.eof()) {
    	  prev_line = line;
	  line = ts.readLine().simplifyWhiteSpace();

	    if (line[0] == '#' || line.left(2) == "//" || line.isEmpty())
		continue;

	    int pos = line.find("xkb_symbols");
	    if (pos < 0)
		continue;
		
	    if( prev_line.find("hidden") >=0 )
		continue;
		
	    pos = line.find('"', pos) + 1;
	    int pos2 = line.find('"', pos);
	    if( pos < 0 || pos2 < 0 )
		continue;
		    
	    result->append(line.mid(pos, pos2-pos));
      }

      f.close();
    }

    _varLists.insert(layout, result);

    return *result;
}

void KeyRules::parseVariants(const QStringList& vars, QDict<char>& variants) 
{
  static const char* LAYOUT_PATTERN = "[a-z0-9_]*";
  static const char* VARIANT_PATTERN = "\\([a-z0-9_]*\\)";
  for (QStringList::ConstIterator it = vars.begin(); it != vars.end(); ++it)
  {
      QString varLine = (*it).stripWhiteSpace();
      QRegExp rx(LAYOUT_PATTERN);
      int pos = rx.search(varLine, 0);
      int len = rx.matchedLength();
  // check for errors
      if( pos < 0 || len < 2 )
        continue;
      QString layout = varLine.mid(pos, len);
      rx.setPattern(VARIANT_PATTERN);
      pos = rx.search(varLine, pos+len);
      len = rx.matchedLength();
  // check for errors
      if( pos < 2 || len < 3 )
        continue;
      QString variant_ = varLine.mid(pos+1, len-2);


      QStringList addVars = getVariants(layout);
      if( !variant_.isEmpty() && addVars.contains(variant_) )
      {
        variants.insert(layout, strdup(variant_.latin1()));
      }
  }
}
