//
// AutoProperties.cc for pekwm
// Copyright © 2003-2009 Claes Nästén <me@pekdon.net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include <algorithm>
#include <vector>
#include <iostream>

#include "AutoProperties.hh"
#include "Config.hh"
#include "Util.hh"

using std::cerr;
using std::endl;
using std::find;
using std::list;
using std::map;
using std::string;
using std::vector;
using std::strtol;

AutoProperties *AutoProperties::_instance = 0;

//! @brief Constructor for AutoProperties class
AutoProperties::AutoProperties(void)
    : _extended(false), _harbour_sort(false),
      _apply_on_start(true)
{
#ifdef DEBUG
    if (_instance) {
        cerr << __FILE__ << "@" << __LINE__ << ": "
             << "AutoProperties(" << this << ")::AutoProperties()" << endl
             << " *** _instance already set: " << _instance << endl;
    }
#endif // DEBUG
    _instance = this;

    // fill parsing maps
    _apply_on_map[""] = APPLY_ON_NONE;
    _apply_on_map["START"] = APPLY_ON_START;
    _apply_on_map["NEW"] = APPLY_ON_NEW;
    _apply_on_map["RELOAD"] = APPLY_ON_RELOAD;
    _apply_on_map["WORKSPACE"] = APPLY_ON_WORKSPACE;
    _apply_on_map["TRANSIENT"] = APPLY_ON_TRANSIENT;
    _apply_on_map["TRANSIENTONLY"] = APPLY_ON_TRANSIENT_ONLY;

    // global properties
    _property_map[""] = AP_NO_PROPERTY;
    _property_map["WORKSPACE"] = AP_WORKSPACE;
    _property_map["PROPERTY"] = AP_PROPERTY;
    _property_map["STICKY"] = AP_STICKY;
    _property_map["SHADED"] = AP_SHADED;
    _property_map["MAXIMIZEDVERTICAL"] = AP_MAXIMIZED_VERTICAL;
    _property_map["MAXIMIZEDHORIZONTAL"] = AP_MAXIMIZED_HORIZONTAL;
    _property_map["ICONIFIED"] = AP_ICONIFIED;
    _property_map["BORDER"] = AP_BORDER;
    _property_map["TITLEBAR"] = AP_TITLEBAR;
    _property_map["FRAMEGEOMETRY"] = AP_FRAME_GEOMETRY;
    _property_map["CLIENTGEOMETRY"] = AP_CLIENT_GEOMETRY;
    _property_map["LAYER"] = AP_LAYER;
    _property_map["SKIP"] = AP_SKIP;
    _property_map["FULLSCREEN"] = AP_FULLSCREEN;
    _property_map["PLACENEW"] = AP_PLACE_NEW;
    _property_map["FOCUSNEW"] = AP_FOCUS_NEW;
    _property_map["FOCUSABLE"] = AP_FOCUSABLE;
    _property_map["CFGDENY"] = AP_CFG_DENY;
    _property_map["ALLOWEDACTIONS"] = AP_ALLOWED_ACTIONS;
    _property_map["DISALLOWEDACTIONS"] = AP_DISALLOWED_ACTIONS;
#ifdef OPACITY
    _property_map["OPACITY"] = AP_OPACITY;
#endif // OPACITY

    // group properties
    _group_property_map[""] = AP_NO_PROPERTY;
    _group_property_map["SIZE"] = AP_GROUP_SIZE;
    _group_property_map["BEHIND"] = AP_GROUP_BEHIND;
    _group_property_map["FOCUSEDFIRST"] = AP_GROUP_FOCUSED_FIRST;
    _group_property_map["GLOBAL"] = AP_GROUP_GLOBAL;
    _group_property_map["RAISE"] = AP_GROUP_RAISE;

    // window type map
    _window_type_map[""] = WINDOW_TYPE;
    _window_type_map["DESKTOP"] = WINDOW_TYPE_DESKTOP;
    _window_type_map["DOCK"] = WINDOW_TYPE_DOCK;
    _window_type_map["TOOLBAR"] = WINDOW_TYPE_TOOLBAR;
    _window_type_map["MENU"] = WINDOW_TYPE_MENU;
    _window_type_map["UTILITY"] = WINDOW_TYPE_UTILITY;
    _window_type_map["SPLASH"] = WINDOW_TYPE_SPLASH;
    _window_type_map["DIALOG"] = WINDOW_TYPE_DIALOG;
    _window_type_map["NORMAL"] = WINDOW_TYPE_NORMAL;
}

