/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
/*
 * Created on 22-Nov-2004
 *
 */
package uk.ac.ic.doc.kevlar.gui.internal.keyboard;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import uk.ac.ic.doc.kevlar.gui.internal.keyboard.actions.IAction;

/**
 * @author Marc
 **/
public class KeyBindings {

/*    public static final Item DEFAULT = new Item(-1);
    
    public static final Item NUMERIC = new Item(-3);
    public static final Item ALPHABETIC = new Item(-2);
    
    public static final Item BACKSPACE = new Item(8);
    public static final Item TAB = new Item(9);
    public static final Item ENTER = new Item(13);
    public static final Item ESCAPE = new Item(27);
    public static final Item SPACE = new Item(32);

    public static final Item A = new Item(97);
    public static final Item B = new Item(98);
    public static final Item C = new Item(99);
    public static final Item D = new Item(100);
    public static final Item E = new Item(101);
    public static final Item F = new Item(102);
    public static final Item G = new Item(103);
    public static final Item H = new Item(104);
    public static final Item I = new Item(105);
    public static final Item J = new Item(106);
    public static final Item K = new Item(107);
    public static final Item L = new Item(108);
    public static final Item M = new Item(109);
    public static final Item N = new Item(110);
    public static final Item O = new Item(111);
    public static final Item P = new Item(112);
    public static final Item Q = new Item(113);
    public static final Item R = new Item(114);
    public static final Item S = new Item(115);
    public static final Item T = new Item(116);
    public static final Item U = new Item(117);
    public static final Item V = new Item(118);
    public static final Item W = new Item(119);
    public static final Item X = new Item(120);
    public static final Item Y = new Item(121);
    public static final Item Z = new Item(122);

    public static final Item DELETE = new Item(127);
    
    public static final Item LEFT = new Item(16777219);
    public static final Item RIGHT = new Item(16777220);
    
    public static final Item NO_MODIFIER = new Item(0);
    public static final Item CTRL = new Item(1);
    public static final Item ALT = new Item(2);
    public static final Item SHIFT = new Item(3);
    public static final Item CTRL_ALT = new Item(4); */
    
    private static final Map<String, Item> codes = new HashMap<String, Item>();
    
    private static Map<Item, Map<Item, Map<Item, Map<Item, IAction>>>> bindings = new HashMap<Item, Map<Item, Map<Item, Map<Item, IAction>>>>();

    public static void configureCodes() {
        
        codes.put("PRESSED", new Item(-5));
        codes.put("RELEASED", new Item(-6));
        
        codes.put("DEFAULT", new Item(-1));
        
        codes.put("SYMBOLIC", new Item(-4));
        codes.put("NUMERIC", new Item(-3));
        codes.put("ALPHABETIC", new Item(-2));
        
        codes.put("BACKSPACE", new Item(8));
        codes.put("TAB", new Item(9));
        codes.put("ENTER", new Item(13));
        codes.put("ESCAPE", new Item(27));
        codes.put("SPACE", new Item(32));

        codes.put("<", new Item(44));
        codes.put(">", new Item(46));
        
        codes.put("A", new Item(97));
        codes.put("B", new Item(98));
        codes.put("C", new Item(99));
        codes.put("D", new Item(100));
        codes.put("E", new Item(101));
        codes.put("F", new Item(102));
        codes.put("G", new Item(103));
        codes.put("H", new Item(104));
        codes.put("I", new Item(105));
        codes.put("J", new Item(106));
        codes.put("K", new Item(107));
        codes.put("L", new Item(108));
        codes.put("M", new Item(109));
        codes.put("N", new Item(110));
        codes.put("O", new Item(111));
        codes.put("P", new Item(112));
        codes.put("Q", new Item(113));
        codes.put("R", new Item(114));
        codes.put("S", new Item(115));
        codes.put("T", new Item(116));
        codes.put("U", new Item(117));
        codes.put("V", new Item(118));
        codes.put("W", new Item(119));
        codes.put("X", new Item(120));
        codes.put("Y", new Item(121));
        codes.put("Z", new Item(122));

        codes.put("DELETE", new Item(127));

        codes.put("UP", new Item(16777217));
        codes.put("DOWN", new Item(16777218));
        codes.put("LEFT", new Item(16777219));
        codes.put("RIGHT", new Item(16777220));

        codes.put("HOME", new Item(16777223));
        codes.put("END", new Item(16777224));
        
        codes.put("CTRL_KEY", new Item(262144));
        codes.put("ALT_KEY", new Item(65536));
        codes.put("SHIFT_KEY", new Item(131072));
        
        codes.put("NO_MODIFIER", new Item(-7));
        codes.put("CTRL", new Item(-8));
        codes.put("ALT", new Item(-9));
        codes.put("SHIFT", new Item(-10));
        codes.put("CTRL_ALT", new Item(-11));
        codes.put("CTRL_SHIFT", new Item(-12));
        codes.put("ALT_SHIFT", new Item(-13));
        codes.put("CTRL_ALT_SHIFT", new Item(-14));
        
        codes.put("WITH_CTRL", new Group(codes.get("CTRL"), codes.get("CTRL_ALT"), codes.get("CTRL_SHIFT"), codes.get("CTRL_ALT_SHIFT")));
//        codes.put("WITH_ALT", new Group(codes.get("ALT"), codes.get("CTRL_ALT"), codes.get("ALT_SHIFT"), codes.get("CTRL_ALT_SHIFT")));
        codes.put("WITH_SHIFT", new Group(codes.get("SHIFT"), codes.get("CTRL_SHIFT"), codes.get("ALT_SHIFT"), codes.get("CTRL_ALT_SHIFT")));

    }
    
