/*
 * Copyright 2009 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.appengine.demos.taskengine.client;

import com.google.appengine.demos.taskengine.client.TaskList.TaskRow;
import com.google.appengine.demos.taskengine.shared.Task;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 * 
 * 'Task Engine' is a mobile sample App that demonstrates using GWT at a lower
 * level and closer to the underlying DOM.
 * 
 * If your Application is simple enough, you may be able to get by without
 * leveraging GWT's built in widgets and just use the core DOM API. For smaller
 * Apps, this may buy you some savings in code size. In the end however, there
 * is a trade off with regards to code complexity and managability. The more
 * complex the App is, the more you may find yourself re-inventing stuff that
 * GWT already has built in. Applications like those more often than not will
 * benefit all around (size and development effort) from using GWT's built in
 * Widgets.
 */
public class Tasks implements EntryPoint {
  /**
   * This class provides Application level controls like page switching, and
   * exposes the RPC service. We pass an instance of this object around so that
   * the various components of our App can have access to the functionality.
   */
  public class Controller {

    /**
     * Adds a new task to the task list UI and persist it to the server.
     * 
     * @param task
     */
    public void addNewTask(Task task) {
      persistTask(taskList.addTaskToUi(task));
      goToTaskList();
    }

    /**
     * Deletes all tasks from the server that have been checked off in the UI as
     * completed.
     */
    public void deleteCompletedTasks() {
      String[] tasksToDelete = taskList.getCompletedTaskIdsAndMoveToPending();
      if (tasksToDelete.length > 0) {
        boolean shouldDelete = DomUtils.getWindow().confirm(
            "Delete Completed Tasks?");
        if (shouldDelete) {
          api.deleteTasks(tasksToDelete, new AsyncCallback<String>() {

            public void onFailure(Throwable caught) {
              // This does not change the UI. So the user should still see the
              // row as unpersisted.
              taskList.movePendingTasksBackToCompleted();
            }

            public void onSuccess(String result) {
              if (result == null) {
                onFailure(null);
              }
              taskList.confirmDeletion();
            }

          });
        }
      }
    }

    /**
     * Transitions to the {@link TaskDetails} page.
     */
    public void goToTaskDetails() {
      uiPages.doPageTransition(taskDetails.getPageIndex());
    }

    /**
     * Transitions to the {@link TaskList} page.
     */
    public void goToTaskList() {
      uiPages.doPageTransition(taskList.getPageIndex());
    }

    /**
     * Loads a task in the TaskDetails page.
     * 
     * @param task the {@link Task} for the task to be loaded.
     */
    public void loadTask(Task task) {
      taskDetails.view(task);
    }

    /**
     * Saves a {@link Task} for a given {@link TaskRow} to the server. This is
     * used to both update existing tasks, and to save new ones. It updates the
     * UI for the TaskRow after getting a response from the server.
     * 
     * @param row the {@link TaskRow} for the task we want to save.
     */
    public void persistTask(final TaskRow row) {
      api.persistTask(row.getTaskData(), new AsyncCallback<String>() {

        public void onFailure(Throwable caught) {
          // Let the user know that persistence failed.
          // try again later or something
          DomUtils.getWindow().alert(
              "Failed to save task to server. Try so re-save it.");
        }

        public void onSuccess(String result) {
          if (result == null) {
            onFailure(null);
          } else {
            // mark the Task row as persisted
            row.setRowAsPersisted(result);
          }
        }

      });
    }

    /**
     * Updates the task information on the server and transitions back to the
     * {@link TaskList}.
     * 
     * @param task the {@link Task} for the updated task
     * @param oldPriority the old value for the priority of the task
     */
    public void updateTask(Task task, int oldPriority) {
      persistTask(taskList.updateTask(task, oldPriority));
      goToTaskList();
    }
  }

