/**
 * 
 */
package xomo.deliverable.julian.gui;

//import static org.junit.Assert.assertTrue;

//import org.junit.Test;
import java.lang.Math;
;
/**
 * @author julianr
 * Class for COCOMO attributes and estimation
 * 
 */
public class Cocomo {
	public static final int COCOMONUMATTRS = 28;

	public static final String[] COCOMOATTRS = { "A", "B", "KSLOC", "ACAP",
			"APEX", "CPLX", "DATA", "DOCU", "FLEX", "LTEX", "PCAP", "PCON",
			"PLEX", "PMAT", "PREC", "PVOL", "RELY", "RESL", "RUSE", "SCED",
			"SITE", "STOR", "TEAM", "TIME", "TOOL", 
                        "AUTOMATED_ANALYSIS", "PEER_REVIEWS", "EXECUTION_TESTING_AND_TOOLS"};
	
	public static String[] tuningtable = { //-1.0 = "?"
		"A", "3.0", "B", "1.0", 
		"time","NA","NA","-1.0","1.00","1.11", "1.29","1.63",
		"stor","NA","NA","-1.0","1.00","1.05","1.17","1.46",
		"data","NA","-1.0","0.90","1.00","1.14","1.28","NA",
		"pvol","NA","-1.0","0.87","1.00","1.15","1.30","NA",
		"ruse","NA","-1.0","0.95","1.00","1.07","1.15","1.24",
		"rely","-1.0","0.82","0.92","1.00","1.10","1.26","NA",
		"docu","-1.0","0.81","0.91","1.00","1.11","1.23","NA",
		"acap","-1.0","1.42","1.19","1.00","0.85","0.71","NA",
		"pcap","-1.0","1.34","1.15","1.00","0.88","0.76","NA",
		"pcon","-1.0","1.29","1.12","1.00","0.90","0.81","NA",
		"apex","-1.0","1.22","1.10","1.00","0.88","0.81","NA",
		"plex","-1.0","1.19","1.09","1.00","0.91","0.85","NA",
		"ltex","-1.0","1.20","1.09","1.00","0.91","0.84","NA",
		"tool","-1.0","1.17","1.09","1.00","0.90","0.78","NA",
		"sced","-1.0","1.43","1.14","1.00","1.00","1.00","NA",
		"cplx","-1.0","0.73","0.87","1.00","1.17","1.34","1.74",
		"site","-1.0","1.22","1.09","1.00","0.93","0.86","0.80",
		"prec","-1.0","6.20","4.96","3.72","2.48","1.24","0.0",
		"flex","-1.0","5.07","4.05","3.04","2.03","1.01","0.0",
		"resl","-1.0","7.07","5.65","4.24","2.83","1.41","0.0",
		"team","-1.0","5.48","4.38","3.29","2.19","1.01","0.0",
		"pmat","-1.0","7.80","6.24","4.68","3.12","1.56","0.0",
                "automated_analysis","-1.0","0.5","0.44","0.28","0.13","0.0","0.0",
                "peer_reviews","-1.0","0.78","0.7","0.54","0.4","0.28","0.0",
                "execution_testing_and_tools","-1.0","0.7","0.65","0.54","0.43","0.23","0.0",}; // the Coqualmo attributes here have been set from table d
		
		public Cocomo tunings;
	
		// Double value representing an invalid tuning parameter
		public static final Double NA=-99942.42; 
		
	public String name;

	public Attribute[] attrs;

//	public class Attribute {
//		public Object attr;
//		
//		public Attribute() {
//		}
//	}
//	
//	public class CocValue extends Attribute {
//		Double value;
//		
//		public CocValue(double v) {
//			value = new Double(v);
//		}
//		
//		public String toString() {
//			return(value.toString());
//		}
//	}
//	
//	public class CocRange extends Attribute {
//		MinMax value;
//		
//		public CocRange(MinMax m) {
//			value = m;
//		}
//		
//		public String toString() {
//				return(value.toString());
//		}
//	}
//	
//	public class TuningRow extends Attribute {
//		Double[] values;
//		
//		public TuningRow() {
//			values = new Double[6];
//		}
//		
//		public void set(int x, Double v) {
//			values[x]=v;
//		}
//		
//		public String toString() {
//			String result="{ ";
//			for(Double v: values) result+=v+" ";
//			return(result + "}");
//		}
//	}
	
