package com.Series40Book.midp.controller;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import com.Series40Book.midp.view.*;
import com.Series40Book.midp.model.*;
import com.Series40Book.midp.model.questions.QuestionsNavigator;
import com.Series40Book.midp.model.messaging.*;
import com.Series40Book.midp.*;

/**
 *
 * Controls all interactions between screens and the model
 * Maintains a pool of shared UI instances
 *
 */
public class UIController {

  public static boolean isPaused = false;
  public static boolean needSubmit;
  private static MIDlet midlet;
  public static Display display;

  public static Sender sender;
  private static Inbox inbox;
  
  // The command pool
  public static Command exitCommand =
      new Command ("Exit", Command.EXIT, 0);
  public static Command backCommand =
      new Command ("Back", Command.BACK, 0);
  public static Command doneCommand =
      new Command ("Done", Command.OK, 0);
  public static Command goCommand =
      new Command ("Go", Command.OK, 0);
  public static Command nextCommand =
      new Command ("Next", Command.OK, 0);
  
  // The screen pool
  public static ShowQuestionUI questionScreen = null;
  public static ShowAnswerUI answerScreen = null;
  public static SelectActionUI actionScreen = 
      new SelectActionUI ();
  public static PrefUI prefScreen = null;
  public static InboxUI inboxScreen = null;

  public static Stack backStack = new Stack ();

  private UIController () {
  }

  public static void init (MIDlet m) throws Exception {
    midlet = m;
    isPaused = false;
    needSubmit = false;
    display = Display.getDisplay(midlet);

    QuestionsNavigator.setUrl (m.getAppProperty("serverURL"));
    GoogleBrowser.init (m.getAppProperty("serverURL"));
    HistoryManager.init (m.getAppProperty("serverURL"));

    //#if WMA
    {
      sender = new SenderImpl ();
      inbox = new InboxImpl ();
      sender.setPort(m.getAppProperty("smsPort"));
      try {
        inbox.setupListener (m.getAppProperty("smsPort"));
      } catch (Exception e) {
        // do nothing
      }
    }
    //#endif

    resume();
  }

  public static void resume () {
    TheTimer.pause();
    actionScreen.setStatus(isPaused, needSubmit);
    display.setCurrent(actionScreen);
  }


  public static void exit () {
    ((TriviaMidlet) midlet).exit();
  }

  public static void submit () {
    TheTimer.pause();
    actionScreen.setStatus(false, needSubmit);

    WorkerRunnable worker = new SubmitAnswersWorker ();
    WaitScreen wait = new WaitScreen ();

    // Start the worker and set the wait screen
    wait.setWorker (worker);
    display.setCurrent(wait);
  }


  public static void download () throws Exception {
    actionScreen.setStatus(false, needSubmit);

    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    qn.initSession ();

    WorkerRunnable worker =
        new DownloadQuestionsWorker ();
    WaitScreen wait = new WaitScreen ();

    // Start the worker and set the wait screen
    wait.setWorker (worker);
    display.setCurrent(wait);

    TheTimer.start();
  }


  public static void resumeQuestion ()
                     throws Exception {
    TheTimer.resume();
    if (questionScreen == null) {
      questionScreen = new ShowQuestionUI ();
    }
    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    questionScreen.setStatus(
            qn.getQuestion(),
            qn.getChoices(),
            qn.getImage(),
            qn.getSound());
    display.setCurrent(questionScreen);
  }

  public static void nextQuestion () throws Exception {

    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    if (qn.next()) {
      resumeQuestion ();
    } else {
      checkAnswers ();
    }
  }

  private static void checkAnswers () throws Exception {
    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    if (qn.isReady()) {
      try {
        HistoryManager.storeScore(qn.correctNum,
                                  qn.wrongNum,
                                  qn.time);
      } catch (Exception e) {
        e.printStackTrace ();
      }
      showReport ();
    } else {
      submitNotify ();
    }
  }

  public static void restartQuestion () throws Exception {
    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    TheTimer.start();
    qn.resetSession ();
    qn.next();
    resumeQuestion ();
  }

  public static void storeAnswer (int ans)
                            throws Exception {
    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    int status = qn.storeAnswer(ans + 1);

    if (status == QuestionsNavigator.NA_ANSWER) {
      // No local answer, move on
      nextQuestion ();

    } else {
      // Prepare and show the local answer
      if (answerScreen == null) {
        answerScreen = new ShowAnswerUI ();
      }

      if (status == QuestionsNavigator.CORRECT_ANSWER) {
        answerScreen.setStatus(true, qn.getAnswerHint());
      } else if (status ==
          QuestionsNavigator.WRONG_ANSWER) {
        answerScreen.setStatus(false, qn.getAnswerHint());
      }

      display.setCurrent(answerScreen);
    }
  }