//! @brief Destructor for AutoProperties class
AutoProperties::~AutoProperties(void)
{
    unload();
    _instance = 0;
}

//! @brief Loads the autoprop config file.
bool
AutoProperties::load(void)
{
    string cfg_file(Config::instance()->getAutoPropsFile());
    if (! Util::requireReload(_cfg_state, cfg_file)) {
        return false;
    }

    // dealloc memory
    unload();

    CfgParser a_cfg;
    if (! a_cfg.parse(cfg_file, CfgParserSource::SOURCE_FILE, false)) {
        cfg_file = SYSCONFDIR "/autoproperties";
        if (! a_cfg.parse (cfg_file, CfgParserSource::SOURCE_FILE, false)) {
          setDefaultTypeProperties();
          return false;
        }
    }

    // Setup template parsing if requested
    loadRequire(a_cfg, cfg_file);

    if (a_cfg.is_dynamic_content()) {
        _cfg_state.clear();
    } else {
        _cfg_state = a_cfg.get_file_list();
    }

    // reset values
    _apply_on_start = true;

    vector<string> tokens;
    vector<string>::iterator token_it;
    list<uint> workspaces;

    CfgParser::iterator it(a_cfg.get_entry_root()->begin());
    for (; it != a_cfg.get_entry_root()->end(); ++it) {
        if (*(*it) == "PROPERTY") {
            parseAutoProperty(*it, 0);
        } else if (*(*it) == "TITLERULES") {
            parseTitleProperty(*it);
        } else if (*(*it) == "DECORRULES") {
            parseDecorProperty(*it);
        } else if (*(*it) == "TYPERULES") {
            parseTypeProperty(*it);
        } else if (*(*it) == "HARBOUR") {
            parseDockAppProperty(*it);
        } else if (*(*it) == "WORKSPACE") { // Workspace section
            CfgParser::Entry *workspace = (*it)->get_section();
            CfgParser::Entry *value = workspace->find_entry("WORKSPACE");
            if (! value) {
                continue; // Need workspace numbers.
            }
            
            tokens.clear();
            if (Util::splitString(value->get_value(), tokens, " \t")) {
                workspaces.clear();
                for (token_it = tokens.begin(); token_it != tokens.end(); ++token_it)
                    workspaces.push_back(strtol(token_it->c_str(), 0, 10) - 1);

                // Get all properties on for these workspaces.
                CfgParser::iterator workspace_it(workspace->begin());
                for (; workspace_it != workspace->end(); ++workspace_it) {
                    parseAutoProperty(*workspace_it, &workspaces);
                }
            }
        }
    }

    // Validate date
    setDefaultTypeProperties();

    return true;
}

/**
 * Load autoproperties quirks.
 */
void
AutoProperties::loadRequire(CfgParser &a_cfg, std::string &file)
{
    CfgParser::Entry *section;

    // Look for requires section, 
    section = a_cfg.get_entry_root()->find_section("REQUIRE");
    if (section) {
        list<CfgParserKey*> key_list;

        key_list.push_back(new CfgParserKeyBool("TEMPLATES", _extended, false));
        section->parse_key_values(key_list.begin(), key_list.end());
        for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());

        // Re-load configuration with templates enabled.
        if (_extended) {
            a_cfg.clear(true);
            a_cfg.parse(file, CfgParserSource::SOURCE_FILE, true);
        }
    } else {
        _extended = false;
    }
}