	public Cocomo() {
	}
	
	public Cocomo(String _name) {
		name = _name;
		attrs = new Attribute[COCOMONUMATTRS];
	}

	public Cocomo(String _name, Attribute[] _attrs) {
		name = _name;
		attrs = _attrs;
	}

	/**
	 *
	 * @param attr String name of COCOMO attribute
	 * @param value MinMax value to set that COCOMO attribute
	 * @return updated COCOMO attributes (this)
	 */
	public Cocomo set(String attrname, Attribute value) throws AttributeNotKnownException {
		Boolean foundAttribute = false;
		
		for (int i = 0; i < COCOMONUMATTRS; i++) {
			if (attrname.equals(COCOMOATTRS[i])) {
				foundAttribute = true;
				attrs[i] = value;
			}
		}
		if (!foundAttribute) throw new AttributeNotKnownException(attrname);
		return (this);
	}

	/**
	 *
	 * @param attr String name of COCOMO attribute
	 * @return value of that COCOMO attribute - can be any valid Attribute subtype.
	 */
	public Attribute get(String attrname) throws AttributeNotKnownException {
		Boolean foundAttribute = false;
		
		Attribute result = new Attribute();
		for (int i = 0; i < COCOMONUMATTRS; i++) {
			if (attrname.equals(COCOMOATTRS[i])) {
				foundAttribute = true;
				result = attrs[i];
			}
		}
		if (!foundAttribute) throw new AttributeNotKnownException(attrname);
		return (result);
	}

	public String toString() {
		String result = "Name: "+name+", ";

		for (int i = 0; i < COCOMONUMATTRS; i++) {
			result += COCOMOATTRS[i] + ": " + attrs[i] + ", ";
		}
		return (result);
	}

	public Cocomo defaultTunings() {
		tunings = new Cocomo("Tunings from Menzies XOMO file");
		
		/* Set A and B values */
		
		tunings.set(tuningtable[0],new CocValue(Double.parseDouble(tuningtable[1])));
		tunings.set(tuningtable[2],new CocValue(Double.parseDouble(tuningtable[3])));
		
		for (int i = 0; i < 25; i++) {
			TuningRow row = new TuningRow();
			String attrname=tuningtable[i*8+4].toUpperCase();
			for(int j=0;j<7;j++) {
				String e = tuningtable[i*8+4+j+1];
				if (e.equals("NA")) row.set(j,Cocomo.NA);
				else row.set(j,Double.parseDouble(e)); 
			}
			tunings.set(attrname,row);
		}
		return(tunings);
	}
	
	class AttributeNotKnownException extends IndexOutOfBoundsException {	
		public AttributeNotKnownException(String s) {
			super(s);
		}
	}
	
