// Copyright 2001-2004, Danger, Inc. All Rights Reserved.
// This file is subject to the Danger, Inc. Sample Code License,
// which is provided in the file SAMPLE_CODE_LICENSE.
// Copies are also available from http://developer.danger.com/

package com.example.DataStoreDemo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.Random;

import danger.internal.Date;
import danger.util.DEBUG;

/**
 * The HighScore object implements a single HighScore item. It includes fields
 * for the winning Name, the Score, the Level attained, and the Date the score
 * was won.
 * 
 * The primary purpose of this object is to store a HighScore event, and to
 * convert that data into a byte array so that it can be stored in the
 * DataStore. The DataStore only accepts byte[], so the methods toByteArray()
 * and fromByteArray() will serialize and deserialize the actual data.
 */
public class HighScore {

	private static final int VERSION_1 = 1;

	/**
	 * The four fields for each HighScore. They are private, but can be modified
	 * using the getter/setter routines.
	 */
	private String mName;

	private int mScore;

	private int mLevel;

	private Date mDate;

	// ------------------------------------------------------------------------
	/**
	 * Constructor for HighScore.
	 * 
	 * Since Strings are immutable, the name String passed in will be copied.
	 * This can generate a lot of garbage for an operation like sorting by name.
	 * The same is true of the Date objects.  Worth remembering, but in this
	 * case it's not a performance issue because of the 50 record guideline.
	 */
	public HighScore(String name, int score, int level, Date date) {
		mName = name; // makes a copy.
		mScore = score;
		mLevel = level;
		mDate = date; // makes a copy
	}

	/**
	 * Alternate constructor for HighScore object, initialized to the data being
	 * provided as a byte array. The fields are filled directly from the
	 * fromByteArray routine.  
	 */
	public HighScore(byte[] data) {
		fromByteArray(data);
	}

	// ------------------------------------------------------------------------
	/**
	 * The two routines toByteArray() and fromByteArray() must be kept in sync.
	 * Together, they serialize the data into a byte stream, that can be saved
	 * and read from the DataStore.
	 * 
	 * Each field is written and read back in the same order. The order can be
	 * changed, but only if a new version of the reader is added.  The first
	 * byte of the stream is the record version.
	 */

	/**
	 * Return a byte array for the HighScore data. This will serialize the data
	 * into a form that can be saved.
	 * 
	 * This uses the Java standard approach of a DataOutputStream passed the
	 * ByteArrayOutputStream type to convert the data.
	 */
	public byte[] toByteArray() {
		try {
			ByteArrayOutputStream byteS = new ByteArrayOutputStream();
			DataOutputStream dataStream = new DataOutputStream(byteS);

			// Version of this HighScore record. This allows different records
			// to be included with older ones.
			dataStream.writeByte(VERSION_1);

			dataStream.writeUTF(mName);
			dataStream.writeInt(mScore);
			dataStream.writeInt(mLevel);
			dataStream.writeInt(mDate.getUnixTimeGMT());
			dataStream.flush();
			return (byteS.toByteArray());
		} catch (Exception exception) {
			// The exceptions will catch spurious errors, and avoid crashing.
			// The writeUTF() will crash with NullPointerException, if passed
			// null, and this handler will avoid that. Similarly, if sDate is
			// null, this will catch that crash. IO errors are the other
			// possibility, which are unrecoverable, so returning null is
			// appropriate.
			DEBUG.p("DataStoreDemo: cannot convert HighScore to byte[].");
		}

		// If it failed to serialize, return null instead of an array.
		return null;
	}

	/**
	 * Take an existing HighScore object, 'this', and reset its internal fields
	 * to the data found in the byte[] passed in. This allows data stored in the
	 * DataStore to be deserialized into a working HighScore object.
	 */
	public void fromByteArray(byte[] data) {
		try {
			ByteArrayInputStream byteS = new ByteArrayInputStream(data);
			DataInputStream dataStream = new DataInputStream(byteS);

			// Version of the HighScore data. In case this ever changes,
			// this version will allow old and new to coexist peacefully.
			// Old high scores can be read by this variant, a newer structure
			// can be read here as well.
			byte version = dataStream.readByte();
			if (version == VERSION_1) {
				mName = dataStream.readUTF();
				mScore = dataStream.readInt();
				mLevel = dataStream.readInt();
				mDate = new Date(dataStream.readInt());
			} else {
				// The version might be unknown for the case of an older program
				// seeing a newer version of the data. Intialize a default
				// object for this case that will work, but be empty.
				mName = "";
				mScore = 0;
				mLevel = 0;
				mDate = new Date(0);
			}
		} catch (Exception exception) {
			// Only IOErrors are expected here, but handle any that might
			// happen, even by accident.
			DEBUG.p("DataStoreDemo: Failed to restore HighScore from byte[].");
		}
	}

	// ------------------------------------------------------------------------
	/**
	 * Getters and Setters for the object fields.
	 */

	/**
	 * Return the mDate.
	 */
	public Date getDate() {
		return mDate;
	}

	/**
	 * Save a new mDate.
	 */
	public void setDate(Date date) {
		mDate = date;
	}

	/**
	 * Return the mLevel.
	 */
	public int getLevel() {
		return mLevel;
	}

	/**
	 * Save a new mLevel.
	 */
	public void setLevel(int level) {
		mLevel = level;
	}

	/**
	 * Returns the mName.
	 */
	public String getName() {
		return mName;
	}

	/**
	 * Save a new mName.
	 */
	public void setName(String name) {
		mName = name;
	}

	/**
	 * Return the mScore.
	 */
	public int getScore() {
		return mScore;
	}

	/**
	 * Save a new mScore.
	 */
	public void setScore(int score) {
		mScore = score;
	}

	/**
	 * Just for debugging and demonstration purposes, this random number
	 * generator is created and seeded here.
	 */
	private static Random mRand = new Random();

	/**
	 * Make a new high score record, artificially in this case.
	 */
	public static HighScore fabricateHighScore(String[] names) {

		String name = names[mRand.nextInt(5)];
		int score = mRand.nextInt(75000);
		int level = mRand.nextInt(15) + 1;
		Date date = new Date(mRand.nextInt());

		return(new HighScore(name, score, level, date));
	}
}