//! @brief Frees allocated memory
void
AutoProperties::unload(void)
{
    list<Property*>::iterator it;

    // remove auto properties
    for (it = _prop_list.begin(); it != _prop_list.end(); ++it) {
        delete *it;
    }
    _prop_list.clear();

    // remove title properties
    for (it = _title_prop_list.begin(); it != _title_prop_list.end(); ++it) {
        delete *it;
    }
    _title_prop_list.clear();

    // remove decor properties
    for (it = _decor_prop_list.begin(); it != _decor_prop_list.end(); ++it) {
        delete *it;
    }
    _decor_prop_list.clear();

    // remove dock app properties
    for (it = _dock_app_prop_list.begin(); it != _dock_app_prop_list.end(); ++it) {
        delete *it;
    }
    _dock_app_prop_list.clear();

    // remove type properties
    map<AtomName, AutoProperty*>::iterator m_it(_window_type_prop_map.begin());
    for (; m_it != _window_type_prop_map.end(); ++m_it) {
        delete m_it->second;
    }
    _window_type_prop_map.clear();
}

//! @brief Finds a property from the prop_list
Property*
AutoProperties::findProperty(const ClassHint* class_hint,
                             std::list<Property*>* prop_list, int ws, uint type)
{
    // Allready remove apply on start
    if (! _apply_on_start && (type == APPLY_ON_START))
        return 0;

    list<Property*>::iterator it(prop_list->begin());
    list<uint>::iterator w_it;

    // start searching for a suitable property
    for (; it != prop_list->end(); ++it) {
        // see if the type matches, if we have one
        if ((type != 0) && ! (*it)->isApplyOn(type))
            continue;

        if (matchAutoClass(*class_hint, *it)) {

            // make sure it applies on the correct workspace
            if ((*it)->getWsList().size()) {
                w_it = find((*it)->getWsList().begin(), (*it)->getWsList().end(),
                            unsigned(ws));
                if (w_it != (*it)->getWsList().end()) {
                  return *it;
                }
            } else {
                return *it;
            }
        }
    }

    return 0;
}

/**
 * Parse regex_str and set on regex, outputting warning with name if
 * it fails.
 */
bool
AutoProperties::parseRegexpOrWarning(RegexString &regex, const std::string regex_str, const std::string &name)
{
    if (! regex_str.size() || regex.parse_match(Util::to_wide_str(regex_str))) {
        return true;
    } else {
        cerr << " *** WARNING: invalid regexp " << regex_str << " for autoproperty " << name << endl;
        return false;
    }
}

//! @brief Parses a property match rule
//! @param str String to parse.
//! @param prop Property to place result in.
//! @param extended Extended syntax including role and title in the name, defaults to true.
//! @return true on success, else false.
bool
AutoProperties::parsePropertyMatch(const std::string &str, Property *prop, bool extended)
{
    bool status = false;

    // Format of property matches are regexp,regexp . Split up in class
    // and role regexps.
    vector<string> tokens;
    Util::splitString(str, tokens, ",", extended ? 5 : 2, true);

    if (tokens.size() >= 2) {
        // Make sure one of the two regexps compiles
        status = parseRegexpOrWarning(prop->getHintName(), tokens[0], "name");
        status = status && parseRegexpOrWarning(prop->getHintClass(), tokens[1], "class");
    }

    // Parse extended part of regexp, role, title and apply on
    if (status && extended) {
        if (status && tokens.size() > 2) {
            status = parseRegexpOrWarning(prop->getRole(), tokens[2], "role");
        }
        if (status && tokens.size() > 3) {
            status = parseRegexpOrWarning(prop->getTitle(), tokens[3], "title");
        }
        if (status && tokens.size() > 4) {
            parsePropertyApplyOn(tokens[4], prop);
        }
    }

    return status;
}

