import eprog.*;
import java.util.*;

/**
 * <tt>WidParser</tt><br>
 * WidParser liefert mir den Wert des WiderstandNetzes.
 *
 * @see FLAGS
 * @see Widerst
 */

class WidParser extends EprogIO
{
    private double fWiderstand;
    private boolean bParseError;

    //Konstanten für den Parser
    private final char SEQUENCEOPEN = '(';
    private final char SEQUENCECLOSE = ')';
    private final char LIMITER = ',';

    //laut Spez dürfens nicht mehr als 6 Stück sein... ;(
    private final int EPROG_LIMIT = 6;
    
    //ein Counter um das auch zu überwachen...
    private int iEprogCount = 0;
    
    /**
     * <tt>WidParser</tt><br>
     * Constructor; nimmt den Eingabestring entgegen und parsed ihn.
     */
    WidParser(String sInputString)
    {
	bParseError = false;
	fWiderstand = parseString(sInputString);
    }

    /**
     * <tt>getWiderstand()</tt>
     * @return double
     */
    public double getWiderstand()
    {
	return fWiderstand;
    }
    
    /**
     * <tt>getError()</tt><br>
     * @return boolean
     */
    public boolean getError()
    {
	return bParseError;
    }

    /**
     * <tt>parseString</tt><br>
     * rekursiver Parser der das WiderstandsNetz auswertet.
     * @return double
     */
    private double parseString(String sWidNet)
    {
	Vector vWidValues = new Vector();
	double result= 0.0;
	char cOP = 'x';
	int iSkip= 0;

	//String muss mit "(" beginnen und mit ")" enden.
	if(sWidNet.charAt(0) == SEQUENCEOPEN && sWidNet.charAt(sWidNet.length()-1) == SEQUENCECLOSE)
	{
	    //er tut es, und wir kürzen die Klammer weg...
	    sWidNet = sWidNet.substring(1,sWidNet.length()-1);
	    
	    if(FLAGS.DEBUG)
		println("in 'parseString()': value of string at start:\t" + sWidNet);
	    
	    //Schleife arbeitet sich durch den ganzen String durch.
	    for(int i=0;;i++)
	    {
		//Hoppala, wir haben einen Unterausdruck anstelle von
		//Widerstandswert. Also extrahieren wir den Unterausdruck
		//und geben ihn als Argument rekursiv parseString mit.
		if(sWidNet.charAt(0)== SEQUENCEOPEN)
		{
		    String stmp = getSubSequence(sWidNet);
		    iSkip = stmp.length();
		    vWidValues.add(new Double(parseString(stmp)));
		}
		
		//Wir haben einen WiderstandsWert gefunden. Da es auch
		//nur ein Symbol sein kann, extrahieren wir es wieder und
		//geben es der Methode getValue() mit.
		else
		{
		    if(++iEprogCount>EPROG_LIMIT){bParseError=true;return 0.0;};
		    vWidValues.add(new Double(getValue(getSubValue(sWidNet))));
		    if(((Double)(vWidValues.get(i))).doubleValue() < 1){bParseError= true;return 0.0;}		   
		    if(FLAGS.DEBUG)
			println("in 'parseString()': value of current argument:\t" + vWidValues.get(i).toString());
	    	}
		//Wir schneiden die länge der Subsequenz ab,
		sWidNet = sWidNet.substring(iSkip,sWidNet.length());
		iSkip = 0;
		//und holen uns den nächsten Ausdruck.
		sWidNet = sWidNet.substring(sWidNet.indexOf(LIMITER)+1,sWidNet.length());
		if(FLAGS.DEBUG)
		    println("in 'parseString()': value for next loop:\t" + sWidNet);
		
		//wenn jetzt nichts mehr übrig ist, hat der String einen
		//Syntaxfehler gehabt. Ansonsten holen wir uns den
		//nächsten Character und schauen obs ein Operator ist.
		if(sWidNet.length() < 1){bParseError=true; return 0.0;}
		cOP = sWidNet.charAt(0);
		if(cOP == 'S')
		{
		    //nach dem Operator darf nichts mehr kommen, da wir
		    //ja die letzte Klammer schon weggeschmissen haben...
		    if(sWidNet.length() > 1){bParseError=true;return 0.0;}
		    
		    //Juhu... wir kennen den Operator und können uns
		    //somit den Wert das Ausdrucks berechnen.
		    for(int j=0; j < vWidValues.size(); j++)
			result += ((Double)(vWidValues.get(j))).doubleValue();
		    break;
		}
		else if(cOP == 'P')
		{
		    //siehe oben... *g*
		    if(sWidNet.length() > 1){bParseError=true;return 0.0;}
		    //detto...
		    for(int j=0; j < vWidValues.size(); j++)
			result += 1.0/((Double)(vWidValues.get(j))).doubleValue();
		    result = 1 / result;
		    break;
		}
	    }
	}
	else
	{
	    if(FLAGS.DEBUG)
		println("in 'parseString()': PARSE ERROR - ABORTING");
	    bParseError = true;
	}
	if(FLAGS.DEBUG)
	    println("in 'parseString()': returnvalue of sequence:\t" + result);
	//wenn wir hier ankommen, haben wir einen gültigen Ausdruck
	//gehabt, und kennen jetzt seinen Wert.
	return result;
    }
    
