package cnp.ew.converter;
import java.awt.*;
import java.util.*;

public class CpNumberToStringConverter extends CpFormattedToStringConverter
{

    static final int STATE_START=0;
    static final int STATE_INLITERAL=1;
    static final int STATE_INQUOTEDLITERAL=2;
    static final int STATE_NUM_OR_SCI_OR_FRAC=3;
    static final int STATE_NUM_OR_SCI_SEEN_DEC=4;
    static final int STATE_INDENOMINATOR=5;
    static final int STATE_INLITERALDENOMINATOR=6;
    static final int STATE_SCI_SEEN_E=7;
    static final int STATE_SCI_SEEN_SIGN=8;
    static final int STATE_SCI_SEEN_DEC=9;
    static final int STATE_QUOTEINQUOTEDLITERAL=10;
    static final int STATE_SEENSLASH=11;
    static final int STATE_SCI_SEEN_SIGN_AND_NUM=12;


    static final char EOF = (char)-1;

    // Is this using the general format?
    boolean isGeneral;

    Vector positiveNumberFields;
    Vector negativeNumberFields;
    Vector zeroNumberFields;

    public CpNumberToStringConverter(String s)
    {
        super(s);
    }

    public CpNumberToStringConverter()
    {
        super();
    }

    public String convert(Object o)
    {

        Enumeration e;
        // Ted Added: 7/25
        if (o == null) {
            return "";
        }
        // End Ted

        if (isGeneral) {
            return o.toString();
        }

        Number num = (Number)o;
        String string;
        float numValue = num.floatValue();

        if (numValue > 0) {
            e = positiveNumberFields.elements();
        } else if (numValue < 0) {
            e = negativeNumberFields.elements();
        } else {
            e = zeroNumberFields.elements();
        }
        string = "";
        while (e.hasMoreElements()) {
            string = string + ((CpToStringConverter)e.nextElement()).convert(new Float(Math.abs(numValue)));
        }
        return string;
    }

    void parseFormatString()
    {
        int firstSemicolonIndex, secondSemicolonIndex;


        if (formatString.length() > 0 && Character.toLowerCase(formatString.charAt(0)) == 'g') {
            isGeneral = true;
        } else {
            isGeneral = false;
            firstSemicolonIndex = formatString.indexOf(';');
            if (firstSemicolonIndex < 0) {
                positiveNumberFields = parseFormatSubstring(formatString);
                negativeNumberFields = positiveNumberFields;
                zeroNumberFields = positiveNumberFields;
            } else {
                positiveNumberFields = parseFormatSubstring(formatString.substring(0,firstSemicolonIndex));
                secondSemicolonIndex = formatString.indexOf(';', firstSemicolonIndex + 1);
                if (secondSemicolonIndex < 0) {
                    negativeNumberFields = parseFormatSubstring(formatString.substring(firstSemicolonIndex + 1, formatString.length()));
                    zeroNumberFields = positiveNumberFields;
                } else {
                    negativeNumberFields = parseFormatSubstring(formatString.substring(firstSemicolonIndex + 1, secondSemicolonIndex));
                    zeroNumberFields = parseFormatSubstring(formatString.substring(secondSemicolonIndex + 1, formatString.length()));
                }
            }
        }
    }