	class ParameterOutofRangeException extends IndexOutOfBoundsException {	
		public ParameterOutofRangeException(String s) {
			super(s);
		}
	}
	
	
	/* Look up value of attribute attrname in this project in the table of tuning 
	 * parameters. Throw an exception if attribute does not have a numeric value
	 * in the correct range. 
	 * 
	 * If an attribute has a non-integer value, interpolate between its floor and
	 * ceiling. */
	public double lookup(String attrname) throws AttributeNotKnownException, ParameterOutofRangeException {
		double lowervalue=-1;
		double uppervalue=-1;
		double result=-1;
		
		Boolean attrexists = false;
		for (int i = 0; i < COCOMONUMATTRS; i++) {
			if (attrname.equals(COCOMOATTRS[i])) {
				attrexists = true;
				CocValue attrvalue = (CocValue)attrs[i];
				Double attrdouble = attrvalue.value;
				TuningRow row = (TuningRow)(tunings.attrs)[i];
				if ((attrdouble < 1.0) || (attrdouble > 7.0))
					throw new ParameterOutofRangeException(attrname+"="+attrdouble);
				else {
						int lower = attrdouble.intValue();
						double delta = attrdouble-(double)lower;
						lowervalue = (row.values)[7-lower];
						
                                                                                              
						/* Get next value up if attrdouble is non-integral
						 * so that we can interpolate.
						 */
						if (delta>0) {
							uppervalue = (row.values)[6-lower];
						}
						
						/* Throw exception if values of parameter which we look up
						 * in tuning table are Cocomo.NA, which signifies a parameter
						 * which cannot take that value.
						 */
						if ((lowervalue == Cocomo.NA) || (uppervalue == Cocomo.NA))
							throw new ParameterOutofRangeException(attrname+"="+attrdouble);
						
						/* Calculate result, interpolating if attrdouble is non-integral */
						result=lowervalue+delta*(uppervalue-lowervalue);
                                                
//                                                System.out.println("lower = " + lower + "\ndelta = " + delta + "\nlowervalue = " + lowervalue + "\nuppervalue = " + uppervalue + "\n");
				}
			}
		}
		if (!attrexists) throw new AttributeNotKnownException(attrname);
		
		return(result);	
	}
	
	/* Return (double) product of effort multipliers in this project. Exceptions
	 * may be thrown when looking up attribute values. */
	public double ems() {
			double result=1.0;
			String emattrs[] = {"RELY", "DATA", "CPLX", "RUSE", "DOCU", "TIME",
					"STOR", "PVOL", "ACAP", "PCAP", "PCON", "APEX", "PLEX",
					"LTEX", "TOOL", "SITE", "SCED"};
			
			for(String em: emattrs) {
				result = result*lookup(em);
			}
			
			return(result);
	}
	
	/* Return (double) sum of scale factors in this project. Exceptions
	 * may be thrown when looking up attribute values. */
	public double sfs() {
		double result=0.0;
		String sfattrs[] = {"PREC", "FLEX", "RESL", "TEAM", "PMAT"};
		
		for(String sf: sfattrs)
			result = result+lookup(sf);
		
		return(result);
	}	
	
	public double a() {
		Attribute av = tunings.get("A");
		return(((CocValue)av).value.doubleValue());
	}
	
	public double b() {
		return(((CocValue)tunings.get("B")).value.doubleValue());
	}
	
	public double ksloc() {
		return(((CocValue)get("KSLOC")).value.doubleValue());
	}
        
        public int prec() {
		return(((CocValue)get("PREC")).value.intValue());
	}
        
        public int resl() {
		return(((CocValue)get("RESL")).value.intValue());
	}
        
        public int team() {
		return(((CocValue)get("TEAM")).value.intValue());
	}
        
        public int pmat() {
		return(((CocValue)get("PMAT")).value.intValue());
	}
	
        public int flex() {
		return(((CocValue)get("FLEX")).value.intValue());
	}
        
        public int rely() {
		return(((CocValue)get("RELY")).value.intValue());
	}
        
        public int data() {
		return(((CocValue)get("DATA")).value.intValue());
	}
        
        public int docu() {
		return(((CocValue)get("DOCU")).value.intValue());
	}
        
        public int cplx() {
		return(((CocValue)get("CPLX")).value.intValue());
	}
        
        public int ruse() {
		return(((CocValue)get("RUSE")).value.intValue());
	}
        
        public int time() {
		return(((CocValue)get("TIME")).value.intValue());
	}
        
        public int stor() {
		return(((CocValue)get("STOR")).value.intValue());
	}
        
        public int pvol() {
		return(((CocValue)get("PVOL")).value.intValue());
	}
        
        public int acap() {
		return(((CocValue)get("ACAP")).value.intValue());
	}
        
        public int apex() {
		return(((CocValue)get("APEX")).value.intValue());
	}
        
        public int pcap() {
		return(((CocValue)get("PCAP")).value.intValue());
	}
        
