/* wckey.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2006,2009,2011 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "wckey.hh"
#include "wcdoubleshortkey.hh"
#include "simplelist.hh"
 
WCKey::WCKey()
{
  dkeys = new List();
}

WCKey::~WCKey()
{
  removeAllKeys();
  delete dkeys;
}

List *WCKey::getDoubleKeys()
{
  return dkeys;
}

void WCKey::setDoubleKeys( List *dklist )
{
  int id;
  WCDoubleShortkey *dk;

  removeAllKeys();
  
  if ( dklist == NULL ) return;  // check after removeAll is not a bug but a feature
  
  id = dklist->initEnum();
  dk = (WCDoubleShortkey*)dklist->getFirstElement( id );
  while ( dk != NULL ) {
    dkeys->addElement( dk->duplicate() );
    dk = (WCDoubleShortkey*)dklist->getNextElement( id );
  }
  dklist->closeEnum( id );
}

bool WCKey::hasKey( KeySym k, unsigned int m )
{
  int id;
  WCDoubleShortkey *dk;
  bool found = false;

  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    if ( dk->getType() == WCDoubleShortkey::WCDS_NORMAL ) {
      if ( dk->isShortkey( k, m ) == true ) {
        found = true;
        break;
      }
    }
    dk = (WCDoubleShortkey*)dkeys->getNextElement( id );
  }
  dkeys->closeEnum( id );
  return found;
}

bool WCKey::hasKey( KeySym k1, unsigned int m1, KeySym k2, unsigned int m2 )
{
  int id;
  WCDoubleShortkey *dk;
  bool found = false;

  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
      if ( dk->isShortkey( k1, m1, k2, m2 ) == true ) {
        found = true;
        break;
      }
    }
    dk = (WCDoubleShortkey*)dkeys->getNextElement( id );
  }
  dkeys->closeEnum( id );
  return found;
}

/*
 * removes all matching single keys
 * and all double keys which begin with given key
 */
void WCKey::removeKey( KeySym k, unsigned int m )
{
  int id;
  WCDoubleShortkey *dk;
  int pos = 0;

  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    if ( dk->getType() == WCDoubleShortkey::WCDS_NORMAL ) {
      if ( dk->isShortkey( k, m ) == true ) {
        dkeys->removeElementAt( pos );
        pos--;
        delete dk;
      }
    } else if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
      if ( ( dk->getKeySym( 0 ) == k ) &&
           ( dk->getMod( 0 ) == m ) ) {
        dkeys->removeElementAt( pos );
        pos--;
        delete dk;
      }
    }
    dk = (WCDoubleShortkey*)dkeys->getElementAt( id, ++pos );
  }
  dkeys->closeEnum( id );
}

/*
 * removes all matching double keys
 * and all single keys matching the k1/m1
 */
void WCKey::removeKey( KeySym k1, unsigned int m1, KeySym k2, unsigned int m2 )
{
  int id;
  WCDoubleShortkey *dk;
  int pos = 0;

  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    if ( dk->getType() == WCDoubleShortkey::WCDS_NORMAL ) {
      if ( dk->isShortkey( k1, m1 ) == true ) {
        dkeys->removeElementAt( pos );
        pos--;
        delete dk;
      }
    } else if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
      if ( dk->isShortkey( k1, m1, k2, m2 ) == true ) {
        dkeys->removeElementAt( pos );
        pos--;
        delete dk;
      }
    }
    dk = (WCDoubleShortkey*)dkeys->getElementAt( id, ++pos );
  }
  dkeys->closeEnum( id );
}

void WCKey::removeKey( WCDoubleShortkey *dk )
{
  if ( dk == NULL ) return;
  if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
    removeKey( dk->getKeySym( 0 ), dk->getMod( 0 ), dk->getKeySym( 1 ), dk->getMod( 1 ) );
  } else {
    removeKey( dk->getKeySym( 0 ), dk->getMod( 0 ) );
  }
}

void WCKey::removeAllKeys()
{
  int id;
  WCDoubleShortkey *dk;
  
  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    delete dk;
    dkeys->removeFirstElement();
    dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  }
  dkeys->closeEnum( id );
}

bool WCKey::conflictKey( KeySym k, unsigned int m )
{
  int id;
  WCDoubleShortkey *dk;
  bool found = false;

  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    if ( dk->getType() == WCDoubleShortkey::WCDS_NORMAL ) {
      if ( dk->isShortkey( k, m ) == true ) {
        found = true;
        break;
      }
    } else if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
      if ( ( dk->getKeySym( 0 ) == k ) &&
           ( dk->getMod( 0 ) == m ) ) {
        found = true;
        break;
      }
    }
    dk = (WCDoubleShortkey*)dkeys->getNextElement( id );
  }
  dkeys->closeEnum( id );
  
  return found;
}

bool WCKey::conflictKey( KeySym k1, unsigned int m1, KeySym k2, unsigned int m2 )
{
  int id;
  WCDoubleShortkey *dk;
  bool found = false;

  id = dkeys->initEnum();
  dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
  while ( dk != NULL ) {
    if ( dk->getType() == WCDoubleShortkey::WCDS_NORMAL ) {
      if ( dk->isShortkey( k1, m1 ) == true ) {
        found = true;
        break;
      }
    } else if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
      if ( dk->isShortkey( k1, m1, k2, m2 ) == true ) {
        found = true;
        break;
      }
    }
    dk = (WCDoubleShortkey*)dkeys->getNextElement( id );
  }
  dkeys->closeEnum( id );
  
  return found;
}

bool WCKey::conflictKey( WCDoubleShortkey *dk )
{
  if ( dk == NULL ) return false;
  if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE ) {
    return conflictKey( dk->getKeySym( 0 ), dk->getMod( 0 ), dk->getKeySym( 1 ), dk->getMod( 1 ) );
  } else {
    return conflictKey( dk->getKeySym( 0 ), dk->getMod( 0 ) );
  }
}

void WCKey::addKey( const KeySym k, const unsigned int m )
{
  WCDoubleShortkey *dk;
  
  dk = new WCDoubleShortkey();
  dk->setKeySym( k, 0 );
  dk->setMod( m, 0 );
  dk->setType( WCDoubleShortkey::WCDS_NORMAL );
  dkeys->addElement( dk );
}

void WCKey::addKey( const KeySym k1, const unsigned int m1, const KeySym k2, const unsigned int m2 )
{
  WCDoubleShortkey *dk;
  
  dk = new WCDoubleShortkey();
  dk->setKeySym( k1, 0 );
  dk->setMod( m1, 0 );
  dk->setKeySym( k2, 1 );
  dk->setMod( m2, 1 );
  dk->setType( WCDoubleShortkey::WCDS_DOUBLE );
  dkeys->addElement( dk );
}

void WCKey::addKey( const WCDoubleShortkey *dk )
{
  if ( dk == NULL ) return;
  dkeys->addElement( dk->duplicate() );
}

bool WCKey::isKeyChainPrefix( KeySym k, unsigned int m )
{
    int id;
    WCDoubleShortkey *dk;
    bool found = false;
    
    id = dkeys->initEnum();
    dk = (WCDoubleShortkey*)dkeys->getFirstElement( id );
    while ( dk != NULL ) {
        if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE &&
             dk->getKeySym( 0 ) == k &&
             dk->getMod( 0 ) == m ) {
            found = true;
            break;
        }
        dk = (WCDoubleShortkey*)dkeys->getNextElement( id );
    }
    dkeys->closeEnum( id );
    return found;
}
