
function makeZeroAdult()
{
    ad = {
	age:0,      
        sex: "FEMALE",
	period: "MONTH",
        married:0,  
        serps: "IN", /* are you contacted in our out of SERPS State Earnings Related Pension Scheme */
        employment: "EMPLOYEE", /* are you EMPLOYEE,SELF_EMP,RETIRED,UNOCCUPIED }; */
        earnings:0.0,  /* what are your earnings per [week??] */
        selfemp: 0.0,  /* what are your weekly self employment profits? */
        savingsIncome:0.0,  /* what is your [weekly] income from savings */
        pensions:0.0   }  /* Incl. state pensions */
   return ad;
}


/* Result: family.
   Creates a new 'family' structure.
*/
function makeZeroFamily()
{
  fam = {
        title: "",
	numAdults: 0,
        ads: new Array( makeZeroAdult(), makeZeroAdult() ),
        beer:0,  /* pints */
        wine:0,  /** xx cl bottles **/
        spirits:0,  /** xx cl whisky bottles **/
        petrol:0,   /** litres **/
        fags:0, 
        kids: new Array( 0 )
  }
  return fam;
}


/* Result: res.
   Creates a new 'results' structure.
*/
function makeZeroResults( )
{
	res = {
		incomeTax: new Array( 0, 0 ),
		ni: new Array( 0, 0 ),
		beer:0,
		wine:0,
		spirits:0,
		fags:0,
		indirect:0,
		direct:0,
		totalTax:0
	}
	return res;
}

function arrayCopy( orig )
{
  var result = new Array( orig.length );
  var i;
  for ( i=0; i<orig.length; i++ ) {
    result[i] = orig[i];
  }
  return result;
}


/* Argument ad: an adult.
   Argument sys: a taxsystem.
   Result: real.
   This is a simple NI calculator, for employees and self-employed.
*/
function calculateNI( ad, sys ){
  var niRes = 0.0;
  var ni = 0.0;
  /* always employent first, even if se bigger*/
  if (((ad.age < 60) && (ad.sex='FEMALE')) || 
      ((ad.age < 65) && (ad.sex='MALE'))) {
    // always do employment first
    if ( ad.earnings > 0.0 ){
      if( ad.serps == 'IN' ){
	niRes = calculateBands( sys.niInRates, 
				sys.niBands, 
				ad.earnings );
      } else {
	niRes = calculateBands( sys.niOutRates, 
				sys.niBands, 
				ad.earnings );
      }
      
    } 
    if( ad.selfemp > 0.0 ){
            // this hack ensures we only calculate emlpoyer's ni on self emp 
            // up to combined UEL
            var tmpNiBands = arrayCopy( sys.niBands );
            tmpNiBands[1] = tmpNiBands[1] - ad.earnings;
            if( tmpNiBands[1] > tmpNiBands[0]){
   
                 niRes += calculateBands( 
                              sys.niClass4, 
			      tmpNiBands,  
			      ad.selfemp );
            }
            if ( ad.selfemp >  sys.niLel){
                  niRes += sys.niClass2;
            }
         }
      }
      return niRes;
}
  /* alert( 'NI: income is'+ad.earnings+' result is '+niRes+" employment="+ad.employment+" serps="+ad.serps ); */



/* Argument fam: a family.
   Argument sys: a taxsystem.
   Argument res: a results structure.
   Result: void.
   This function updates res with values
   for the family's indirect taxes.
*/
function calculateIndirect( fam, sys, res )
{
  res.beer = (fam.beer * sys.beer);
  
  res.wine = (fam.wine * sys.wine); 
  res.spirits = fam.spirits * sys.spirits
  res.petrol = fam.petrol * sys.petrol;
  res.fags = fam.fags * sys.fags;
  res.indirect = res.beer + res.wine + res.spirits + res.petrol+ res.fags;
  //alert( 'res.indirect ' + res.indirect ); 
}


/* Argument baseRates: an array of rates.
   Argument baseBands: an array of bands.
   Argument savingsRate: a real.
   Argument taxableSavings: a real.
   Argument taxableOther: a real.
   Result: real.
 
   Constructs truncated rate/band arrays for use in savings taxation.
   e.g. if you have 27000 earnings & 29900 higher rate, outBands 
   would be [0] = 2900, [1] = 999999999
   outrates would be[20][40], with 20 coming from savingsRate
   returns number of bands needed for trucated arrays, with arrays in 
   outBands & outRates;
 
   Calculate tax on savings, using a Cunning Plan. Constuct a new set
   of tax rates, starting at where the other taxable income leaves
   off. Set the basic rate of this to savingsRate (20%) and run a
   standard calculation.
   returns ValAndLen.val = tax on savings, .len = garbage.
*/
function calculateSavingsTax(  baseRates, 
			       baseBands, 
			       savingsRate,
			       taxableSavings, 
			       taxableOther ){
  var resSavings = 0.0;
  var x = 0.0;
  var i = 0;
  var numOutBands = 0;
  var savingsBands = new Array(0);
  var savingsRates = new Array(0);
  for( i = 0; i < baseRates.length; i++ ) {
    x = baseBands[i] - taxableOther;
    taxableOther -= baseBands[i];
    if( taxableOther < 0.0 ) {
      taxableOther = 0.0;
    }
    if (x > 0.0) {
      push( savingsBands, x );
      push( savingsRates, baseRates[ i ] );
      if( i == 1 ) {
        savingsRates[numOutBands] = savingsRate;
      }
      numOutBands++;
    }
  }
  resSavings = calculateBands( 
 			      savingsRates, 
			      savingsBands, 
			      taxableSavings );
  /* alert( 'calculateSavingsTax numOutBands='+numOutBands + "taxableOther = "+taxableOther+ 
         "\nband[0] = "+savingsBands[0]+"resSavings="+resSavings+" taxableSavings="+taxableSavings ); **/
                              
 return resSavings;
}