  public static void clearHistory () {
    HistoryManager.clear ();
    showReport ();
  }

  public static void showReport () {
    ReportUI report = new ReportUI ();
    report.setStatus(HistoryManager.getCorrectNums(),
                     HistoryManager.getWrongNums(),
                     HistoryManager.getTimes());
    display.setCurrent(report);
  }

  public static void synchronize () {
    WorkerRunnable worker = new SyncWorker ();
    WaitScreen wait = new WaitScreen ();

    // Start the worker and set the wait screen
    wait.setWorker (worker);
    display.setCurrent(wait);
  }

  public static void submitNotify () {
    needSubmit = true;
    TheTimer.pause();

    SubmitNotifyUI show = new SubmitNotifyUI ();
    show.setStatus();
    display.setCurrent(show);
  }

  public static void showGoogleQuery (Displayable back)
                                     throws Exception {
    TheTimer.pause();
    backStack.push(back);

    GoogleQueryUI show = new GoogleQueryUI ();
    QuestionsNavigator qn =
        QuestionsNavigator.getInstance ();
    show.setStatus(qn.getQuestion());
    display.setCurrent(show);
  }

  public static void getGoogleResults (String keywords,
                                Displayable back) {
    backStack.push(back);

    WorkerRunnable worker =
        new GoogleSearchWorker (keywords);
    WaitScreen wait = new WaitScreen ();

    // Start the worker and set the wait screen
    wait.setWorker (worker);
    display.setCurrent(wait);
  }

  public static void setPref (String category,
           int googleResultsNum, int googleTextMargin,
           boolean googleIsPhrase) {
    PrefStore pref = PrefStore.getInstance ();
    pref.category = category;
    pref.googleResultsNum = googleResultsNum;
    pref.googleTextMargin = googleTextMargin;
    pref.googleIsPhrase = googleIsPhrase;

    try {
      pref.save ();
    } catch (Exception e) {
      System.out.println("Error saving prefScreen");
      e.printStackTrace();
    }
    resume ();
  }

  public static void showPref() throws Exception {
    if (prefScreen == null) {
      prefScreen = new PrefUI ();
    }
    PrefStore pref = PrefStore.getInstance ();
    prefScreen.setStatus(
        pref.category,
        pref.googleResultsNum,
        pref.googleTextMargin,
        pref.googleIsPhrase);
    display.setCurrent(prefScreen);
  }

  public static void showBack () throws Exception {
    display.setCurrent(backStack.pop());
  }

  // The following methods are for the messaging module
  public static void firstMessage (Displayable back)
                                     throws Exception {
    TheTimer.pause();
    backStack.push(back);

    if (inboxScreen == null) {
      inboxScreen = new InboxUI ();
    }
    inboxScreen.setStatus (inbox.firstMessage());
    display.setCurrent(inboxScreen);
  }

  public static void nextMessage () throws Exception {
    if (inboxScreen == null) {
      inboxScreen = new InboxUI ();
    }
    inboxScreen.setStatus (inbox.nextMessage());
    display.setCurrent(inboxScreen);
  }

  public static void deleteMessage () throws Exception {
    if (inboxScreen == null) {
      inboxScreen = new InboxUI ();
    }
    inbox.deleteMessage ();

    inboxScreen.setStatus (inbox.nextMessage());
    display.setCurrent(inboxScreen);
  }

  public static void exitMessage () throws Exception {
    inbox.cleanUp();
    showBack();
  }


  public static void showSMSComposer (Displayable back) {
    backStack.push(back);

    SMSComposerUI show = new SMSComposerUI ();
    display.setCurrent(show);
  }

  public static void sendMessage (String addr,
                                  String mesg) {
    WorkerRunnable worker =
        new SendSMSWorker (addr, mesg);
    WaitScreen wait = new WaitScreen ();

    // Start the worker and set the wait screen
    wait.setWorker (worker);
    display.setCurrent(wait);
  }

  public static void showAlert (String title, String message,
                                Displayable prev) {
    Alert alertScreen = new Alert(title);
    alertScreen.setString(message);
    alertScreen.setTimeout(Alert.FOREVER);
    display.setCurrent(alertScreen, prev);
  }

}


