//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package de.ugoe.cs.autoquest.commands.sequences;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import de.ugoe.cs.autoquest.CommandHelpers;
import de.ugoe.cs.autoquest.SequenceInstanceOf;
import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
import de.ugoe.cs.util.console.Command;
import de.ugoe.cs.util.console.GlobalDataContainer;

/**
 * <p>
 * This command iterates the provided sequences and corrects the order of events in case of tab
 * key navigation. This is required, as from time to time the event of pressing the tab key for
 * navigation in formulars comes before the text input event in a text input field out of which
 * the tab key navigates.
 * </p>
 * 
 * @author Patrick Harms
 * @version 1.0
 */
public class CMDcorrectTabKeyNavigationOrder implements Command {

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.util.console.Command#help()
     */
    @Override
    public String help() {
        return "correctTabKeyNavigationOrder <sequences> {<new sequences>}";
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
     */
    @SuppressWarnings("unchecked")
    @Override
    public void run(List<Object> parameters) {
        String sequencesName;
        String newSequencesName;
        try {
            sequencesName = (String) parameters.get(0);
            if (parameters.size() > 1) {
                newSequencesName = (String) parameters.get(1);
            }
            else {
                newSequencesName = sequencesName;
            }
        }
        catch (Exception e) {
            throw new IllegalArgumentException("must provide a sequences name");
        }

        Collection<List<Event>> sequences = null;
        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
        if (dataObject == null) {
            CommandHelpers.objectNotFoundMessage(sequencesName);
            return;
        }
        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
            return;
        }

        sequences = (Collection<List<Event>>) dataObject;

        Collection<List<Event>> newSequences = new LinkedList<List<Event>>();
        
        for (List<Event> sequence : sequences) {
            newSequences.add(correctTabKeyNavigationOrder(sequence));
        }

        if (GlobalDataContainer.getInstance().addData(newSequencesName, newSequences)) {
            CommandHelpers.dataOverwritten(newSequencesName);
        }
        
    }

    /**
     * <p>
     * convenience method that corrects the order of tab key navigation events, if required
     * </p>
     *
     * @param sequence the sequence, in which the tab key navigation events shall be corrected.
     * 
     * @return a new sequence with a corrected order of tab key navigation events
     */
    private List<Event> correctTabKeyNavigationOrder(List<Event> sequence) {
        List<Event> result = new LinkedList<Event>();
        
        int index = 0;
        while (index < sequence.size()) {
            if (mustCorrectTabKeyNavigationOrder(sequence, index)) {
                Event event1 = sequence.get(index);
                Event event2 = sequence.get(index + 1);
                
                // switch timestamps
                long timestamp1 = event1.getTimestamp();
                event1.setTimestamp(event2.getTimestamp());
                event2.setTimestamp(timestamp1);
                
                // change order of events
                result.add(event2);
                result.add(event1);
                
                index += 2;
            }
            else {
                result.add(sequence.get(index++));
            }
        }
        
        return result;
    }

    /**
     * <p>
     * convenience method that checks if at the given position in the sequence is a tab key event
     * followed by a text input event. If this is the case, both events have the wrong order and
     * this method returns true. Otherwise it returns false.
     * </p>
     *
     * @param sequence the sequence to check for the occurrence of a correctable tab key navigation
     * @param index    the index in the sequence to check
     * 
     * @return as described
     */
    private boolean mustCorrectTabKeyNavigationOrder(List<Event> sequence, int index) {
        boolean result = false;
        
        Event event1 = sequence.get(index);
        
        if ((index < (sequence.size() - 1)) &&
            ((event1.getType() instanceof KeyPressed) || (event1.getType() instanceof KeyTyped)))
        {
            VirtualKey key = ((KeyInteraction) event1.getType()).getKey();
            if (key == VirtualKey.TAB) {
                IEventTarget target1 = event1.getTarget();
                Event event2 = sequence.get(index + 1);
                
                if (((target1 instanceof ITextArea) || (target1 instanceof ITextField)) &&
                    (target1.equals(event2.getTarget())) &&
                    (event2.getType() instanceof TextInput))
                {
                    result = true;
                }
            }
        }
        
        return result;
    }

}