//! @brief Parses a cs and sets up a basic property
bool
AutoProperties::parseProperty(CfgParser::Entry *section, Property *prop)
{
    CfgParser::Entry *value;

    // Get extra matching info.
    value = section->find_entry ("TITLE");
    if (value) {
        parseRegexpOrWarning(prop->getTitle(), value->get_value(), "title");
    }
    value = section->find_entry ("ROLE");
    if (value) {
        parseRegexpOrWarning(prop->getRole(), value->get_value(), "role");
    }

    // Parse apply on mask.
    value = section->find_entry ("APPLYON");
    if (value) {
        parsePropertyApplyOn(value->get_value(), prop);
    }

    return true;
}

/**
 * Parse property apply on.
 */
void
AutoProperties::parsePropertyApplyOn(const std::string &apply_on, Property *prop)
{
    vector<string> tokens;
    if ((Util::splitString(apply_on, tokens, " \t", 5))) {
        vector<string>::iterator it(tokens.begin());
        for (; it != tokens.end(); ++it) {
            prop->applyAdd(static_cast<unsigned int>(ParseUtil::getValue<ApplyOn>(*it, _apply_on_map)));
        }
    }
}

//! @brief Parses AutopProperty
void
AutoProperties::parseAutoProperty(CfgParser::Entry *section, std::list<uint>* ws)
{
    // Get sub section
    section = section->get_section();
    if (! section) {
        return;
    }

    AutoProperty* property = new AutoProperty();
    parsePropertyMatch(section->get_value(), property, _extended);

    if (parseProperty(section, property)) {
        parseAutoPropertyValue(section, property, ws);
        _prop_list.push_back(property);

    } else {
        delete property;
    }
}

//! @brief Parses a Group section of the AutoProps
void
AutoProperties::parseAutoGroup(CfgParser::Entry *section, AutoProperty* property)
{
    if (! section) {
        return;
    }

    if (section->get_value().size()) {
        property->group_name = Util::to_wide_str(section->get_value());
    }
    
    PropertyType property_type;

    CfgParser::iterator it(section->begin());
    for (; it != section->end(); ++it) {
        property_type = ParseUtil::getValue<PropertyType>((*it)->get_name(), _group_property_map);

        switch (property_type) {
        case AP_GROUP_SIZE:
            property->group_size = strtol((*it)->get_value().c_str(), 0, 10);
            break;
        case AP_GROUP_BEHIND:
            property->group_behind = Util::isTrue((*it)->get_value());
            break;
        case AP_GROUP_FOCUSED_FIRST:
            property->group_focused_first = Util::isTrue((*it)->get_value());
            break;
        case AP_GROUP_GLOBAL:
            property->group_global = Util::isTrue((*it)->get_value());
            break;
        case AP_GROUP_RAISE:
            property->group_raise = Util::isTrue((*it)->get_value());
            break;
        default:
            // do nothing
            break;
        }
    }
}

/**
 * Parses a title property section.
 */
void
AutoProperties::parseTitleProperty(CfgParser::Entry *section)
{
    section = section->get_section();

    TitleProperty *title_property;
    CfgParser::Entry *title_section;

    CfgParser::iterator it(section->begin());
    for (; it != section->end(); ++it) {
        title_section = (*it)->get_section();
        if (! title_section) {
            continue;
        }

        title_property = new TitleProperty();
        parsePropertyMatch(title_section->get_value(), title_property, _extended);
        if (parseProperty(title_section, title_property)) {
            CfgParser::Entry *value = title_section->find_entry("RULE");
            if (value && title_property->getTitleRule().parse_ed_s(Util::to_wide_str(value->get_value()))) {
                _title_prop_list.push_back(title_property);
                title_property = 0;
            }
        }

        if (title_property) {
            delete title_property;
        }
    }
}

/**
 * Parse decor property sections.
 */