    /**
     * <tt>getSubSequence()</tt><br>
     * holt aus einem verschachtelten Ausdruck den Unterausdruck
     * heraus.<br>
     * Eine kleine Graphik:<br>
     * ((a,b,c,P),x,S) -> (a,b,c,P)
     *
     * @return String
     */
    private String getSubSequence(String sSequence)
    {
	if(FLAGS.DEBUG)
	    println("in 'getSubSequence()': value at start:\t\t" + sSequence);
	//in welchem nesting-level bin ich?
	int iCounter=0;
	for(int i=0; i < sSequence.length(); i++)
	{
	    if(sSequence.charAt(i)== SEQUENCEOPEN)iCounter++;
	    if(sSequence.charAt(i)== SEQUENCECLOSE)iCounter--;

	    //aahh, wir haben die passende Endklammer gefunden.
	    if(iCounter == 0)
	    {
		//somit geb ich den Unterausdruck zurück.
		String tmp = sSequence.substring(0,++i);
		if(FLAGS.DEBUG)
		    println("in 'getSubSequence()': value at return:\t\t" + tmp);
		return tmp;
	    }
	}
	//wenn wir hier ankommen, hatte der String einen Syntaxfehler.
	return " ";
    }

    /**
     * <tt>getSubValue</tt><br>
     * liefert den Ausdruck vor dem ersten Limiter zurück.<br>
     * Beispiel:<br>
     * x,y,P -> x
     *
     * @return String
     */
    private String getSubValue(String sValue)
    {
	int iPos = sValue.indexOf(LIMITER);
	//iPos kann -1 werden wenn kein LIMITER gefunden. also machen
	//wirs auf alle fälle grösser als 0
	return sValue.substring(0,(iPos < 0 ? 0 : iPos));
    }

    /**
     * <tt>getValue</tt><br>
     * liefert den Wert eines möglichen Symbols zurück.<br>
     * Beispiel:<br>
     * 4K7 -> 4700
     *
     * @return double;
     */
    private double getValue(String sValue)
    {
	if(FLAGS.DEBUG)
	    println("in 'getValue()': get value of this:\t\t" + sValue);
	char ctmp= ' ';
	try{
	// wir loopen uns durch den String... 
	for(int i=0; i < sValue.length(); i++)
	{
	    //...und ersetzen alles was keine Zahl ist duch einen
	    //Dezimalpunkt und multiplizieren mit der entsprechenden
	    //Potenz.
	    ctmp = sValue.charAt(i);
	    if(!Character.isDigit(ctmp))
	    {
	    switch(ctmp)
		{
		case 'M':
		    return Double.parseDouble(sValue.replace('M','.')) * 1000000.0;
		case 'K':
		    return Double.parseDouble(sValue.replace('K','.')) * 1000.0;
		case 'R':
		case '.':
		    return Double.parseDouble(sValue.replace('R','.')); 
		//irgendwas was keine Zahl ist, aber auch kein Symbol...
		default:
		    return -1.0;
		}
	    }
	}
	return Double.parseDouble(sValue);
	}
	//wenn parseDouble eine Exception wirft, können wir davon
	//ausgehen, dass das Symbol nicht der Spez entspricht.
	catch(NumberFormatException e){
	    bParseError = true;
	    return -1.0;
	}
    }
}