/* Argument ad: an 'adult' structure.
   Argument adno: an integer.
   Argument sys: a taxsystem.
   Result:real.
   This function calculates income tax for the adult,
   with age allowances & flat rates for savings
   income.
*/
function calculateIncomeTax( ad, adno, sys ){
  var resOther = 0;
  var resSavings = 0;
  var incExlSavings = 0;
  incExlSavings += ad.earnings + ad.selfemp + ad.pensions;
  /*
  alert( 'incExlSavings ='+incExlSavings+' ad.earnings = '+ad.earnings + ' ad.selfemp = '+ad.selfemp + ' ad.pensions '+ ad.pensions );
  */
  var incIncSavings = incExlSavings + ad.savingsIncome;
  var extraInc = 0.0;
  var tax = 0.0;
  var taxableExlSavings = 0.0;
  var taxableSavings = 0.0;
  
  var allowance = sys.personalAllowance;
  /* age allowance, and withdrawal thereof */
  if( ((ad.age >= 65 ) && ( ad.sex = 'MALE' )) ||
      ((ad.age >= 60 ) && ( ad.sex = 'FEMALE' ))){
    if( ad.age >= 75 ){
      allowance = sys.ageAllowance2;
    } else {
      allowance = sys.ageAllowance1;
    };
    extraInc = incIncSavings - sys.ageAllowanceThreshold;
    if( extraInc > 0.0 ){
      var withdraw = (sys.ageAllowanceTaper * extraInc );
      allowance = Math.max( sys.personalAllowance, allowance - withdraw );
    }
  };
  /*
  alert(  "incExlSavings = "+ incExlSavings + "allowance="+allowance );
  */
  taxableExlSavings = Math.max( 0.0, (incExlSavings - allowance) );
  allowance -= taxableExlSavings;
  if( allowance < 0.0 ){ 
          allowance = 0.0;
  }
  taxableSavings = Math.max( 0.0,  ad.savingsIncome - allowance );
  
  if( taxableExlSavings >= 0.0){
    resOther = calculateBands( sys.itRates, 
			       sys.itBands, 
			       taxableExlSavings );
  }
 
  if( taxableSavings >= 0.0){
    resSavings = calculateSavingsTax( sys.itRates, 
				      sys.itBands,
				      sys.savingsRate,
				      taxableSavings, 
				      taxableExlSavings );
  }
  /*
    alert( 'calculateIncomeTax allowance='+allowance+" taxableExlSavings="+
         taxableExlSavings +"\ntaxableSavings ="+
         taxableSavings + " resOther="+resOther+" resSavings="+
         resSavings + "ad.savingsIncome="+ad.savingsIncome + 
         "ad.earnings="+ad.earnings+"ad.pensions="+ad.pensions);
  */
  tax = resSavings + resOther;

  /* MCA still exists, as 10% tax credit, for over 68s !! CHECK !! 69 ||| */
  
  if (( adno == 0) && ( ad.married ) ){
    if( ad.age >= 75 ){
      tax = tax - sys.MCA2;
    } else if ( ad.age >= 68 ) {
      tax = tax - sys.MCA1;
    }
  }
  tax = Math.max( 0.0, tax );
  return tax;
}

function calculateIndirDifferences( res1,  res2 ){
        res2.beer = res2.beer - res1.beer;
        res2.wine = res2.wine - res1.wine; 
        res2.spirits = res2.spirits - res1.spirits
        res2.petrol = res2.petrol - res1.petrol;
        res2.fags =  res2.fags - res1.fags;
        res2.indirect = res2.indirect - res1.indirect;
        return res2;
}


/* Argument fam: a 'family' structure.
   Argument sys: a tax system.
   Result: a 'results' structure.
   This function does the main calculations
   for the Budget data, returning the results
   as the 'results' structure.
*/
function calculateOneSystem( fam, sys )
{
	var res = makeZeroResults();
	for( i = 0; i < fam.numAdults; i++ ){
		res.ni[i] = calculateNI( fam.ads[i], sys );
		res.incomeTax[i] = calculateIncomeTax( fam.ads[i], i, sys );
	}
	calculateIndirect( fam, sys, res );
	res.direct = res.ni[1]+res.ni[0]+res.incomeTax[0]+res.incomeTax[1]; 
	res.totalTax = res.direct+res.indirect;
        /*
	alert( 'res.total='+res.totalTax+' res.direct='+res.direct+' res.indirect '+res.indirect +
               '\nres.ni[1]='+res.ni[1]+
               '\nres.ni[0]='+res.ni[0]+
               '\nres.incomeTax[0]='+res.incomeTax[0]+
               '\nres.incomeTax[1]='+res.incomeTax[1]);
	*/
        return res;
}


/* Argument array: an array.
   Argument value: any value.
   Result: void.
   Appends value to the end of array.
   Used as a replacement for the
   Array.push() method, which
   doesn't work on some Mac
   browsers.
*/
function push( array, value )
{
  array[array.length] = value;
}