void
AutoProperties::parseDecorProperty(CfgParser::Entry *section)
{
    section = section->get_section();

    DecorProperty *decor_property;
    CfgParser::Entry *decor_section;

    CfgParser::iterator it(section->begin());
    for (; it != section->end(); ++it) {
        decor_section = (*it)->get_section ();
        if (! decor_section) {
            continue;
        }

        decor_property = new DecorProperty();
        parsePropertyMatch(decor_section->get_value (), decor_property, _extended);
        if (parseProperty(decor_section, decor_property)) {
            CfgParser::Entry *value = decor_section->find_entry("DECOR");
            if (value) {
                decor_property->applyAdd(APPLY_ON_START);
                decor_property->setName(value->get_value());
                _decor_prop_list.push_back(decor_property);
                decor_property = 0;
            }
        }

        if (decor_property) {
            delete decor_property;
        }
    }
}

/**
 * Parse dock app properties.
 */
void
AutoProperties::parseDockAppProperty(CfgParser::Entry *section)
{
    section = section->get_section();

    // Reset harbour sort, set to true if POSITION property found.
    _harbour_sort = false;

    DockAppProperty *dock_property;
    CfgParser::Entry *dock_section;

    CfgParser::iterator it(section->begin());
    for (; it != section->end(); ++it) {
        dock_section = (*it)->get_section();
        if (! dock_section) {
            continue;
        }

        dock_property = new DockAppProperty();
        parsePropertyMatch(dock_section->get_value(), dock_property, _extended);
        if (parseProperty(dock_section, dock_property)) {
            CfgParser::Entry *value = dock_section->find_entry("POSITION");
            if (value) {
                _harbour_sort = true;

                int position = strtol(value->get_value().c_str(), 0, 10);
                dock_property->setPosition(position);
                _decor_prop_list.push_back(dock_property);
                dock_property = 0;
            }
        }

        if (dock_property) {
            delete dock_property;
        }
    }
}

//! @brief Parse type auto properties.
//! @param section Section containing properties.
void
AutoProperties::parseTypeProperty(CfgParser::Entry *section)
{
    // Get sub section
    section = section->get_section();

    AtomName atom;
    AutoProperty *type_property;
    CfgParser::Entry *type_section;
    map<AtomName, AutoProperty*>::iterator atom_it;

    // Look for all type properties
    CfgParser::iterator it(section->begin());
    for (; it != section->end(); ++it) {
        type_section = (*it)->get_section();
        if (! type_section) {
            continue;
        }

        // Create new property and try to parse
        type_property = new AutoProperty();
        atom = ParseUtil::getValue<AtomName>(type_section->get_value(), _window_type_map);
        if (atom == WINDOW_TYPE) {
            cerr << " *** WARNING: unknown type " << type_section->get_value() << " for autoproperty." << endl;
        }

        if (atom != WINDOW_TYPE && parseProperty(type_section, type_property)) {
            // Parse of match ok, parse values
            parseAutoPropertyValue(type_section, type_property, 0);
      
            // Add to list, make sure it does not exist already
            atom_it = _window_type_prop_map.find(atom);
            if (atom_it != _window_type_prop_map.end()) {
                cerr << " *** WARNING: multiple type autoproperties for type "
                     << type_section->get_value() << endl;
                delete atom_it->second;
            }
            _window_type_prop_map[atom] = type_property;
        } else {
            delete type_property;
        }
    }
}

