/*
 * Created on Aug 2, 2005
 *
 */

package de.renew.fa.model;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import de.renew.fa.FADrawing;
import de.renew.fa.util.FAHelper;
import de.renew.fa.util.LetterNameComparator;
import de.renew.fa.util.StateNameComparator;


/**
 * {@link de.renew.fa.model.FA}
 * @author cabac
 */
public class FAImpl implements FA {
    private HashMap<String, Letter> _alphabet = new HashMap<String, Letter>();
    private HashMap<String, Arc> _arcs = new HashMap<String, Arc>();
    private String _name = "";
    private HashMap<String, State> _states = new HashMap<String, State>();

    /**
     * Construct a new empty Finite Automata.
     */
    public FAImpl() {
        super();
    }

    /**
     * Explicit default constructor.
     *
     * @param drawing the drawing to be associated with this finite automata.
     */
    public FAImpl(FADrawing drawing) {}

    @Override
    public Vector<State> endStates() {
        Iterator<State> it = getStates();
        Vector<State> endStates = new Vector<State>();
        while (it.hasNext()) {
            State state = it.next();
            if (state.isEndState()) {
                endStates.add(state);
            }
        }
        return endStates;
    }

    @Override
    public Iterator<Letter> getAlphabet() {
        Vector<Letter> values = new Vector<Letter>(_alphabet.values());
        Collections.sort(values, new LetterNameComparator());
        return values.iterator();
    }

    @Override
    public Iterator<Arc> getArcs() {
        return _arcs.values().iterator();
    }

    @Override
    public String getName() {
        return _name;
    }

    @Override
    public State getStateByName(String name) {
        return _states.get(name);
    }

    @Override
    public Iterator<State> getStates() {
        Vector<State> stateList = new Vector<State>(_states.values());
        Collections.sort(stateList, new StateNameComparator());
        return stateList.iterator();
    }

    @Override
    public State[] getStatesAsArray() {
        return _states.values().toArray(new State[_states.size()]);
    }

    @Override
    public boolean hasArc(String state, String state2) {
        Iterator<Arc> it = getArcs();
        while (it.hasNext()) {
            Arc arc = it.next();
            if (arc.getFrom().getName().equals(state) && arc.getTo().getName().equals(state2)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean isLiteral(Word word) {
        return _alphabet.containsKey(word.getName());
    }

    @Override
    public Arc newArc(State from, Word inscription, State to) {
        if (from == null || inscription == null || to == null) {
            return null;
        }
        Arc newArc = new ArcImpl(from, inscription, to);
        _arcs.put(newArc.getName(), newArc);
        return newArc;
    }

    @Override
    public Letter newLetter(String letterName) {
        Letter letter = new LetterImpl(letterName);
        if (!_alphabet.containsKey(letterName)) {
            _alphabet.put(letterName, letter);
            return letter;
        }
        return null;
    }

    @Override
    public State newState(String name) {
        if (_states.containsKey(name)) {
            return null;
        }
        State state = new StateImpl(name);
        _states.put(name, state);
        return state;
    }

    @Override
    public Word newWord(String name) {
        return new WordImpl(name);
    }

    /**
     * @param sigma
     *            The alphabet to set.
     */
    public void setAlphabet(Iterator<Letter> sigma) {
        while (sigma.hasNext()) {
            Letter letter = sigma.next();
            this.newLetter(letter.getName());
        }
    }

    //    /**
    //     * @param delta
    //     *            The delta to set.
    //     */
    //    public void setArcs(Collection arcs) {
    //        Iterator it = arcs.iterator();
    //        while (it.hasNext()) {
    //            Arc arc = (Arc) it.next();
    //            this.newArc(arc.);
    //        }
    //    }

    @Override
    public boolean setAsEndState(String name) {
        if (_states.containsKey(name)) {
            getStateByName(name).setEndState(true);
            return true;
        }
        return false;
    }

    @Override
    public boolean setAsStartState(String name) {
        if (_states.containsKey(name)) {
            getStateByName(name).setStartState(true);
            return true;
        }
        return false;
    }

    //
    //    /**
    //     * Takes the model of this FA and transforms it to a
    //     * Renew <code>FADrawing</code>.
    //     * However, layout is not considered yet.
    //     * @return - the renew drawing of this FA.
    //     */
    //    public Drawing[] toDrawings() {
    //        Drawing[] drawings = new Drawing[1];
    //        drawings[0] = FAHelper.convertModelToDrawing(this);
    //        return drawings;
    //    }

    @Override
    public void setName(String name) {
        this._name = name;
    }

    /**
     * @param states
     *            The states to set.
     */
    public void setStates(Iterator<State> states) {
        while (states.hasNext()) {
            State state = states.next();
            this.newState(state.getName());
        }
    }

    @Override
    public Vector<State> startStates() {
        Iterator<State> it = getStates();
        Vector<State> startStates = new Vector<State>();
        while (it.hasNext()) {
            State state = it.next();
            if (state.isStartState()) {
                startStates.add(state);
            }
        }
        return startStates;
    }

    /**
     * Writes the FA in XFA format.
     */
    @Override
    public String toString() {
        return FAHelper.toProperties(this).toString();


        //        return "Z       = " + states.values().toString() + "\nSigma   = "
        //                + alphabet.values().toString() + "\nK       = "
        //                + arcs.values().toString() + "\nZ_Start = "
        //                + startStates().toString() + "\nZ_End   = "
        //                + endStates().toString();
    }

    @Override
    public int numberOfStates() {
        return _states.values().size();
    }

    @Override
    public int numberOfArcs() {
        return _arcs.size();
    }

    @Override
    public int numberOfLetters() {
        return _alphabet.size();
    }
}