package com.sandbox.client;

import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.TimeZone;
import com.google.gwt.user.client.ui.RootPanel;
import com.smartgwt.client.data.AdvancedCriteria;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.types.AutoFitWidthApproach;
import com.smartgwt.client.types.RecordComponentPoolingMode;
import com.smartgwt.client.util.DateDisplayFormatter;
import com.smartgwt.client.util.DateFormatStringFormatter;
import com.smartgwt.client.util.DateParser;
import com.smartgwt.client.util.DateUtil;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.FilterBuilder;
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.layout.Layout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.layout.VStack;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Sandbox implements EntryPoint {

    public static DateDisplayFormatter DATE_TIME_DISPLAY_FORMATTER;
    public static DateParser DATE_PARSER;
    private static TimeZone USER_TIMEZONE;
    private static int USER_RFC_TIMEZONE_OFFSET_IN_MINUTES = Integer.MAX_VALUE;

    public static final String SYS_ID = "sysId";
    public static final String SYS_CREATED_BY = "sysCreatedBy";
    public static final String SYS_CREATED_ON = "sysCreatedOn";
    public static final String SYS_CREATED_AT = "sysCreatedAt";
    public static final String VERSION = "version";
    public static final String COUNT = "count";
    public static final String NAME = "name";
    public static final String TYPE = "type";
    public static final String OK = "ok";

    public static final String DATE_FORMAT = "yyyy-MM-dd";
    public static final String DATE_FORMAT_ISO = "yyyyMMdd";
    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_TIME_FORMAT_Z = "yyyy-MM-dd HH:mm:ss Z";
    public static final String DATE_TIME_FORMAT_ISO8601_Z = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ";
    public static final String DATE_TIME_FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss.SSS";

    private DataSource dataSource;
    private FilterBuilder filterBuilder;
    private ListGrid listGrid;

    public void onModuleLoad() {

        // for this sample set timezone offset as if Eastern, hence the 4
        // we normally get this info from the server for the specific user.
        setUserRfcTimeZoneOffsetInMinutes(4 * 60);

        DATE_TIME_DISPLAY_FORMATTER = new DateDisplayFormatter() {
            @Override
            public String format(Date date) {
                DateTimeFormat dtf = DateTimeFormat.getFormat(DATE_TIME_FORMAT_Z);
                String dateStr = dtf.format(date, USER_TIMEZONE);
                GWT.log("Normal - Formatted date: " + date + " -> " + dateStr);
                return dateStr;
            }
        };

        // setup some of our date time global formatting
        DateUtil.setNormalDatetimeDisplayFormatter(DATE_TIME_DISPLAY_FORMATTER);
        DateUtil.setShortDatetimeDisplayFormatter(DATE_TIME_DISPLAY_FORMATTER);
        DateUtil.setDefaultDateSeparator("-");
        DateUtil.setDateInputFormat("YMD");
        DateFormatStringFormatter dateFormatter = new DateFormatStringFormatter(DATE_FORMAT);
        DateUtil.setShortDateDisplayFormatter(dateFormatter);
        DateUtil.setNormalDateDisplayFormatter(dateFormatter);
        DateUtil.setDateParser(getDateParser());

        dataSource = DataSource.get("stuff");
        dataSource.setCacheAllData(Boolean.TRUE);
        dataSource.setCacheData(getRecords());

        RootPanel.get("mainArea").add(getLayout());
    }

    public static Date parseDate(String dateString) {
        if (dateString == null) return null;

        dateString = scrubDateString(dateString);
        Date date = null;
        DateTimeFormat formatter = null;
        // Attempt to parse the date using length matching algorithm
        try {
            formatter = getLengthMatchingDateFormatter(dateString);
            date = formatter.parse(dateString);
            GWT.log("Parsed String (" + dateString + ")  using pattern (" + formatter.getPattern() + ") into Date ("
                + date + ")");
            return date;
        } catch (Exception e) {
            GWT.log("FAILED to parsed: " + dateString);
        }

        if (date == null) {
            try {
                // try ISO 8601 format
                formatter = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.ISO_8601);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date == null) {
            try {
                // try ISO 8601 format with timezone
                formatter = DateTimeFormat.getFormat(DATE_TIME_FORMAT_ISO8601_Z);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date == null) {
            try {
                // try ISO 8601 format without timezone
                formatter = DateTimeFormat.getFormat(DATE_TIME_FORMAT_ISO8601);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date == null) {
            try {
                // try Opswise format with timezone
                formatter = DateTimeFormat.getFormat(DATE_TIME_FORMAT_Z);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date == null) {
            try {
                // try Opswise format without timezone
                formatter = DateTimeFormat.getFormat(DATE_TIME_FORMAT);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date == null) {
            try {
                // try just date
                formatter = DateTimeFormat.getFormat(DATE_FORMAT);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date == null) {
            try {
                // try just date
                formatter = DateTimeFormat.getFormat(DATE_FORMAT_ISO);
                GWT.log("Attempting to parse: using format: " + formatter.getPattern());
                date = formatter.parse(dateString);
            } catch (Exception e) {}
        }

        if (date != null) {
            GWT.log("Parsed String (" + dateString + ")  using pattern (" + formatter.getPattern() + ") into Date ("
                + date + ")");
        }
        return date;
    }

    public static String scrubDateString(String dateString) {
        if (dateString == null) return dateString;
        dateString = dateString.trim();
        int len = dateString.length();
        if (len == 23 && dateString.charAt(10) == 'T') return dateString + "+00:00";
        return dateString;
    }

    public static DateTimeFormat getLengthMatchingDateFormatter(String dateString) {
        if (dateString == null) return null;
        int len = dateString.length();
        if (len > 11 && dateString.charAt(10) == 'T') {
            if (len == 23) {
                return DateTimeFormat.getFormat(DATE_TIME_FORMAT_ISO8601);
            }
            return DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.ISO_8601);
        }
        String formatPattern = getLengthMatchingDatePattern(dateString);
        return DateTimeFormat.getFormat(formatPattern);

    }

    public static DateParser getDateParser() {
        if (DATE_PARSER == null) {
            DATE_PARSER = new DateParser() {
                @Override
                public Date parse(String dateString) {
                    return parseDate(dateString);
                }
            };
        }
        return DATE_PARSER;
    }

    /**
     * Set the user time zone based upon the user time zone offset <br>
     * <br>
     * In order to conform with the POSIX style, those zones beginning with "Etc/GMT" have their
     * sign reversed from what most people expect. In this style, zones west of GMT have a positive
     * sign and those east have a negative sign.
     * 
     * @param userTimeZoneOffsetInMinutes
     */
    public static void setUserRfcTimeZoneOffsetInMinutes(int userTimeZoneOffsetInMinutes) {
        if (userTimeZoneOffsetInMinutes == USER_RFC_TIMEZONE_OFFSET_IN_MINUTES) return;
        USER_RFC_TIMEZONE_OFFSET_IN_MINUTES = userTimeZoneOffsetInMinutes;

        // Returns the RFC representation of the time zone name for the given date.
        // To be consistent with JDK/Javascript API, west of Greenwich will be positive.
        USER_TIMEZONE = TimeZone.createTimeZone(USER_RFC_TIMEZONE_OFFSET_IN_MINUTES);
        int absoluteOffsetInMinutess = userTimeZoneOffsetInMinutes < 0 ? (-1 * userTimeZoneOffsetInMinutes)
            : userTimeZoneOffsetInMinutes;
        int hours = absoluteOffsetInMinutess / 60;
        int minutes = absoluteOffsetInMinutess - (hours * 60);
        String hourStr = hours < 10 ? ("0" + hours) : Integer.toString(hours);
        String minStr = minutes < 10 ? ("0" + minutes) : Integer.toString(minutes);
        String prefix = userTimeZoneOffsetInMinutes < 0 ? "+" : "-";
        String defaultDisplayTimeZone = prefix + hourStr + ":" + minStr;
        DateUtil.setDefaultDisplayTimezone(defaultDisplayTimeZone);
        GWT.log("Setting user time zone offst in minutes to " + USER_RFC_TIMEZONE_OFFSET_IN_MINUTES
            + " and TimeZone to: " + USER_TIMEZONE.getID() + " (" + USER_TIMEZONE.getStandardOffset()
            + ") - setDefaultDisplayTimezone(" + defaultDisplayTimeZone + ")");
    }

    public static String getLengthMatchingDatePattern(String dateString) {
        if (dateString == null) return null;
        int len = dateString.length();
        if (len == DATE_TIME_FORMAT_Z.length() + 4) return DATE_TIME_FORMAT_Z;
        if (len == DATE_FORMAT.length()) return DATE_FORMAT;
        if (len == DATE_FORMAT_ISO.length()) return DATE_FORMAT_ISO;
        if (len == DATE_TIME_FORMAT.length()) return DATE_TIME_FORMAT;
        return DATE_TIME_FORMAT_Z;
    }

    public Layout getLayout() {
        VLayout layout = new VLayout();

        layout.setMargin(10);
        layout.setWidth100();
        layout.setHeight100();

        Label label = new Label();
        label.setHeight(10);
        label.setWidth100();
        label.setContents("Showing items from Sandbox custom ds");

        filterBuilder = new FilterBuilder();
        filterBuilder.setDataSource(dataSource);

        listGrid = new ListGrid();
        listGrid.setWidth100();
        listGrid.setHeight(500);

        listGrid.setAutoFitFieldsFillViewport(Boolean.TRUE);
        listGrid.setAutoFitWidthApproach(AutoFitWidthApproach.BOTH);
        listGrid.setAutoFitFieldWidths(Boolean.TRUE);
        listGrid.setRecordComponentPoolingMode(RecordComponentPoolingMode.RECYCLE);
        listGrid.setAlternateRecordStyles(true);
        listGrid.setAutoFetchData(true);
        listGrid.setShowRollOver(Boolean.FALSE);

        listGrid.setDataSource(dataSource);
        listGrid.setAutoFetchData(true);

        ListGridField idField = new ListGridField(SYS_ID, 100);
        ListGridField nameField = new ListGridField(NAME, 300);
        ListGridField typeField = new ListGridField(TYPE, 100);
        ListGridField versionField = new ListGridField(VERSION);
        ListGridField countField = new ListGridField(COUNT);
        ListGridField createdByField = new ListGridField(SYS_CREATED_BY);
        ListGridField createdOnField = new ListGridField(SYS_CREATED_ON);
        ListGridField createdAtField = new ListGridField(SYS_CREATED_AT);
        ListGridField okField = new ListGridField(OK);

        listGrid.setFields(idField, nameField, typeField, okField, createdByField, createdOnField, createdAtField,
            countField, versionField);

        listGrid.setSortField(SYS_ID);

        IButton filterButton = new IButton("Filter");
        filterButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                AdvancedCriteria criteria = filterBuilder.getCriteria();
                String filter = criteria.toJSON();
                GWT.log("Filter Criteria: " + filter);
                listGrid.filterData(criteria);
            }
        });

        VStack vStack = new VStack(10);
        vStack.addMember(label);
        vStack.addMember(filterBuilder);
        vStack.addMember(filterButton);
        vStack.addMember(listGrid);

        layout.addMember(vStack);
        layout.draw();

        return layout;
    }

    private Record[] getRecords() {
        Record[] records = new Record[5];
        for (int i = 0; i < records.length; i++) {
            ListGridRecord lgr = new ListGridRecord();
            lgr.setAttribute(SYS_ID, "Key-" + i);
            lgr.setAttribute(NAME, "Name " + i);
            lgr.setAttribute(TYPE, i + 1);
            lgr.setAttribute(COUNT, i + 100);
            lgr.setAttribute(VERSION, 1);
            lgr.setAttribute(OK, Boolean.TRUE);
            lgr.setAttribute(SYS_CREATED_BY, "John Smith");
            lgr.setAttribute(SYS_CREATED_ON, new Date());
            lgr.setAttribute(SYS_CREATED_AT, new Date());
            records[i] = lgr;
        }
        return records;

    }
}