//! @brief Set default values for type auto properties not in configuration.
void
AutoProperties::setDefaultTypeProperties(void)
{
    // DESKTOP
    if (! findWindowTypeProperty(WINDOW_TYPE_DESKTOP)) {
        AutoProperty *prop = new AutoProperty();
        prop->maskAdd(AP_CLIENT_GEOMETRY);
        prop->client_gm_mask =
                XParseGeometry("0x0+0+0",
                     &prop->client_gm.x, &prop->client_gm.y,
                     &prop->client_gm.width, &prop->client_gm.height);
        prop->maskAdd(AP_STICKY);
        prop->sticky = true;
        prop->maskAdd(AP_TITLEBAR);
        prop->titlebar = false;
        prop->maskAdd(AP_BORDER);
        prop->border = false;
        prop->maskAdd(AP_SKIP);
        prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP|SKIP_PAGER|SKIP_TASKBAR;
        prop->maskAdd(AP_LAYER);
        prop->layer = LAYER_DESKTOP;
        prop->maskAdd(AP_FOCUSABLE);
        prop->focusable = false;
        prop->maskAdd(AP_DISALLOWED_ACTIONS);
        prop->disallowed_actions = ACTION_ACCESS_MOVE|ACTION_ACCESS_RESIZE;

        _window_type_prop_map[WINDOW_TYPE_DESKTOP] = prop;
    }

    // DOCK
    if (! findWindowTypeProperty(WINDOW_TYPE_DOCK)) {
        AutoProperty *prop = new AutoProperty();
        prop->maskAdd(AP_STICKY);
        prop->sticky = true;
        prop->maskAdd(AP_TITLEBAR);
        prop->titlebar = false;
        prop->maskAdd(AP_BORDER);
        prop->border = false;
        prop->maskAdd(AP_SKIP);
        prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_PAGER|SKIP_TASKBAR;
        prop->maskAdd(AP_LAYER);
        prop->layer = LAYER_DOCK;
        prop->maskAdd(AP_FOCUSABLE);
        prop->focusable = false;
        prop->maskAdd(AP_DISALLOWED_ACTIONS);
        prop->disallowed_actions = ACTION_ACCESS_MOVE|ACTION_ACCESS_RESIZE;

        _window_type_prop_map[WINDOW_TYPE_DOCK] = prop;
    }

    // TOOLBAR
    if (! findWindowTypeProperty(WINDOW_TYPE_TOOLBAR)) {
        AutoProperty *prop = new AutoProperty();
        prop->maskAdd(AP_TITLEBAR);
        prop->titlebar = true;
        prop->maskAdd(AP_BORDER);
        prop->border = true;
        prop->maskAdd(AP_SKIP);
        prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_PAGER|SKIP_TASKBAR;

        _window_type_prop_map[WINDOW_TYPE_TOOLBAR] = prop;
    }

    // MENU
    if (! findWindowTypeProperty(WINDOW_TYPE_MENU)) {
        AutoProperty *prop = new AutoProperty();
        prop->maskAdd(AP_TITLEBAR);
        prop->titlebar = false;
        prop->maskAdd(AP_BORDER);
        prop->border = false;
        prop->maskAdd(AP_SKIP);
        prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP|SKIP_PAGER|SKIP_TASKBAR;

        _window_type_prop_map[WINDOW_TYPE_MENU] = prop;
    }

    // UTILITY
    if (! findWindowTypeProperty(WINDOW_TYPE_UTILITY)) {
        AutoProperty *prop = new AutoProperty();
        prop->maskAdd(AP_TITLEBAR);
        prop->titlebar = true;
        prop->maskAdd(AP_BORDER);
        prop->border = true;
        prop->maskAdd(AP_SKIP);
        prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP;

        _window_type_prop_map[WINDOW_TYPE_UTILITY] = prop;
    }

    // SPLASH
    if (! findWindowTypeProperty(WINDOW_TYPE_SPLASH)) {
        AutoProperty *prop = new AutoProperty();
        prop->maskAdd(AP_TITLEBAR);
        prop->titlebar = false;
        prop->maskAdd(AP_BORDER);
        prop->border = false;

        _window_type_prop_map[WINDOW_TYPE_SPLASH] = prop;
    }
}

