/*
 * Copyright 2009 Palm, Inc. All rights reserved.
 */
/*global Mojo, Class, $L, PageControls, MenuData */
/*jslint rhino:true, undef:true */

var WebKitErrors = {
	ERR_SYS_FILE_DOESNT_EXIST: 14,
	ERR_WK_FLOADER_CANCELLED: 1000,
	ERR_CURL_FAILURE: 2000,
	ERR_CURL_COULDNT_RESOLVE_HOST: 2006,
	ERR_CURL_SSL_CACERT: 2060
};

var WVH = WVH || {};
WVH.Log = {};
WVH.Log.info = function() {}; //Mojo.Log.info;
WVH.Log.warn = Mojo.Log.warn;
WVH.Log.error = Mojo.Log.error;
WVH.Log.logException = Mojo.Log.logException;


var WebViewHelper = Class.create({

    initialize:function(params) 
    {
        this.loadedInitialUrl = false;
        this.onTitleSet = undefined;  // Callback function for when the title is set
        this.isChatWindow = false; // default value
        this.hidePageControls = false; // default value
        this.pageFinishedLoading = false;

        if (params)
        {
            this.initialURL = params.url;
            this.assistant = params.assistant;
            if (params.hidePageControls !== undefined) {
                this.hidePageControls = params.hidePageControls;
			}
            if (params.isChatWindow !== undefined) {
                this.isChatWindow = params.isChatWindow;
			}
        } else {
            Mojo.Log.info("Error initializing WebViewHelper: No params specified");
		}

    },

  cleanup:function() {
    delete this.assistant;
    delete this._webView;

    this._pageControls.cleanup();
  },

  setup:function( ) {

    try {
      this._webView = this.assistant.controller.get('web-view-help');

      this.assistant.controller.setupWidget('web-view-help', {
        url:undefined,
        showClickedLink:true,
        userCanAlwaysScale:!this.isChatWindow,
        formAutoZoom:!this.isChatWindow,
		useMouseEvents: this.isChatWindow,
		virtualpagewidth: this.assistant.controller.window.innerWidth,
		virtualpageheight: this.assistant.controller.window.innerHeight
      });

      this._onWebStartLoading = this._onWebStartLoading.bind(this);
      this._onWebStopLoading = this._onWebStopLoading.bind(this);
      this._onWebFailedLoad = this._onWebFailedLoad.bind(this);
      this._onWebLoadProgress = this._onWebLoadProgress.bind(this);
      this._onTitleUrlChange = this._onTitleUrlChange.bind(this);
      this._onUpdateHistory = this._onUpdateHistory.bind(this);
      this._onWebServerConnected = this._onWebServerConnected.bind(this);
      this._onWebServerDisconnected = this._onWebServerDisconnected.bind(this);

      var handlRes = this._onWebViewResourceHandoff.bind(this);
      this._webView.addEventListener(Mojo.Event.webViewMimeNotSupported, handlRes, false);
      this._webView.addEventListener(Mojo.Event.webViewMimeHandoff, handlRes, false);
      this._webView.addEventListener(Mojo.Event.webViewUrlRedirect, handlRes, false);

      this._backModel = {
        label: MenuData.NavigationMenu.Back.label,
        icon: MenuData.NavigationMenu.Back.icon,
        command: MenuData.NavigationMenu.Back.command
      };

      this._forwardModel = {
        label: MenuData.NavigationMenu.Forward.label,
        icon: MenuData.NavigationMenu.Forward.icon,
        command: MenuData.NavigationMenu.Forward.command
      };

      this.cmdMenuModel = {
        visible: true,
        items: [
          this._backModel,
          this._forwardModel,
          {}]};

      this.assistant.controller.setupWidget(Mojo.Menu.commandMenu, {menuClass:'no-fade'}, this.cmdMenuModel);

      this._pageControls = new PageControls(this.assistant.controller, this.hidePageControls);
      this._pageControls.setup(true);
    }
    catch (e) {
      Mojo.Log.logException(e, "WebViewHelper.setup");
    }
  },

  activate: function() {
    // this._pageControls.showIdle();
	this._webView.mojo.focus();
  },

  ready: function( ) {

    // Only want this set if we are NOT in a chat window!
    if ( this.isChatWindow === false)
    {
        this._webView.mojo.addUrlRedirect( 'http[sS]?://.*help\\.palm\\.com\/.+\/chat\/.+\\.html', true, 'chat', 0);
    }
	else
	{
		this._webView.mojo.clearCache();
	}

    this._webView.mojo.addUrlRedirect('.json$', true, 'json', 0);
    this._webView.mojo.addUrlRedirect('^http://portal\\.stage\\.palm\\.com', false, '', 0);
    this._webView.mojo.addUrlRedirect('^http[sS]?://(.+@)?.*help\\.palm\\.com', false, '', 0);
    this._webView.mojo.addUrlRedirect('^http://kb\\.palm\\.com', false, '', 0);
    this._webView.mojo.addUrlRedirect('^https?://.*\\.logmeinrescue\\.com', false, '', 0);
    this._webView.mojo.addUrlRedirect('.*', true, '', 0);
    this._webView.mojo.addSystemRedirects('com\\.palm\\.app\\.help');

    if (this.isChatWindow === true)
    {
        if (this._webView.mojo._setScrollNode !== undefined)
        {
            Mojo.Log.info("Setting scroll mode in webview widget to true");
            this._webView.mojo._setScrollNode(true);
        }
    }
  },
  
  startListening:function() {
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewLoadStarted, this._onWebStartLoading);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewLoadStopped, this._onWebStopLoading);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewLoadFailed, this._onWebFailedLoad);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewLoadProgress, this._onWebLoadProgress);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewTitleUrlChanged, this._onTitleUrlChange);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewUpdateHistory, this._onUpdateHistory);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewServerConnect, this._onWebServerConnected);
    this.assistant.controller.listen('web-view-help', Mojo.Event.webViewServerDisconnect, this._onWebServerDisconnected);
  },

  stopListening:function() {
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewLoadStarted, this._onWebStartLoading);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewLoadStopped, this._onWebStopLoading);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewLoadFailed, this._onWebFailedLoad);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewLoadProgress, this._onWebLoadProgress);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewTitleUrlChanged, this._onTitleUrlChange);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewUpdateHistory, this._onUpdateHistory);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewServerConnect, this._onWebServerConnected);
    this.assistant.controller.stopListening('web-view-help', Mojo.Event.webViewServerDisconnect, this._onWebServerDisconnected);
  },

  _onWebStartLoading: function(event) {
    Mojo.Log.info("Help App.WebViewHelper#webViewLoadStarted: starting to load page");
    this._handleAction('start');
    if (!this.isChatWindow) {
        this.assistant.setSpin( false );
	}
    this.pageFinishedLoading = false;
  },

  _onWebStopLoading: function(event) {
    Mojo.Log.info("Help App.WebViewHelper#webViewLoadStopped: finished loading page.");
    this._handleAction('stop');
    this.pageFinishedLoading = true;
    if (this.isChatWindow) {
      this.assistant.setSpin( false );
	}
  },

  _onWebFailedLoad: function(event) {
    Mojo.Log.info("Help App.WebViewHelper#webViewLoadFailed: failed to load page. %j", event);
    if (event.errorCode != WebKitErrors.ERR_WK_FLOADER_CANCELLED) {
      this._handleAction('stop');
      if (!this.pageFinishedLoading) {
        this.assistant.showLoadFailed( event );
	}
      if (this.isChatWindow) {
        this.assistant.setSpin( false );
	}
    }
  },

  _onWebLoadProgress: function(event) {
    this._handleAction('progress', {progress: event.progress});
  },

  _openResource: function(uri, mimeType) {
    var params = { target: uri };
    if (mimeType) {
      params.mime = mimeType;
    }

    this.assistant.controller.serviceRequest('palm://com.palm.applicationManager', {
      method: 'open',
      parameters: params,
      onSuccess: function(item, response) {
        if (response.returnValue) {
          Mojo.Log.info("Successfully handed off '%s'", uri);
        }
        else {
          Mojo.Log.error("Error handing off '%s', msg:'%s'", uri, response.errorText);
        }
      },
      onFailure: function(response) {
        Mojo.Log.error("Failure handing off '%s', msg:'%s'", uri, response.errorText);
      }
    });
  },

  _onWebViewResourceHandoff: function(event) {
    try {
      Mojo.Log.info("Just got handed off a resource: " + Object.toJSON(event) );
      this._handleAction('userstop');

      if (this.assistant.isCommand(event.url)) {
          this._openResource(event.url, event.mimeType);
      }
      else 
      {
        if (event.appId == 'json')
        {
            Mojo.Log.info("Going to push a new json scene for: " + event.url);
            this.assistant.controller.stageController.pushScene("json", event.url);
        }
        else if (event.appId == 'chat')
        {
            Mojo.Log.info( "Chat link detected: " + event.url );

            var stageName = "Chat for Help";
            var stage = Mojo.Controller.getAppController().getStageController( stageName );
            if (stage === undefined)
            {
                Mojo.Controller.getAppController().createStageWithCallback( 
                        {name: stageName, lightweight: true}, 
                        function(stageController)
                        {
                            stageController.pushScene( {name: 'chat'}, event.url ); 
                        }
                    );
            }
            else
            {
                stage.activate();
            }
        }
        else
        {
            var mime = event.mimeType || '';
            this._getResourceInfo(event.url, mime,
                function(response) { // onSuccess
                  if (response.returnValue && response.canStream) {
                    this._streamResource(event.url, response.appIdByExtension, response.mimeByExtension);
                  }
                  else {
                    Mojo.Log.info("Resource not streamed, but can't download (not browser)");
                  }
                }.bind(this),
                function() { // onFailure
                  Mojo.Log.error("Error getting resource info for '%s':(%s)", event.url, mime);
                  Mojo.Log.info("Resource not streamed, but can't download (not browser)");
                }.bind(this));
        }
      }
    }
    catch (e) {
      Mojo.Log.logException(e, '_onWebViewResourceHandoff');
      throw e;
    }
  },

  _getResourceInfo: function(uri, mimeType, onSuccess, onFailure) {

    var params = {uri: uri};

    if (this._isValidMimeType(mimeType)) {
      params.mime = mimeType;
    }

    this.assistant.controller.serviceRequest('palm://com.palm.applicationManager/getResourceInfo', {
      parameters: params,
      onSuccess: onSuccess,
      onFailure: onFailure
    });
  },

  _isValidMimeType: function(mimeType) {
    return mimeType !== '' && mimeType !== 'unknown';
  },

  _streamResource: function(uri, appid, mimeType) {

    Mojo.Log.info("Streaming: '%s' with '%s' (%s)", uri, appid, mimeType);

    // Only a few select applications can be
    var crossAppScene = {
      'com.palm.app.videoplayer': 'nowplaying',
      'com.palm.app.streamingmusicplayer': 'nowplaying'
    };
    var params = {target: uri, mimeType: mimeType};
    if (crossAppScene[appid]) {
	  if (appid =='com.palm.app.videoplayer') {
	    params.title = " ";
	  }
      var args = { appId: appid, name: crossAppScene[appid] };
      params.title = " ";
      this.assistant.controller.stageController.pushScene(args, params);
    }
    else {
      this.assistant.controller.serviceRequest('palm://com.palm.applicationManager', {
        method: 'open',
        parameters: {
          'id': appid,
          'params': params
        }
      });
    }
  },

  _updateBackForward: function() {
    var backForward = function(back, forward) {

			this.canGoBack = back;
			this.canGoForward = forward;

      // Can always go back (to home page if no history)
			this._pageControls.updateBackForward(true, this.canGoForward);

		}.bind(this);

		this._webView.mojo.getHistoryState(backForward);
  },

  _onTitleUrlChange: function(event) {
    //Mojo.Log.info("Help App.WebViewHelper#onTitleUrlChange" + Object.toJSON( event ) );
    this.currentUrl = event.url;
    this._updateBackForward();
   
    // Callback into this function if it's set 
    if ( this.onTitleSet !== undefined ) {
        this.onTitleSet( event.title );
	}

  },

  _onUpdateHistory: function(event) {

    this._updateBackForward();
  },

  _onWebServerConnected: function(event) {
    Mojo.Log.info("Help App#WebViewHelper._onWebServerConnected");
    this.assistant.setSpin( false );

    if (this.currentUrl && this._currentState != "Idle") {
      this._webView.mojo.openURL(this.currentUrl);
    }
  },

  _onWebServerDisconnected: function(event) {
    Mojo.Log.info("Help App#WebViewHelper._onWebServerDisconnected");

    this.assistant.setSpin( true );
  },

  tryToLoadInitialUrl: function() {
    Mojo.Log.info("Help App#WebViewHelper._tryToLoadInitialUrl");
    try {
      if (!this.loadedInitialUrl) {
        this._webView.mojo.openURL(this.initialURL);
        this.loadedInitialUrl = true;
      }
    }
    catch (e) {
      this.assistant.setSpin( true );
      Mojo.Log.logException(e, 'Help App#WebViewHelper.tryToLoadInitialUrl (%s) retrying', this.initialURL);
      this.assistant.controller.window.setTimeout(this.tryToLoadInitialUrl.bind(this), 200);
    }
  },

  /******************************************************************
   * Our state machine for navigation and page animations
   * @param {Object} contract
   */
  _initialState: 'Idle',
  _currentState: 'Idle',

  _handleAction: function(someAction, details) {

    try {
      details = details || {};

      var nextState;
      var anActionFunction = this._actionFunctions[this._currentState][someAction];
      if (!anActionFunction) {
        WVH.Log.warn("UNEXPECTED ACTION - STATE MACHINE: (CUR STATE: %s, ACTION: %s)", this._currentState, someAction);
        anActionFunction = this._undefinedAction;
      }

      WVH.Log.info("STATE MACHINE: (CUR STATE: %s, ACTION: %s)", this._currentState, someAction);

      // call the state-machines action function with the current instance context.
      nextState = anActionFunction.call(this, details);

      WVH.Log.info("STATE MACHINE: (%s -> %s -> %s)", this._currentState, someAction, nextState);
      if (!nextState) {
        nextState = this._currentState;
      }

      if (!this._actionFunctions[nextState]) {
        nextState = this._undefinedState(someAction, nextState);
      }

      this._currentState = nextState;
    } catch (e) {
      Mojo.Log.logException(e, '#_handleAction()');
    }
  },

  _undefinedAction: function(details) {

    WVH.Log.warn("WebViewHelper handled unexpected action");

    this._pageControls.showIdle();

    return this._initialState;
  },

  _undefinedState: function(someAction, state) {

    WVH.Log.error("WebViewHelper handled unexpected state: %s, for action: %s", state, someAction);

    this._pageControls.showIdle();

    return this._initialState;
  },

  _actionFunctions: {

    Idle: {

      stop: function() {
        return this._currentState;
      },

      start: function() {
        this._pageControls.showSearch();
        return 'Progress';
      },

      useropen: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userreload: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userforward: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userback: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userstop: function() {
        return this._currentState;
      },

      progress: function() {
        return this._currentState;
      }
    },

    Pending: {

      stop: function() {
        return this._currentState;
      },

      start: function() {
        this._pageControls.showSearch();
        return 'Progress';
      },

      useropen: function() {
        return this._currentState;
      },

      userreload: function() {
        return this._currentState;
      },

      userforward: function() {
        return this._currentState;
      },

      userback: function() {
        return this._currentState;
      },

      userstop: function() {
        this._pageControls.showIdle();
        return 'Idle';
      },

      progress: function() {
        return this._currentState;
      }
    },

    Progress: {

      stop: function() {
        this._pageControls.showIdle();
        return 'Idle';
      },

      start: function() {
        return this._currentState;
      },

      useropen: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userreload: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userforward: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userback: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userstop: function() {
        this._pageControls.showIdle();
        return 'Idle';
      },

      progress: function(details) {
        if (details.progress > 10) {
          // On a start progress is immediately set to 10
          // even though we've not downloaded content yet
          // nor are we sure the server is live.
          this._pageControls.showProgress();
          this._pageControls.updateProgress(details.progress);
          return 'Progress10';
        }
        return this._currentState;
      }
    },

    Progress10: {

      stop: function() {
        this._pageControls.showIdle();
        return 'Idle';
      },

      start: function() {
        return this._currentState;
      },

      useropen: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userreload: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userforward: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userback: function() {
        this._pageControls.showSearch();
        return 'Pending';
      },

      userstop: function() {
        this._pageControls.showIdle();
        return 'Idle';
      },

      progress: function(details) {
        this._pageControls.updateProgress(details.progress);
        return this._currentState;
      }
    }
  }
});