    Vector parseFormatSubstring(String s)
    {
        char currentCharacter;
        int currentState=STATE_START;
        int start=0;
        int nextPosition = 0;
        int length = s.length();


        // Have to initialize these, since the compiler doesn't issue warnings, only errors. Ugh.
        CpScientificNotationToString sciNumField = new CpScientificNotationToString();
        CpFractionToString fractionField = new CpFractionToString();
        CpDecimalToString numField = new CpDecimalToString("");

        String currentToken = "";
        Vector fields = new Vector(5);
        currentCharacter = 'a'; // something arbitrary, just not EOF

        while (currentCharacter != EOF) {
            if (nextPosition == length) {
                currentCharacter = (char)EOF;
            } else {
                currentCharacter = s.charAt(nextPosition);
                nextPosition++;
            }

            switch (currentState) {
            case STATE_START:
                switch (currentCharacter) {
                case '0':
                case '#':
                    currentState = STATE_NUM_OR_SCI_OR_FRAC;
                    break;
                case '.':
                    currentState = STATE_NUM_OR_SCI_SEEN_DEC;
                    break;
                case '"':
                    start = nextPosition;
                    currentState = STATE_INQUOTEDLITERAL;
                    break;
                case 'E':
                case 'e':
                    sciNumField = new CpScientificNotationToString();
                    sciNumField.scientificNotationCharacterString = String.valueOf(currentCharacter);
                    start = nextPosition;
                    currentState = STATE_SCI_SEEN_E;
                    break;
                default:
                    currentState = STATE_INLITERAL;
                    break;
                }
                break;
            case STATE_INLITERAL:
                switch(currentCharacter) {
                case EOF:
                    fields.addElement(new CpStringToString(s.substring(start, nextPosition)));
                    break;
                case '#':
                case '0':
                case '.':
                case 'E':
                case 'e':
                    fields.addElement(new CpStringToString(s.substring(start, nextPosition - 1)));
                    start = nextPosition - 1;
                    currentState = STATE_START;
                    nextPosition--;
                    break;
                }
                break;
            case STATE_INQUOTEDLITERAL:
                switch(currentCharacter) {
                case '"':
                    currentState = STATE_QUOTEINQUOTEDLITERAL;
                    break;
                case EOF:
                    throw new IllegalArgumentException("Bad format");
                }
                break;
            case STATE_QUOTEINQUOTEDLITERAL:
                switch (currentCharacter) {
                case '"':
                // TBD: this is a bug: it doesn't absorb the extra quote.
                    currentState = STATE_INQUOTEDLITERAL;
                    break;
                case EOF:
                    fields.addElement(new CpStringToString(s.substring(start, nextPosition - 1)));
                    break;
                default:
                    fields.addElement(new CpStringToString(s.substring(start, nextPosition - 2)));
                    start = nextPosition - 1;
                    nextPosition--;
                    currentState = STATE_START;
                }
                break;
            case STATE_NUM_OR_SCI_OR_FRAC:
                switch (currentCharacter) {
                case '.':
                    currentState = STATE_NUM_OR_SCI_SEEN_DEC;
                    break;
                case '0':
                case '#':
                case ',':
                case '%':
                    break;
                case EOF:
                    fields.addElement(new CpDecimalToString(s.substring(start, nextPosition)));
                    break;
                default:
                    // Scan ahead for a fraction...
                    int peekPosition = nextPosition - 1;
                    char peekChar;
                    boolean foundFraction = false;
                    do {
                        peekChar = s.charAt(peekPosition++);
                        if (peekChar == '/') {
                            foundFraction = true;
                        }
                    } while (peekChar != '#' && !Character.isDigit(peekChar) && peekPosition < length );

                    if (foundFraction  && peekPosition <= length) {
                        fractionField = new CpFractionToString();
                        fractionField.setNumeratorFormatString(s.substring(start, nextPosition - 1));
                        fractionField.setSlashString(s.substring(nextPosition - 1, peekPosition - 1));
                        nextPosition = peekPosition - 1;
                        start = nextPosition;
                        currentState = STATE_SEENSLASH;
                    } else {
                        fields.addElement(new CpDecimalToString(s.substring(start, nextPosition - 1)));
                        nextPosition--;
                        start = nextPosition;
                        currentState = STATE_START;
                    }
                }
                break;
            case STATE_NUM_OR_SCI_SEEN_DEC:
                switch (currentCharacter) {
                case '0':
                case '#':
                    break;
                case EOF:
                    fields.addElement(new CpDecimalToString(s.substring(start, nextPosition)));
                    break;
                default:
                    fields.addElement(new CpDecimalToString(s.substring(start, nextPosition - 1)));
                    nextPosition--;
                    start = nextPosition;
                    currentState = STATE_START;
                }
                break;
            case STATE_SEENSLASH:
                if (currentCharacter == '#' || currentCharacter == '0') {
                    currentState = STATE_INDENOMINATOR;
                } else {
                    if (Character.isDigit(currentCharacter)) {
                        currentState = STATE_INLITERALDENOMINATOR;
                    } else {
                        throw new IllegalArgumentException("Bad format");
                    }
                }
                break;
            case STATE_INDENOMINATOR:
                switch (currentCharacter) {
                case '0':
                case '#':
                    break;
                case EOF:
                        fractionField.setDenominatorFormatString(s.substring(start, nextPosition));
                        fields.addElement(fractionField);
                        break;
                default:
                    if (Character.isDigit(currentCharacter)) {
                        currentState = STATE_INLITERALDENOMINATOR;
                    } else {
                        fractionField.setDenominatorFormatString(s.substring(start, nextPosition - 1));
                        fields.addElement(fractionField);
                        nextPosition--;
                        start = nextPosition;
                        currentState = STATE_START;
                    }
                }
                break;
            case STATE_INLITERALDENOMINATOR:
                if (currentCharacter == EOF) {
                    fractionField.setLiteralDenominator(s.substring(start, nextPosition));
                    fields.addElement(fractionField);
                } else {
                    if (!Character.isDigit(currentCharacter)) {
                        fractionField.setLiteralDenominator(s.substring(start, nextPosition - 1));
                        fields.addElement(fractionField);
                        nextPosition--;
                        start = nextPosition;
                        currentState = STATE_START;
                    }
                }
                break;
            case STATE_SCI_SEEN_E:
                switch (currentCharacter) {
                case '+':
                case '-':
                    sciNumField.showPositiveExponentSign = (currentCharacter == '+');
                    start = nextPosition;
                    currentState = STATE_SCI_SEEN_SIGN;
                    break;
                default:
                    throw new IllegalArgumentException("Bad format");
                }
                break;
            case STATE_SCI_SEEN_SIGN:
                switch (currentCharacter) {
                case '#':
                case '0':
                    currentState = STATE_SCI_SEEN_SIGN_AND_NUM;
                    break;
                case '.':
                    currentState = STATE_SCI_SEEN_DEC;
                    break;
                default:
                    throw new IllegalArgumentException("Bad format");
                }
                break;
            case STATE_SCI_SEEN_SIGN_AND_NUM:
                switch (currentCharacter) {
                case '#':
                case '0':
                    break;
                case '.':
                    currentState = STATE_SCI_SEEN_DEC;
                    break;
                case EOF:
                    sciNumField.setExponentFormatString(s.substring(start, nextPosition));
                    fields.addElement(sciNumField);
                    break;
                default:
                    sciNumField.setExponentFormatString(s.substring(start, nextPosition - 1));
                    fields.addElement(sciNumField);
                    nextPosition--;
                    start = nextPosition;
                    currentState = STATE_START;
                    break;
                }
                break;
            case STATE_SCI_SEEN_DEC:
                switch (currentCharacter) {
                case '#':
                case '0':
                    break;
                case EOF:
                    sciNumField.setExponentFormatString(s.substring(start, nextPosition));
                    fields.addElement(sciNumField);
                    break;
                default:
                    sciNumField.setExponentFormatString(s.substring(start, nextPosition - 1));
                    fields.addElement(sciNumField);
                    nextPosition--;
                    start = nextPosition;
                    currentState = STATE_START;
                    break;
                }
                break;
            default:
                throw new IllegalArgumentException("Internal error - unknown state");
            }
        }

        // Scan the fields, looking for dependencies between them.
        Enumeration e = fields.elements();
        fractionField = null;
        sciNumField = null;
        numField = null;
        CpToStringConverter field;

        while (e.hasMoreElements()) {
            field = (CpToStringConverter)e.nextElement();
            if (field instanceof CpFractionToString) {
                fractionField = (CpFractionToString)field;
            }
            if (field instanceof CpDecimalToString) {
                numField = (CpDecimalToString)field;
            }
            if (field instanceof CpScientificNotationToString) {
                sciNumField = (CpScientificNotationToString)field;
            }
        }

        if (numField != null) {
            numField.isScientificNotation = (sciNumField != null);
        }

        if (fractionField != null) {
            fractionField.includeIntegerPortion = (numField == null);
        }

        return fields;
    }



    public boolean isFormatStringValid(String s) {
        // we'll do something with this later.
        return true;
    }

}
