package generated.javacard.mondex.purse;

import javacard.framework.*;

/**
 * Generated Smartcard for Mondex model
 */
public class Purse extends SimpleComm {

	public short balance;
	public Symmkey sesskey;
	public byte state;
	public PurseData data;
	public PayDetails pdAuth;
	public ListOfPayDetails exlog;

	/**
	 * Constructor
	 */
	public Purse() {
		balance = 0;
		sesskey = new Symmkey();
		state = State.APPLET_UNINITIALIZED;
		data = new PurseData();
		pdAuth = new PayDetails();
		exlog = new ListOfPayDetails((short) 10);
	}

	public void appletInitialization(byte[] buf, short offset) {
		if (state != State.APPLET_UNINITIALIZED)
			stop();
		Coding.getInstance().decodePurse(buf, offset, this);
	}

	/**
	 * process-method, is called from SimpleCommPurse when a message arrives
	 * 
	 * @param inmsg message to process
	 */
	public void process(Message inmsg) {
		if (state == State.APPLET_UNINITIALIZED)
			stop();
		switch (inmsg.getCode()) {
			case Code.GETDATA :
				processGetData((GetData) inmsg);
				break;
			case Code.STARTFROM :
				processStartFrom((StartFrom) inmsg);
				break;
			case Code.STARTTO :
				processStartTo((StartTo) inmsg);
				break;
			case Code.REQ :
				processReq((Req) inmsg);
				break;
			case Code.VAL :
				processVal((Val) inmsg);
				break;
			case Code.ACK :
				processAck((Ack) inmsg);
				break;
			case Code.GETBALANCE :
				processGetBalance((GetBalance) inmsg);
				break;
			//Unknown Message
			default :
				stop();
		}
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processGetData(GetData inmsg) {
		ABORT();
		sendMsg(Store.newResGetData(data));
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processStartFrom(StartFrom inmsg) {
		short value = inmsg.value;
		PurseData dataTo = inmsg.dataTo;
		if (full()) {
			stop();
		} else {
			if (state == State.IDLE) {
				boolean b = CheckValueSeqnoFrom(value, dataTo.nextSeqNo);
				if ((b == true && (!Arrays.equals(dataTo.name, data.name)))) {
					pdAuth.from.copy(data);
					pdAuth.to.copy(dataTo);
					pdAuth.value = value;
					data.nextSeqNo++;
					state = State.EPR;
					Msgcontent encmess = Store.newMsgcontent(Constants.STARTTO,
							pdAuth);
					EncDataSymm enc = EncDataSymm.encrypt(sesskey, encmess);
					sendMsg(Store.newStartTo(enc));
				} else {
					stop();
				}
			} else {
				ABORT();
				stop();
			}
		}
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processStartTo(StartTo inmsg) {
		EncDataSymm enc = inmsg.encmess;
		if (full()) {
			stop();
		} else {
			Msgcontent msg = (Msgcontent) (EncDataSymm.decrypt(sesskey, enc));
			if (((state == State.IDLE && msg.msgflag == Constants.STARTTO) && msg.pd.to
					.equals(data))) {
				boolean b = CheckValueSeqnoTo(msg.pd.value, msg.pd.to.nextSeqNo);
				if ((b == true && (!Arrays.equals(msg.pd.from.name, data.name)))) {
					pdAuth.from.copy(msg.pd.from);
					pdAuth.to.copy(data);
					pdAuth.value = msg.pd.value;
					data.nextSeqNo++;
					state = State.EPV;
					msg = Store.newMsgcontent(Constants.REQ, pdAuth);
					enc = EncDataSymm.encrypt(sesskey, msg);
					sendMsg(Store.newReq(enc));
				} else {
					stop();
				}
			} else {
				ABORT();
				stop();
			}
		}
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processReq(Req inmsg) {
		EncDataSymm enc = inmsg.encmess;
		if (exlog.hasFree()) {
			Msgcontent msg = (Msgcontent) (EncDataSymm.decrypt(sesskey, enc));
			if (((state == State.EPR && msg.msgflag == Constants.REQ) && msg.pd
					.equals(pdAuth))) {
				balance = Math.minus(balance, pdAuth.value);
				state = State.EPA;
				msg = Store.newMsgcontent(Constants.VAL, pdAuth);
				enc = EncDataSymm.encrypt(sesskey, msg);
				sendMsg(Store.newVal(enc));
			} else {
				ABORT();
				stop();
			}
		} else {
			stop();
		}
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processVal(Val inmsg) {
		EncDataSymm enc = inmsg.encmess;
		if (exlog.hasFree()) {
			Msgcontent msg = (Msgcontent) (EncDataSymm.decrypt(sesskey, enc));
			if (((state == State.EPV && msg.msgflag == Constants.VAL) && msg.pd
					.equals(pdAuth))) {
				balance = Math.plus(balance, pdAuth.value);
				state = State.IDLE;
				msg = Store.newMsgcontent(Constants.ACK, pdAuth);
				enc = EncDataSymm.encrypt(sesskey, msg);
				sendMsg(Store.newAck(enc));
			} else {
				ABORT();
				stop();
			}
		} else {
			stop();
		}
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processAck(Ack inmsg) {
		EncDataSymm enc = inmsg.encmess;
		if (exlog.hasFree()) {
			Msgcontent msg = (Msgcontent) (EncDataSymm.decrypt(sesskey, enc));
			if (((state == State.EPA && msg.msgflag == Constants.ACK) && msg.pd
					.equals(pdAuth))) {
				state = State.IDLE;
			} else {
				ABORT();
				stop();
			}
		} else {
			stop();
		}
	}

	/**
	 * This method is called, when a -message arrives.
	 * Parameters are a result of the activity diagrams
	 */
	public void processGetBalance(GetBalance inmsg) {
		sendMsg(Store.newResGetBalance(balance));
	}

	void ABORT() {

		if (exlog.hasFree()) {
			if ((state == State.EPV || state == State.EPA)) {
				exlog.add(pdAuth);
				state = State.IDLE;
			} else {
				state = State.IDLE;
			}
		} else {
		}
	}

	boolean CheckValueSeqnoFrom(short value, short seqno) {
		boolean result;
		if (((seqno < 0) || (value <= 0))) {
			result = false;
			return result;
		} else {
			if ((balance < value)) {
				result = false;
				return result;
			} else {
				result = true;
				return result;
			}
		}
	}

	boolean CheckValueSeqnoTo(short value, short seqno) {
		boolean result;
		if (((seqno < 0) || (value <= 0))) {
			result = false;
			return result;
		} else {
			if (Math.overflowsPlus(value, balance)) {
				result = false;
				return result;
			} else {
				result = true;
				return result;
			}
		}
	}

	boolean full() {
		boolean result;
		if ((exlog.hasFree() && (data.nextSeqNo < 32767))) {
			result = false;
			return result;
		} else {
			result = true;
			return result;
		}
	}

}