package com.sbs.jpm.gwt.client;

import java.util.LinkedList;
import java.util.List;

import com.google.gwt.core.client.EntryPoint;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.fields.DataSourceIntegerField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.types.DSDataFormat;
import com.smartgwt.client.types.DSProtocol;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.widgets.Button;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;

public class TestCase extends HLayout implements EntryPoint {

	private TestDataSource dataSource;
	private ListGrid testGrid;
	private ListGridField testNameField;
	private DynamicForm updateTestForm;
	private TextItem textItem;
	private Button saveButton;
	private TestRecord testRecord;

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {

		dataSource = new TestDataSource();

		testNameField = new ListGridField(TestDataSource.TEST_NAME, "Test name");

		testGrid = new ListGrid();
		testGrid.setWidth(500);
		testGrid.setHeight(500);
		testGrid.setSelectionType(SelectionStyle.SINGLE);
		testGrid.setDataSource(dataSource);
		testGrid.setAutoFetchData(true);
		testGrid.setFields(testNameField);
		testGrid.addRecordClickHandler(new RecordClickHandler() {
			@Override
			public void onRecordClick(RecordClickEvent event) {
				//just a simple conversion to mimic the conversions from my application
				testRecord = dataSource.toTestRecord(testGrid
						.getSelectedRecord());
				updateTestForm.editRecord(dataSource
						.toListGridRecord(testRecord));
			}
		});

		textItem = new TextItem();
		textItem.setTitle("Test name");
		textItem.setName(TestDataSource.TEST_NAME);

		updateTestForm = new DynamicForm();
		updateTestForm.setAutoFocus(true);
		updateTestForm.setDataSource(dataSource);
		updateTestForm.setFields(textItem);

		saveButton = new Button("Save");
		saveButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				if (updateTestForm.validate()) {
					updateTestForm.submit();
				}
			}
		});

		setWidth100();
		setHeight100();
		addMember(testGrid);
		addMember(updateTestForm);
		addMember(saveButton);

		draw();
	}

	// the implementation of the GwtRpcDatasource
	public class TestDataSource extends GwtRpcDataSource {
		public static final String DS_UNIQUE_ID = "testDataSource";

		public static final String TEST_UNIQUE_ID = "testUniqueId";
		public static final String TEST_NAME = "testNameField";
		public static final String NESTED_DATA = "nestedDataField";

		private TestDataSource() {
			DataSourceIntegerField testUniqueId = new DataSourceIntegerField(
					TEST_UNIQUE_ID);
			testUniqueId.setPrimaryKey(true);
			testUniqueId.setHidden(true);

			DataSourceTextField testNameField = new DataSourceTextField(
					TEST_NAME, "Test");

			setID(DS_UNIQUE_ID);
			setFields(testUniqueId, testNameField);
		}

		protected void executeFetch(final String requestId, DSRequest request,
				final DSResponse response) {

			// just bilding the data locally for the test case
			List<TestRecord> testRecords = new LinkedList<TestRecord>();
			for (int i = 0; i < 3; i++) {
				TestRecord testRecord = new TestRecord(i, "test " + i);
				testRecords.add(testRecord);
			}

			//just a simple conversion from a shared object to a record/listGridRecord
			ListGridRecord[] list = new ListGridRecord[testRecords.size()];
			for (int i = 0; i < list.length; i++) {
				ListGridRecord record = toListGridRecord(testRecords.get(i));
				list[i] = record;
			}
			response.setData(list);
			processResponse(requestId, response);

		}

		protected void executeAdd(String requestId, DSRequest request,
				DSResponse response) {
		}

		protected void executeUpdate(final String requestId, DSRequest request,
				final DSResponse response) {
			System.out.println("updating the record");

		}

		protected void executeRemove(String requestId, DSRequest request,
				DSResponse response) {
		}

		public TestRecord toTestRecord(ListGridRecord listGridRecord) {
			TestRecord assignmentRecord = new TestRecord();
			assignmentRecord.setUniqueId(listGridRecord
					.getAttributeAsLong(TEST_UNIQUE_ID));
			assignmentRecord.setTestName(listGridRecord
					.getAttributeAsString(TEST_NAME));
			assignmentRecord.setNestedData((List<NestedRecord>) listGridRecord
					.getAttributeAsObject(NESTED_DATA));
			return assignmentRecord;
		}

		public ListGridRecord toListGridRecord(TestRecord testRecord) {
			ListGridRecord listGridRecord = new ListGridRecord();
			listGridRecord.setAttribute(TEST_UNIQUE_ID,
					testRecord.getUniqueId());
			listGridRecord.setAttribute(TEST_NAME, testRecord.getTestName());
			listGridRecord
					.setAttribute(NESTED_DATA, testRecord.getNestedData());
			return listGridRecord;
		}
	}

	// in the orginal project this class is placed in the shared package and is
	// implementing the IsSerialazible interface
	// because the objects constructed from this class are created in the server
	// side and sent to the client side via RPC
	// or vice-versa
	public class TestRecord {

		private long uniqueId;
		private String testName;
		
		//the object that causes the JS error. If i remove this embedded object it works fine
		private List<NestedRecord> nestedData;

		public TestRecord(long uniqueId, String testName) {
			this.uniqueId = uniqueId;
			this.testName = testName;
			
			//creating and adding the embedded data to the main data object
			nestedData = new LinkedList<NestedRecord>();
			for (int i = 0; i < 3; i++) {
				NestedRecord nestedRecord = new NestedRecord("nesterecord" + i);
				nestedData.add(nestedRecord);
			}
		}

		public TestRecord() {
		}

		public long getUniqueId() {
			return uniqueId;
		}

		public void setUniqueId(long uniqueId) {
			this.uniqueId = uniqueId;
		}

		public String getTestName() {
			return testName;
		}

		public void setTestName(String testName) {
			this.testName = testName;
		}

		public List<NestedRecord> getNestedData() {
			return nestedData;
		}

		public void setNestedData(List<NestedRecord> nestedData) {
			this.nestedData = nestedData;
		}
	}

	// just a simple case that is used as nested data in TestRecord class
	// By adding a list of this kind of objects in the TestRecord class will cause
	// the JS exception to be thrown.
	public class NestedRecord {

		private String nestedRecord;

		public NestedRecord(String nestedRecord) {
			this.nestedRecord = nestedRecord;
		}

		public NestedRecord() {
		}
	}

	// an abstract class for a RPC datasource
	public abstract class GwtRpcDataSource extends DataSource {

		public GwtRpcDataSource() {
			setDataProtocol(DSProtocol.CLIENTCUSTOM);
			setDataFormat(DSDataFormat.CUSTOM);
			setClientOnly(false);
		}

		protected Object transformRequest(DSRequest request) {
			String requestId = request.getRequestId();
			DSResponse response = new DSResponse();
			response.setAttribute("clientContext",
					request.getAttributeAsObject("clientContext"));
			response.setStatus(0);
			switch (request.getOperationType()) {
			case FETCH:
				executeFetch(requestId, request, response);
				break;
			case ADD:
				executeAdd(requestId, request, response);
				break;
			case UPDATE:
				executeUpdate(requestId, request, response);
				break;
			case REMOVE:
				executeRemove(requestId, request, response);
				break;
			default:
				break;
			}
			return request.getData();
		}

		protected abstract void executeFetch(String requestId,
				DSRequest request, DSResponse response);

		protected abstract void executeAdd(String requestId, DSRequest request,
				DSResponse response);

		protected abstract void executeUpdate(String requestId,
				DSRequest request, DSResponse response);

		protected abstract void executeRemove(String requestId,
				DSRequest request, DSResponse response);

	}
}