class DownloadQuestionsWorker extends WorkerThread {

  public void run () {
    try {
      PrefStore pref = PrefStore.getInstance ();
      QuestionsNavigator qn =
          QuestionsNavigator.getInstance ();

      updateWaitStatus ("Start downloading");
      qn.download(pref.category, this);
      updateWaitStatus ("End downloading");

      ShowQuestionUI show = new ShowQuestionUI ();
      if (qn.next()) {
        show.setStatus(
                qn.getQuestion(),
                qn.getChoices(),
                qn.getImage(),
                qn.getSound());

      } else {
        throw new Exception ("Empty download set");
      }
      if (!stopped) {
        UIController.display.setCurrent(show);
      }
    } catch (Exception e) {
      e.printStackTrace ();
      if (!stopped) {
        if (UIController.actionScreen == null) {
          UIController.showAlert("Error",
              "Download error", UIController.actionScreen);
        }
      }
    }
  }

  public void stop () {
    stopped = true;
    UIController.display.setCurrent (
             UIController.actionScreen);
  }
}

class SubmitAnswersWorker extends WorkerThread {

  public void run () {
    UIController.needSubmit = false;

    try {
      // run the network handler
      QuestionsNavigator qn =
          QuestionsNavigator.getInstance ();

      updateWaitStatus("Starting submitting");
      qn.submit(this);

      updateWaitStatus ("Generate the result screen");
      ResultUI result = new ResultUI ();
      result.setStatus(
          qn.correctNum,
          qn.wrongNum,
          qn.time);

      updateWaitStatus ("Update the history");
      HistoryManager.storeScore(
          qn.correctNum,
          qn.wrongNum,
          qn.time);

      if (!stopped) {
        UIController.display.setCurrent(result);
      }
    } catch (Exception e) {
      e.printStackTrace ();
      if (!stopped) {
        UIController.actionScreen.setStatus(false, true);
        UIController.showAlert("Failed",
            e.getMessage(), UIController.actionScreen);
      }
    }
  }

  public void stop () {
    stopped = true;
    UIController.display.setCurrent (
               UIController.actionScreen);
  }
}

class GoogleSearchWorker extends WorkerThread {

  private String keywords;

  public GoogleSearchWorker (String k) {
    super ();
    keywords = k;
  }

  public void run () {

    try {
      PrefStore pref = PrefStore.getInstance ();
      GoogleBrowser.search (keywords,
              pref.googleIsPhrase,
              pref.googleTextMargin,
              pref.googleResultsNum, this);

      GoogleResultUI show = new GoogleResultUI ();
      show.setStatus(GoogleBrowser.spellSugg,
                     GoogleBrowser.urls,
                     GoogleBrowser.snippets);

      if (!stopped) {
        UIController.display.setCurrent(show);
      }
    } catch (Exception e) {
      e.printStackTrace ();
      if (!stopped) {
        // Go back to the query screen after failure
        UIController.showAlert("Failed",
          e.getMessage(), UIController.backStack.pop());
      }
    }
  }

  public void stop () {
    stopped = true;
    try {
      UIController.showBack();
    } catch (Exception e) { }
  }
}

class SendSMSWorker extends WorkerThread {

  private String addr, mesg;

  public SendSMSWorker (String a, String m) {
    super ();
    addr = a;
    mesg = m;
  }

  public void run () {

    boolean success = false;

    try {
      updateWaitStatus ("Start sending");
      UIController.sender.send (addr, mesg);
      updateWaitStatus ("Finished");
      success = true;
    } catch (Exception e) {
      e.printStackTrace ();
      success = false;
    }

    if (!stopped) {
      SMSStatusUI show = new SMSStatusUI ();
      updateWaitStatus ("");

      if (success) {
        show.setStatus("Message is sent");
      } else {
        show.setStatus("Message failed");
      }

      UIController.display.setCurrent(show);
    }
  }

  public void stop () {
    stopped = true;
    UIController.display.setCurrent (
        UIController.inboxScreen);
  }
}

class SyncWorker extends WorkerThread {

  public void run () {

    try {
      updateWaitStatus ("Start sync");
      HistoryManager.synchronize(this);
      UIController.showReport();

    } catch (Exception e) {
      e.printStackTrace ();
      if (!stopped) {
        UIController.showReport();
      }
    }
    if (!stopped) {
      UIController.showReport ();
    }
  }

  public void stop () {
    stopped = true;
    UIController.showReport ();
  }
}