    public static Item code(String name) {
        
        return codes.get(name);
        
    }
    
    public static String name(Item code) {
        
        String result = "";
        
        if (code != null) {
        
            if (code instanceof Group) {
            
                Item[] items = ((Group)code).getItems();
            
                for (int i=0; i<items.length; i++) {
                
                    if (i != 0) {
                    
                        result += " ";
                    
                    }
                
                    result += name(items[i]);
                
                }
            
            } else {

                ArrayList<String> keys = new ArrayList<String>(codes.keySet());
                int i = 0;
            
                while (result.equals("") && i<keys.size()) {
                
                    Item item = codes.get(keys.get(i));
                
                    if (item.equals(code)) {
                    
                        result = keys.get(i);
                    
                    }
                
                    i++;
                
                }
            
                if (result.equals("")) {
                
                    result = Integer.toString(code.intValue());
                
                }
            
            }
            
        } else {
            
            result = "null";
            
        }
        
        return result;
        
    }
    
    public static void addBinding(Item events, Item states, Item modifiers, Item keys, IAction action) {
        
        Item[] e;
        
        if (events instanceof Group) {
            
            e = ((Group)events).getItems();
            
        } else {
            
            e = new Item[] {events};
            
        }
        
        Item[] s;

        if (states instanceof Group) {
            
            s = ((Group)states).getItems();
            
        } else {
            
            s = new Item[] {states};
            
        }
        
        Item[] m;
        
        if (modifiers instanceof Group) {
            
            m = ((Group)modifiers).getItems();
            
        } else {
            
            m = new Item[] {modifiers};
            
        }
        
        Item[] k;
        
        if (keys instanceof Group) {
            
            k = ((Group)keys).getItems();
            
        } else {
            
            k = new Item[] {keys};
            
        }
        
        for (Item event: e) {
        
            for (Item state : s) {
                
                for (Item modifier : m) {
                
                    for (Item key : k) {
                    
                        addItemBinding(event, state, modifier, key, action);
                        
                    }
                    
                }
                
            }
            
        }
        
    }
    
    public static void addItemBinding(Item event, Item stateID, Item modifier, Item key, IAction action) {
        
//        System.err.println("Adding binding "+name(event)+" "+name(stateID)+" "+name(modifier)+" "+name(key)+" "+action.getClass().getName());
        
        Map<Item, Map<Item, Map<Item, IAction>>> stateMap = bindings.get(event);
        
        if (stateMap == null) {
            
            stateMap = new HashMap<Item, Map<Item, Map<Item, IAction>>>();
            bindings.put(event, stateMap);
            
        }
        
        Map<Item, Map<Item, IAction>> modifierMap = stateMap.get(stateID);
        
        if (modifierMap == null) {
            
            modifierMap = new HashMap<Item, Map<Item, IAction>>();
            stateMap.put(stateID, modifierMap);
            
        }
        
        Map<Item, IAction> keyMap = modifierMap.get(modifier);
        
        if (keyMap == null) {
            
            keyMap = new HashMap<Item, IAction>();
            modifierMap.put(modifier, keyMap);
            
        }
        
        keyMap.put(key, action);
        
    }
    
