/** TaxDeclarationDemo.java --- objects and classes in Java
 * @author Uwe Assmann
 * @version 1.1
 * @date 2015-04-08
 */

import java.util.*;

/**
   Simple main program, is called by the run-time system (operating system). 
   Main operation "main" is a class operation, i.e., exists only once.
 */
public class TaxDeclarationDemo {
    // Counts all tax declarations ever built.
    public static int globalCounter = 0;
    // all tax payers
    public static TaxPayer taxPayers[] = new TaxPayer[10]; 
    // only one tax authority
    public static TaxAuthority taxOffice = new TaxAuthority(); 

    /* increase the global counter of TaxDeclarationDemo */
    public static int getGlobalCounter() {
	globalCounter++;
	return globalCounter;
    }
    // Main program is a "Klassenoperation".  
    public static void main(String args[]) {
	//// (1) Create the objects.
	// In Germany, tax identification numbers are
	// handed out by the Finanzamt, once in your life
	TaxPayer uweAssmann = new TaxPayer("Uwe Aßmann",104208490);
	taxPayers[0] = uweAssmann;
	System.out.println("Tax payer 1: "+uweAssmann.toString());

	//// new tax payer: Lady Miller
	TaxPayer victoriaMiller = new TaxPayer("Victoria Miller",490839201);
	taxPayers[1] = victoriaMiller;
	System.out.println("Tax payer 2: "+victoriaMiller.toString());

	//// new tax payer: a moonlighter
	TaxPayer johnSilver = new Moonlighter("John Silver",490839201);
	taxPayers[2] = johnSilver;
	System.out.println("Tax payer 3: "+johnSilver.toString());
	// moonlighters do not pay tax, so the tax declaration for them will be
	// wrong!

	// (2) Construct the object net
	// Implicitly link tax declaration of to tax payer 
	TaxDeclaration uweTax = new TaxDeclaration(2015,"Einkommenssteuererklaerung",uweAssmann);
	TaxDeclaration vicciTax = new TaxDeclaration(2015,"Einkommenssteuererklaerung",victoriaMiller);
	TaxDeclaration silverTax = new TaxDeclaration(2015,"Einkommenssteuererklaerung",johnSilver);

	// (3) Do the real work: edit the tax declaration the first time...
	for (int taxpayer = 0; taxpayer < 3; taxpayer++) {
	    taxPayers[taxpayer] // this delivers a TaxPayer
		.getTaxDeclaration()  // this delivers his current tax declaration
		.edit();  // and this edits it.
	} 

	// now work for some time during the year
	victoriaMiller.marry();
	victoriaMiller.work(2050);
	uweAssmann.work(2050);
	johnSilver.work(2050);
	uweAssmann.work(2060);
	johnSilver.marry();
	victoriaMiller.work(2070);

	// (3b) Edit again the tax declaration
	for (int taxpayer = 0; taxpayer < 3; taxpayer++) {
	    taxPayers[taxpayer] // this delivers a TaxPayer
		.getTaxDeclaration()  // this delivers his current tax declaration
		.edit();  // and this edits it.
	} 

	// (4) submit the tax declarations to the singleton TaxAuthority
	for (int taxpayer = 0; taxpayer < 3; taxpayer++) {
	    taxOffice.submit(
			     taxPayers[taxpayer] // this delivers a TaxPayer
			     .getTaxDeclaration()  // this delivers his current tax declaration
			     );

	}
    }
}

/** 
 * Base class TaxAuthority.
 */
class TaxAuthority {
    private String name;            // internal attribute name; not visible
    private TaxDeclaration taxDeclaration;  // current tax declaration

    // Attribute query: read the name of a TaxPayer
    String getName() { return name; }
    // Attribute modifier: change the name of a TaxPayer
    void setName(String a_name) { name = a_name; }


    // Constuctors; are called by call to the allocator new:
    // new TaxAuthority(..); 
    public TaxAuthority() { }

    public void submit(TaxDeclaration taxDecl) {
	System.out.println("Tax Office processes new tax declaration of tax payer " + taxDecl.getTaxPayer().toString());
	System.out.println("Salary: " + taxDecl.getDeclaredTax());
	// Swedish tax rules: only three classes of income tax
	if (taxDecl.getDeclaredTax() >= 3000) {
	    if (taxDecl.getDeclaredTax() >= 5000) {
		System.out.println("Tax: 30% = " + taxDecl.getDeclaredTax()*30/100);
	    } else {
		System.out.println("Tax: 20% = " + taxDecl.getDeclaredTax()*20/100);
	    }
	} else {
	    System.out.println("Tax: 10% = " + taxDecl.getDeclaredTax()/10);
	}
    }
}

