import java.text.NumberFormat;

public class CalculatorModel
{
    private static final String NO_OP = "";
    private double myStored;
    private double myCurrent;
    private String myOperation;
    private boolean myStarting;
    private NumberFormat myFormatter;
    private ICalculatorView myView;
    
    public CalculatorModel(ICalculatorView view)
    {
        this();        
        myView = view;
    }

    public CalculatorModel()
    {
        myFormatter = NumberFormat.getInstance();
        myFormatter.setMinimumFractionDigits(20);
        clear();
    }    
    public void setView(ICalculatorView view)
    {
        myView = view;
    }

    public void clear()
    {
        myStored = myCurrent = 0;
        myStarting = true;
        myOperation = NO_OP;
        update(myCurrent);
    }

    public void clearEntry()
    {
        myCurrent = 0;
        myStarting = true;
        update(myCurrent);
    }

    public void addDigit(int digit)
    {
        if (myStarting){
            myStarting = false;
            myCurrent = 0;
        }
        if (myCurrent >= 0){
            myCurrent = myCurrent * 10 + digit;            
        }
        else {
            myCurrent = myCurrent * 10 - digit;
        }

        update(myCurrent);
    }

    public void unaryOp(String op)
    {
        boolean error = false;
        if (op.equals("sin")){
            myCurrent = Math.sin(myCurrent);
        }
        else if (op.equals("cos")){
            myCurrent = Math.cos(myCurrent);
        }
        else if (op.equals("tan")){
            myCurrent = Math.tan(myCurrent);
        }
        else if (op.equals("e^x")){
            myCurrent = Math.pow(Math.E,myCurrent);
        }
        else if (op.equals("x^2")){
            myCurrent = myCurrent * myCurrent;
        }
        else if (op.equals("1/x")){
            if (myCurrent != 0){
                myCurrent = 1/myCurrent;
            }
            else {
                error = true;
            }
        }
        else if (op.equals("ln")){
            myCurrent = Math.log(myCurrent);
        }
        else if (op.equals("log")){
	    if (myCurrent <= 0){
		error = true;
	    }
	    else {
		myCurrent = Math.log(myCurrent)/Math.log(10);
	    }
        }
        else if (op.equals("n!")){
            double dval =  Math.abs(Math.floor(myCurrent));
	    if (dval == Double.POSITIVE_INFINITY){
		error = true;
	    }
	    else {
		int val = (int) dval;
		double prod = 1;
		for(int k=1; k <= val; k++){
		    prod *= (long)k;
		}
		myCurrent = prod;		
	    }
        }
        else if (op.equals("x^3")){
            myCurrent = myCurrent * myCurrent * myCurrent;
        }
        else if (op.equals("pi")){
            myCurrent = Math.PI;
        }
        else if (op.equals("e")){
            myCurrent = Math.E;
        }
	if (error){
	    showError("Error: function evaluation");
            myOperation = NO_OP;
            myStored = myCurrent = 0;
            myStarting = true;	    
	}
	else {
	    update(myCurrent);   
	}
    }
    
    public void binaryOp(String op)
    {
        if (myOperation.equals(NO_OP)){
            myStored = myCurrent;
            update(myStored);
            myOperation = op;
            myStarting = true;
        }
        else {
            doOp(op);
        }
    }

    public void toggleSign()
    {
        myCurrent = -1*myCurrent;
        update(myCurrent);
    }

    private void doOp(String op)
    {
        double result = 0.0;
        boolean error = false;

        if (myOperation.equals("*")){
            result = myStored * myCurrent;
        }
        else if (myOperation.equals("/")){
            if (myCurrent == 0.0){
                error = true;
            }
            else {
                result = myStored/myCurrent;
            }
        }
        else if (myOperation.equals("+")){
            result = myStored + myCurrent;
        }
        else if (myOperation.equals("-")){
            result = myStored - myCurrent;
        }

        
        // all done processing, show result
        
        if (error){
            showError("Error: division by zero");
            myOperation = NO_OP;
            myStored = myCurrent = 0;
            myStarting = true;
        }
        else {
            myStarting = true;            
            if (op.equals("=")){
                myOperation = NO_OP;
            }
            else {
                myOperation = op;                
            }

            myStored = myCurrent = result;
            update(myStored);
        }
    }

    private void update(double value)
    {
        String s = myFormatter.format(value);
        int dot = s.indexOf(".");
        if (dot != -1){
            int lastNonZero = s.length()-1;
            while (s.charAt(lastNonZero) == '0'){
                lastNonZero--;
            }
            s = s.substring(0,lastNonZero+1);            
        }
        if (myView != null){
            myView.display(s);
        }        
    }

    private void showError(String s)
    {
        myView.display(s);
    }
}