    public static void addBinding(Item stateID, Item modifier, Item key, IAction action) {

        addBinding(code("PRESSED"), stateID, modifier, key, action);
        
    }
    
    public static IAction getBinding(Item event, Item stateID, Item modifier, Item key) {
        
        Map<Item, Map<Item, Map<Item, IAction>>> stateMap = bindings.get(event);
        
        if (stateMap == null) {
            
            return null;
            
        }
        
        IAction action = getNormalStateBinding(stateMap, stateID, modifier, key);
        
        if (action == null) {
            
            return getDefaultStateBinding(stateMap, modifier, key);
            
        }
        
        return action;        
                
    }
    
    private static IAction getNormalStateBinding(Map<Item, Map<Item, Map<Item, IAction>>> stateMap, Item stateID, Item modifier, Item key) {
     
        Map<Item, Map<Item, IAction>> modifierMap = stateMap.get(stateID);
        
        if (modifierMap == null) {
            
            return null;
            
        }
        
        return getModifierBinding(modifierMap, modifier, key);
        
    }
    
    private static IAction getDefaultStateBinding(Map<Item, Map<Item, Map<Item, IAction>>> stateMap, Item modifier, Item key) {
        
        Map<Item, Map<Item, IAction>> modifierMap = stateMap.get(code("DEFAULT"));
        
        if (modifierMap == null) {
            
            return null;
            
        }

        return getModifierBinding(modifierMap, modifier, key);
        
    }
    
    private static IAction getModifierBinding(Map<Item, Map<Item, IAction>> modifierMap, Item modifier, Item key) {
        
        IAction action = getNormalModifierBinding(modifierMap, modifier, key);
        
        if (action == null) {
            
            return getDefaultModifierBinding(modifierMap, key);
            
        }
        
        return action;        
        
    }
    
    private static IAction getNormalModifierBinding(Map<Item, Map<Item, IAction>> modifierMap, Item modifier, Item key) {
        
        Map<Item, IAction> keyMap = modifierMap.get(modifier);
        
        if (keyMap == null) {
            
            return null;
            
        }
        
        return getKeyBinding(keyMap, key);
        
    }
    
    private static IAction getDefaultModifierBinding(Map<Item, Map<Item, IAction>> modifierMap, Item key) {
             
        Map<Item, IAction> keyMap = modifierMap.get(code("DEFAULT"));
        
        if (keyMap == null) {
            
            return null;
            
        } else {
            
            return getKeyBinding(keyMap, key);
            
        }
        
    }
    
    private static IAction getKeyBinding(Map<Item, IAction> keyMap, Item key) {
        
        IAction action = getNormalKeyBinding(keyMap, key);
        
        if (action == null) {
            
            return getDefaultKeyBinding(keyMap);
            
        }
        
        return action;
        
    }
    
    private static IAction getNormalKeyBinding(Map<Item, IAction> keyMap, Item key) {
        
        IAction action = keyMap.get(key);
        
        if (action == null) {
            
            if (isAlphabetic(key)) {
            
                action = keyMap.get(code("ALPHABETIC"));                
                return action;
                
            }
                        
            if (isNumeric(key)) {
                
                action = keyMap.get(code("NUMERIC"));
                return action;
                
            }
            
            if (isSymbolic(key)) {
                
                action = keyMap.get(code("SYMBOLIC"));
                return action;
                
            }
            
        }
        
        return action;
        
    }
    
    private static IAction getDefaultKeyBinding(Map<Item, IAction> keyMap) {
        
        return keyMap.get(code("DEFAULT"));            
        
    }
    
    public static boolean isAlphabetic(Item keyCode) {
        
        int key = keyCode.intValue();
        return (key >= 97) && (key <= 122);
        
    }
    
    public static boolean isNumeric(Item keyCode) {
        
        int key = keyCode.intValue();
        return (key >= 48) && (key <= 57);
        
    }
    
    public static boolean isSymbolic(Item keyCode) {
        
        int key = keyCode.intValue();
        return ((key >= 33) && (key <= 47)) || ((key >= 58) && (key <= 64)) || ((key >= 91) && (key <= 96)) || ((key >= 123) && (key <= 126));
        
    }
    
    public static boolean isDisplayable(Item keyCode) {
        
        int key = keyCode.intValue();
        return ((key >= 32) && (key <= 126));
        
    }
    
}