  /**
   * Our resources used in the sample.
   * 
   * {@link ControlBar.Resources} is an {@link ImmutableResourceBundle} (IRB).
   * IRB allows us to have a programmatic interface with static resources used
   * in this sample, like CSS styles and Images. Images specified here (or in
   * the inheritance chain) are automatically combined into a single sprite,
   * with corresponding CSS automatically generated to display each individual
   * image piece through cropping.
   * 
   */
  public interface Resources extends TaskList.Resources, TaskDetails.Resources {
  }

  private final TasksApiAsync api = GWT.create(TasksApi.class);;
  private final Resources resources = GWT.create(Resources.class);
  private TaskDetails taskDetails;
  private TaskList taskList;
  private PageTransitionPanel uiPages;

  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    // Inject styles into the page
    StyleInjector.injectAtEnd(resources.taskDetailsCss().getText()
        + resources.taskListCss().getText()
        + resources.controlBarCss().getText()
        + resources.labelMatrixCss().getText());

    // Our main Widget and container of our UI pages
    uiPages = new PageTransitionPanel(Document.get().getBody());
    // The object passed around that controls the main functionality of the app.
    Controller controller = new Controller();

    // Make UI Pages
    ControlBar.Controls taskListControls = TaskList.createControls(controller,
        resources);
    taskList = new TaskList(uiPages, taskListControls, controller, resources);

    ControlBar.Controls taskDetailsControls = TaskDetails.createControls(
        controller, resources);
    taskDetails = new TaskDetails(uiPages, taskDetailsControls, controller,
        resources);

    // Do initial load of tasks wait
    loadEntireTaskList();

    // Do our initial page sizing.
    uiPages.doResize();

    // We queue up a deferred resize as a hack to work around the fact that
    // certain mobile browsers fail to repaint after the above initial resize
    // happens and leave you with a blank screen. If we only do a deferred
    // resize, we see the screen flicker, so we do both the synchronous resize
    // above, and this deferred one.
    DeferredCommand.defer(new DeferredCommand() {
      @Override
      public void onExecute() {
        uiPages.doResize();
      }
    }, 100);
  }

  /**
   * Fetches all tasks from the server.
   */
  private void loadEntireTaskList() {
    // Set a timeout in case the call to the backend takes an unbounded amount
    // of time (e.g. network failure). We simply ask them to try signing in
    // again.
    DeferredCommand.defer(new DeferredCommand() {
      @Override
      public void onExecute() {
        if (!taskList.isLoggedIn()) {
          taskList.notifyNotLoggedIn(null);
        }
      }
    }, 5000);

    // Fetch the list of tasks
    api.getTaskList(new AsyncCallback<Task[]>() {

      public void onFailure(Throwable caught) {
        if (caught == null) {
          // RPC may have returned success, but the user isn't logged in!
          // So we Fetch SignIn URL.
          api.getLoginUrl(new AsyncCallback<String>() {

            public void onFailure(Throwable caught) {
              taskList.notifyNotLoggedIn(null);
            }

            public void onSuccess(String loginUrl) {
              taskList.notifyNotLoggedIn(loginUrl);
            }

          });
        } else {
          // We had a serialized exception. User need not be aware of this.
          // For debugging purposes we may want to alert this.
          // DomUtils.getWindow().alert(caught.getMessage());
          taskList.notifyNotLoggedIn(null);
        }
      }

      public void onSuccess(Task[] tasks) {
        if (tasks == null) {
          onFailure(null);
        } else {
          // The 0th index contains a meta task that is just a container for the
          // user's email address and logout URL
          Task metaTask = tasks[0];
          String userEmail = metaTask.getEmail();
          String logoutUrl = metaTask.getDetails();
          taskList.setUserLoggedIn(userEmail, logoutUrl);

          for (int i = 1, n = tasks.length; i < n; i++) {
            Task task = tasks[i];
            taskList.addTaskToUi(task).setRowAsPersisted(task.getId());
          }
        }
      }

    });
  }
}