//! @brief Parse AutoProperty value attributes.
//! @param section Config section to parse.
//! @param prop Property to store result in.
//! @param ws List of workspaces to apply property on.
void
AutoProperties::parseAutoPropertyValue(CfgParser::Entry *section, AutoProperty *prop, std::list<uint> *ws)
{
    // Copy workspaces, if any
    if (ws) {
        prop->getWsList().assign(ws->begin(), ws->end());
    }

    // See if we have a group section
    CfgParser::Entry *group_section(section->find_section ("GROUP"));
    if (group_section) {
        parseAutoGroup(group_section, prop);
    }

    // start parsing of values
    string name, value;
    vector<string> tokens;
    vector<string>::iterator token_it;
    PropertyType property_type;

    CfgParser::iterator it(section->begin());    
    for (; it != section->end(); ++it) {
        property_type = ParseUtil::getValue<PropertyType>((*it)->get_name(), _property_map);

        switch (property_type) {
        case AP_STICKY:
            prop->maskAdd(AP_STICKY);
            prop->sticky = Util::isTrue((*it)->get_value());
            break;
        case AP_SHADED:
            prop->maskAdd(AP_SHADED);
            prop->shaded = Util::isTrue((*it)->get_value());
            break;
        case AP_MAXIMIZED_VERTICAL:
            prop->maskAdd(AP_MAXIMIZED_VERTICAL);
            prop->maximized_vertical = Util::isTrue((*it)->get_value());
            break;
        case AP_MAXIMIZED_HORIZONTAL:
            prop->maskAdd(AP_MAXIMIZED_HORIZONTAL);
            prop->maximized_horizontal = Util::isTrue((*it)->get_value());
            break;
        case AP_ICONIFIED:
            prop->maskAdd(AP_ICONIFIED);
            prop->iconified = Util::isTrue((*it)->get_value());
            break;
        case AP_BORDER:
            prop->maskAdd(AP_BORDER);
            prop->border = Util::isTrue((*it)->get_value());
            break;
        case AP_TITLEBAR:
            prop->maskAdd(AP_TITLEBAR);
            prop->titlebar = Util::isTrue((*it)->get_value());
            break;
        case AP_FRAME_GEOMETRY:
            prop->maskAdd(AP_FRAME_GEOMETRY);
            prop->frame_gm_mask =
                XParseGeometry((char*) (*it)->get_value().c_str(),
                             &prop->frame_gm.x, &prop->frame_gm.y,
                             &prop->frame_gm.width, &prop->frame_gm.height);
            break;
        case AP_CLIENT_GEOMETRY:
            prop->maskAdd(AP_CLIENT_GEOMETRY);
            prop->client_gm_mask =
                XParseGeometry((char*) (*it)->get_value().c_str(),
                       &prop->client_gm.x, &prop->client_gm.y,
                       &prop->client_gm.width, &prop->client_gm.height);
            break;
        case AP_LAYER:            
            prop->layer = Config::instance()->getLayer((*it)->get_value());
            if (prop->layer != LAYER_NONE) {
                prop->maskAdd(AP_LAYER);
            }
            break;
        case AP_WORKSPACE:
            prop->maskAdd(AP_WORKSPACE);
            prop->workspace = unsigned(strtol((*it)->get_value().c_str(), 0, 10) - 1);
            break;
        case AP_SKIP:
            prop->maskAdd(AP_SKIP);
            tokens.clear();
            if ((Util::splitString((*it)->get_value(), tokens, " \t"))) {
                for (token_it = tokens.begin(); token_it != tokens.end(); ++token_it) {
                    prop->skip |= Config::instance()->getSkip(*token_it);
                }
            }
            break;
        case AP_FULLSCREEN:
            prop->maskAdd(AP_FULLSCREEN);
            prop->fullscreen = Util::isTrue((*it)->get_value());
            break;
        case AP_PLACE_NEW:
            prop->maskAdd(AP_PLACE_NEW);
            prop->place_new = Util::isTrue((*it)->get_value());
            break;
        case AP_FOCUS_NEW:
            prop->maskAdd(AP_FOCUS_NEW);
            prop->focus_new = Util::isTrue((*it)->get_value());
            break;
        case AP_FOCUSABLE:
            prop->maskAdd(AP_FOCUSABLE);
            prop->focusable = Util::isTrue((*it)->get_value());
            break;
        case AP_CFG_DENY:
            prop->maskAdd(AP_CFG_DENY);
            tokens.clear();
            if ((Util::splitString((*it)->get_value(), tokens, " \t"))) {
                for (token_it = tokens.begin(); token_it != tokens.end(); ++token_it) {
                    prop->cfg_deny |= Config::instance()->getCfgDeny(*token_it);
                }
            }
            break;
        case AP_ALLOWED_ACTIONS:
            prop->maskAdd(AP_ALLOWED_ACTIONS);
            Config::instance()->parseActionAccessMask((*it)->get_value(), prop->allowed_actions);
            break;
        case AP_DISALLOWED_ACTIONS:
            prop->maskAdd(AP_DISALLOWED_ACTIONS);
            Config::instance()->parseActionAccessMask((*it)->get_value(), prop->disallowed_actions);
            break;
#ifdef OPACITY
        case AP_OPACITY:
            prop->maskAdd(AP_OPACITY);
            Config::parseOpacity((*it)->get_value(), prop->focus_opacity, prop->unfocus_opacity);
            break;
#endif // OPACITY
        default:
            // do nothing
            break;
        }
    }
}