        public int plex() {
		return(((CocValue)get("PLEX")).value.intValue());
	}
        
        public int ltex() {
		return(((CocValue)get("LTEX")).value.intValue());
	}
        
        public int pcon() {
		return(((CocValue)get("PCON")).value.intValue());
	}
        
        public int tool() {
		return(((CocValue)get("TOOL")).value.intValue());
	}
        
        public int sced() {
		return(((CocValue)get("SCED")).value.intValue());
	}
        
        public int site() {
		return(((CocValue)get("SITE")).value.intValue());
	}
        
        public int automated_analysis() {
		return(((CocValue)get("AUTOMATED_ANALYSIS")).value.intValue());
	}
        
        public int peer_reviews() {
		return(((CocValue)get("PEER_REVIEWS")).value.intValue());
	}
        
        public int execution_testing_and_tools() {
		return(((CocValue)get("EXECUTION_TESTING_AND_TOOLS")).value.intValue());
	}
        
        public int getAttVal (String attname) {            
            return(((CocValue)get(attname)).value.intValue());
        }
        
        
	public double estimate() {
		return(a()*ems()*Math.pow(ksloc(),b()+0.01*sfs()));
	}
//@Test 
	public void testCOCOMO1() {
		Cocomo cev = new Cocomo("CEV");
		cev.defaultTunings();
		System.out.println(cev.tunings);

		cev.set("KSLOC",new CocValue(100.0));
		cev.set("PREC",new CocValue(3.0));    /* NB VL=1.0, XH=6.0 */ //WRONG!
		cev.set("RESL",new CocValue(3.0));
		cev.set("TEAM",new CocValue(3.0));
		cev.set("PMAT",new CocValue(3.0));
		cev.set("FLEX",new CocValue(3.0));
		cev.set("RELY",new CocValue(3.0));
		cev.set("DATA",new CocValue(3.0));
		cev.set("DOCU",new CocValue(3.0));
		cev.set("CPLX",new CocValue(3.0));
		cev.set("RUSE",new CocValue(3.0));
		cev.set("TIME",new CocValue(3.0));
		cev.set("STOR",new CocValue(3.0));
		cev.set("PVOL",new CocValue(3.0));
		cev.set("ACAP",new CocValue(3.0));
		cev.set("APEX",new CocValue(3.0));
		cev.set("PCAP",new CocValue(3.0));
		cev.set("PLEX",new CocValue(3.0));
		cev.set("LTEX",new CocValue(3.0));
		cev.set("PCON",new CocValue(3.0));
		cev.set("TOOL",new CocValue(3.0));
		cev.set("SCED",new CocValue(3.0));
		cev.set("SITE",new CocValue(3.0));

		double acap_l=cev.lookup("TIME");
		double a=cev.a();
		System.out.println("A="+a);
		
		double estimate=cev.estimate();
		System.out.println("COCOMO II Estimate = "+estimate);
		System.out.println("TIME = "+acap_l);
		System.out.println("CEV attributes = "+cev);
		String expectedResult="Name: CEV, A: null, B: null, KSLOC: 100.0, ACAP: 5.0, APEX: null, CPLX: 1.0";
//		assertTrue(((cev.toString()).substring(1,42)).equals(expectedResult.substring(1,42)));
//		assertTrue(acap_l==1.19);
	}
//	
}

class Attribute {
    public Object attr;
    
    public Attribute() {
    }
}

class CocValue extends Attribute {
    Double value;
    
    public CocValue(double v) {
        value = new Double(v);
    }
    
    public String toString() {
        return(value.toString());
    }
}

class CocRange extends Attribute {
    MinMax value;
    
    public CocRange(MinMax m) {
        value = m;
    }
    
    public String toString() {
        return(value.toString());
    }
}

class TuningRow extends Attribute {
    Double[] values;
    
    public TuningRow() {
        values = new Double[7];
    }
    
    public void set(int x, Double v) {
        values[x]=v;
    }
    
    public String toString() {
        String result="{ ";
        for(Double v: values) result+=v+" ";
        return(result + "}");
    }
}