/** 
 * Base class TaxPayer.
 */

class TaxPayer {
    protected String name;            // internal attribute name; not visible
    protected int taxId;              // number of the tax payer
    protected TaxDeclaration taxDeclaration;  // current tax declaration
    protected String maritalStatus;   // marital status 
    protected int yearlySalary = 0;   // salary sum over the year

    // Constuctors; are called by call to the allocator new:
    // new TaxPayer(..); 
    public TaxPayer() { maritalStatus = "celibataire"; }
    public TaxPayer(String a_name) { setName(a_name); maritalStatus = "celibataire"; }
    public TaxPayer(String a_name, int a_taxId) { setName(a_name);  setTaxId(a_taxId); maritalStatus = "celibataire"; }

    // Attribute query: read the name of a TaxPayer
    String getName() { return name; }
    // Attribute modifier: change the name of a TaxPayer
    void setName(String a_name) { name = a_name; }

    // Attribute query: read the taxId of a TaxPayer
    int getTaxId() { return taxId; }
    // Attribute modifier: change the name of a TaxPayer
    void setTaxId(int a_taxId) { taxId = a_taxId; }

    // Attribute query: read the current taxDeclaration of a TaxPayer
    TaxDeclaration getTaxDeclaration() { return taxDeclaration; }
    // Attribute modifier: change the name of a TaxPayer
    void setTaxDeclaration(TaxDeclaration a_taxDeclaration) { taxDeclaration = a_taxDeclaration; }

    // Attribute query: read the current maritalStatus of a TaxPayer
    String getMaritalStatus() { return maritalStatus; }
    // Attribute modifier: change the maritalStatus of a TaxPayer
    void setMaritalStatus(String a_maritalStatus) { maritalStatus = a_maritalStatus; }
    void marry() { maritalStatus = "married"; }

    // increase the yearly income by some salary
    void work(int money) { yearlySalary += money; }

    public String toString() {
	return getName() + " " + getTaxId() + " is " + maritalStatus;
    }

}

class Moonlighter extends TaxPayer {
    public Moonlighter() { maritalStatus = "celibataire"; }
    public Moonlighter(String a_name) { setName(a_name); maritalStatus = "celibataire"; }
    public Moonlighter(String a_name, int a_taxId) { setName(a_name);  
	setTaxId(a_taxId); 
	maritalStatus = "celibataire"; 
    }
    void work(int money) { earn(money); }
    void earn(int money) { 
	// moonlighters do not work, they only earn money
    }
}

class TaxDeclaration {
    int year;           // year of taxation
    int number;         // number of the tax declaration
    int version;        // version of the tax declaration; is increased every
		        // time the tax declaration is loaded and modified
    TaxPayer taxPayer;  // person who is taxed
    int declaredTax;    // this is the sum the tax payer will declare to the tax
			// authority


    // Constructors
    public TaxDeclaration(int a_year, String a_name, TaxPayer a_taxPayer) { 
	year = a_year; 
	// version is set to 1, because it is the first edit
	version = 1;
	taxPayer = a_taxPayer;
	// do the backlink to taxPayer from TaxDeclaration
	a_taxPayer.setTaxDeclaration(this);
    }

    // get the tax declaration number.
    int getNumber() { return number; }
    // setNumber sets the tax declaration's number by querying the global counter
    public void setNumber() { 
	number = TaxDeclarationDemo.getGlobalCounter(); 
    }

    // Attribute query: read the current taxDeclaration of a TaxPayer
    TaxPayer getTaxPayer() { return taxPayer; }
    // Attribute modifier: change the name of a TaxPayer
    void setTaxPayer(TaxPayer a_taxPayer) { taxPayer = a_taxPayer; }

    // Attribute query: read the declared tax of a TaxPayer
    int getDeclaredTax() { return declaredTax; }
    // Attribute modifier: change the DeclaredTax of a TaxPayer
    void setDeclaredTax(int a_DeclaredTax) { declaredTax = a_DeclaredTax; }

    /* Editing the current tax declaration.  This function will be extended
       later. For the moment, it just increases the version. */
    public void edit() {
	int oldversion = version;
	version++;
	System.out.println("Edit new version of taxpayer " + this.getTaxPayer().toString() 
			   + oldversion + " -> " + version);
	declaredTax = this.getTaxPayer().yearlySalary;
    }
}