//! @brief Searches the _prop_list for a property
AutoProperty*
AutoProperties::findAutoProperty(const ClassHint* class_hint, int ws, uint type)
{
    return static_cast<AutoProperty*>(findProperty(class_hint, &_prop_list, ws, type));
}

//! @brief Searches the _title_prop_list for a property
TitleProperty*
AutoProperties::findTitleProperty(const ClassHint* class_hint)
{
    return static_cast<TitleProperty*>(findProperty(class_hint, &_title_prop_list, -1, 0));
}

//! @brief
DecorProperty*
AutoProperties::findDecorProperty(const ClassHint* class_hint)
{
    return static_cast<DecorProperty*>(findProperty(class_hint, &_decor_prop_list, -1, 0));
}

DockAppProperty*
AutoProperties::findDockAppProperty(const ClassHint *class_hint)
{
    return static_cast<DockAppProperty*>(findProperty(class_hint, &_dock_app_prop_list, -1, 0));
}

//! @brief Get AutoProperty for window of type type
//! @param atom Atom to get property for.
//! @return AutoProperty on success, else 0.
AutoProperty*
AutoProperties::findWindowTypeProperty(AtomName atom)
{
    AutoProperty *prop = 0;
    map<AtomName, AutoProperty*>::iterator it(_window_type_prop_map.find(atom));
    if (it != _window_type_prop_map.end()) {
        prop = it->second;
    }

    return prop;
}

//! @brief Removes all ApplyOnStart actions as they consume memory
void
AutoProperties::removeApplyOnStart(void)
{
    list<Property*>::iterator it(_prop_list.begin());
    for (; it != _prop_list.end(); ++it) {
        if ((*it)->isApplyOn(APPLY_ON_START)) {
            (*it)->applyRemove(APPLY_ON_START);
            if (! (*it)->getApplyOn()) {
                delete *it;
                it = _prop_list.erase(it);
                --it; // compensate for the ++ in the loop
            }
        }
    }

    _apply_on_start = false;
}

//! @brief Tries to match a class hint against an autoproperty data entry
bool
AutoProperties::matchAutoClass(const ClassHint &hint, Property *prop)
{
    bool ok = false;

    if ((prop->getHintName() == hint.h_name)
            && (prop->getHintClass() == hint.h_class))
    {
        ok = true;
        if (prop->getTitle ().is_match_ok ())  {
            ok = (prop->getTitle () == hint.title);
        }
        if (ok && prop->getRole ().is_match_ok ()) {
            ok = (prop->getRole () == hint.h_role);
        }
    }

    return ok;
}
