/*
 * Copyright 2009 Palm, Inc. All rights reserved.
 */
/*global Mojo, Class, $L, AppAssistant, ConnectionWidget, HelpAssistant, LazyLoadImage, UrlManager, XMLHttpRequest, escape */
/*jslint rhino:true, undef:true */

/*
	Small controller class used for the dialog sample.
*/
var SampleDialogAssistant = Class.create({
	
	initialize: function(sceneAssistant) 
    {
		this.sceneAssistant = sceneAssistant;
		this.controller = sceneAssistant.controller;
	},
	
	setup : function(widget) 
    {
		this.widget = widget;
		this.controller.get('submitButton').addEventListener(Mojo.Event.tap, this.handleSubmit.bindAsEventListener(this));
		this.controller.get('cancelButton').addEventListener(Mojo.Event.tap, this.handleCancel.bindAsEventListener(this));
	},

	handleCancel: function() 
    {
		this.widget.mojo.close();
	},
	
	handleSubmit: function() 
    {
        // Now that they have entered a username/password, re-request using these credentials
        this.sceneAssistant.requestJSONFile();
		this.widget.mojo.close();
	}
	
});


var JsonAssistant = Class.create(HelpAssistant, {
    initialize: function($super, url ) {
        $super();

        this.xmlHttp = null;
        this.appIcons = [];
        this.currentSort = null;

        Mojo.Log.info("JsonAssistant:initialize: url: " + url);

        this.connectionAlert = this.connectionAlert.bind(this);
        this.gotNetworkStatus = this.gotNetworkStatus.bind(this);
        this.url = url; // This is the same as "UrlManager.getBaseUrl();" ?
		this.topicListTap = this.topicListTap.bind(this);
        this.dividerFunc = this.dividerFunc.bind(this);
        this.handleCommand = this.handleCommand.bind(this);
        this.saveDepotObject = this.saveDepotObject.bind(this);
        this.getDepotObject = this.getDepotObject.bind(this);
        this.sortByCategory = this.sortByCategory.bind(this);
        this.sortAlphabetically = this.sortAlphabetically.bind(this);
        this.resortItems = this.resortItems.bind(this);
        this.showLoadFailed = this.showLoadFailed.bind(this);

        this.Depot = new Mojo.Depot( { name: "HelpJSON"}, 
            function()
            {
                Mojo.Log.info("Depot connected successfully");
            }, 
            function()
            {
                Mojo.Log.info("Depot failed to connect: HelpJSON");
            });

    },

    connectionAlert: function(params)
    {
        this.base_connectionAlert(params);
        this.showLoadFailed( true, $L("No Internet Connection") );
    },

    setup:function() {
        this.base_setup( true );

		this.controller.setupWidget('username', this.usernameAttributes, this.usernameModel = { value: UrlManager.username, disabled: false } );
		this.controller.setupWidget('password', this.passwordAttributes, this.passwordModel = { value: UrlManager.password, disabled: false } );

        var failedElem = this.controller.get('load-failed-message');
        var failedModel = {message:$L("Failed to load the requested page.")};
        failedElem.innerHTML = Mojo.View.render({template:"failed-message-template", object:failedModel});

        AppAssistant.Network.addNetworkCheckedMethods({
            target: this,
            onNetworkDown: this.connectionAlert,
            methods: [ "requestJSONFile", "pushUrl" ]
        });

        this.requestJSONFile();

        this.topicModel = {
            items : [ ]
        };

        this.cmdMenuModel = {
            visible: false, 
            items: [
                {},
                {
                    toggleCmd: "text", 
                    items:[
                        {label: $L("A-Z"), command:"text", width: 150},
                        {label: $L("Category"), command:"category", width: 150}
                        ]
                },	
                {}
            ]
        };

        // Set up command menu. This has buttons which perform various operations on the list items.
        this.controller.setupWidget(Mojo.Menu.commandMenu, undefined, this.cmdMenuModel);

        this.controller.setupWidget("topicList",
             this._listAttr = {
                 itemTemplate: "json/listitem",
                 renderLimit: 40
             },
             this.topicModel);

        this.controller.listen("topicList", Mojo.Event.listTap, this.topicListTap);

        // Create a application-to-icon mapping
        this.controller.serviceRequest('palm://com.palm.applicationManager', 
        {
            method: 'listApps',
            onSuccess: function(response) 
            {
                if (!response.returnValue && response.returnValue !== '') 
                {
                    for (var i = 0; i < response.apps.length; ++i)
                    {
                        var id = response.apps[i].id;
                        var icon = response.apps[i].icon;
                        this.appIcons[ String(id) ] = icon;
                    }
                }
                else 
                {
                    Mojo.Log.error("Error retreiving list of applications, msg:'%s'", response.errorText);
                }
            }.bind(this),
            onFailure: function(response) 
            {
                Mojo.Log.error("Error retreiving list of applications, msg:'%s'", response.errorText);
            }
        });

    },

    requestJSONFile: function()
    {
        // "http://dev-help.palm.com/en-us/c019-00/d100-00/index.json";
        //url = "http://dev-help.palm.com/en-us/c019-00/d100-00/basics/index.json";
        //this.url = "http://dev-help.palm.com/en-us/error/disallowed_device.html";

        this.xmlHttp = new XMLHttpRequest();
        this.xmlHttp.onreadystatechange = this.httpRequestResponse.bind(this);

        var username = this.usernameModel.value;
        var password = this.passwordModel.value;

        // If the user has set a username/password, use these credentials when getting the file
        if ((username.length > 0) && (password.length > 0))
        {
            this.xmlHttp.open("GET", this.url, true, username, password);
            UrlManager.username = username;
            UrlManager.password = password;
        }
        else
        {
            this.xmlHttp.open("GET", this.url, true);
        }

        this.xmlHttp.setRequestHeader("X-Palm-Locale", UrlManager.locale);
        //this.xmlHttp.setRequestHeader("X-Palm-Carrier", UrlManager.carrier);
        this.xmlHttp.setRequestHeader("X-Palm-Carrier-Name", Mojo.Environment.DeviceInfo.carrierName);
        this.xmlHttp.setRequestHeader("X-Palm-Device", UrlManager.device);
        this.xmlHttp.setRequestHeader("X-Palm-Device-Name", escape(Mojo.Environment.DeviceInfo.modelName));
        this.xmlHttp.setRequestHeader("X-Palm-Device-Model", UrlManager.deviceModel);

        try
        {
            this.xmlHttp.send();
        }
        catch(e)
        {
            Mojo.Log.info("Failed to send xmLHttpRequest");
            this.showLoadFailed( true, $L("Failed to send request for data") );
        }

    },

    httpRequestResponse: function() {

        if (this.xmlHttp.readyState == 4) {

            var jsonFile = null;
            if (jsonFile === null)
            {
                if (this.xmlHttp.status==404)
                {
                    Mojo.Log.error("URL does not exist: " + this.url);
                    this.showLoadFailed( true, $L("404: URL does not exist") );
                    return;
                }
                if (this.xmlHttp.status==401)
                {
                    this.url = this.xmlHttp.getResponseHeader("Location");
                    Mojo.Log.info("Authorization failed, asking for username/password");
                    this.controller.showDialog({
                        template: 'username-password-dialog',
                        assistant: new SampleDialogAssistant(this)
                    });
                }
                if (this.xmlHttp.responseText.length <= 0)
                {
                    Mojo.Log.info("xmLHttpRequest returned empty response, status=" + this.xmlHttp.status);
                    this.showLoadFailed( true, $L("URL response was empty") );
                    return;
                }

                try 
                {
                    jsonFile = this.xmlHttp.responseText.evalJSON( true );
                    this.saveDepotObject( this.url, jsonFile );
                }
                catch (e) 
                {
                    Mojo.Log.logException(e, "evalJSON failed");

                    //this.getDepotObject( this.url );
                    this.showLoadFailed( true, $L("Malformed JSON file") );
                    return;


                }
            }

            this.populateListWithJSON( jsonFile );
        }
    },

    populateListWithJSON: function( jsonFile )
    {
        // Set the title specified in the json object
        if (jsonFile.title) 
        {
            this.controller.get("json-title").update( jsonFile.title );
        }

        // Set the title icon specified in the json object
		var titleIcon = (jsonFile.app_name && this.appIcons[jsonFile.app_name]) || jsonFile.icon;

        if (titleIcon && titleIcon.length > 0)
        {
            this.titleicon = new LazyLoadImage(titleIcon, this.controller.get("json-title-icon"));
        }

        if (!jsonFile.items)
        {
            Mojo.Log.error("No list items were found in json object");
            this.showLoadFailed( true, $L("No items found in data") );
            return;
        }

        Mojo.Log.info("json_result=" + jsonFile.json_result);
        Mojo.Log.info("# of json list items is " + jsonFile.items.length);

        // Put the downloaded items into the model for viewing in the list
        // Also, use the app_name to lookup the path of the app icon in the list of 
        // icons that was populated during setup from appManager
        for (var i=0; i<jsonFile.items.length; i++)
        {
            this.topicModel.items[i] = jsonFile.items[i];
			this.topicModel.items[i].categoryKey = i;

			if (this.topicModel.items[i].app_name && this.appIcons[this.topicModel.items[i].app_name])
			{
                // Set the app icon path based on looking up the app name in applicationManager
                this.topicModel.items[i].icon = this.appIcons[ this.topicModel.items[i].app_name ];
			}

            if (!this.topicModel.items[i].icon || (this.topicModel.items[i].icon.length === 0))
            {
                // If nothing was found, set a default image
                this.topicModel.items[i].icon = 'images/help-icon32.png';
            }
        }

        // Only sort if specified by the json object
        if (jsonFile.sort_by && jsonFile.sort_by.length > 0)
        {
            var sort_by = jsonFile.sort_by[0];
            this.resortItems( sort_by );

            // At the moment items that have icons only exist in the same instances where they can be sorted
            // which is why we only set the icon template if we are sorting
            this._listAttr.itemTemplate = "json/listiconitem";

            // Make sure the correct command menu button is depressed
            this.cmdMenuModel.items[1].toggleCmd = sort_by;
            this.controller.modelChanged(this.cmdMenuModel);

            this.controller.setMenuVisible(Mojo.Menu.commandMenu, true);
        }

        this.controller.modelChanged(this.topicModel);
        this.setSpin(false);
        this.showLoadFailed( false );
        this.xmlHttp = null;

    },

	// This is how the menu commands are handled.
	handleCommand: function(event) {		

        if (this.base_handleCommand(event))
        {
            // The base function handled the event, so we don't have to
            return;
        }

		if(event.type == Mojo.Event.command) {
			
			switch(event.command) {
				case 'text':
                    this.resortItems( "text" );
					break;
				case 'category':
                    this.resortItems( "category" );
					break;
				default:
                    Mojo.Log.warn("Unhandled command: " + event.command);
					return; // avoid the stop below!
			}
			event.stop();
		}
		
	},
	
    dividerFunc: function(itemModel) {
        return itemModel.category;

	},

    resortItems: function( sortBy )
    {
        if (this.currentSort == sortBy) {
            return;
		}

        this.currentSort = sortBy;

        if (sortBy == "category") {
            this.sortByCategory();
		} else if (sortBy == "text") {
            this.sortAlphabetically();
		} else {
            Mojo.Log.error("Unknown sort method: " + sortBy);
		}
    },

    sortAlphabetically: function()
    {
        this.currentSort = "text";

        this._listAttr.dividerFunction = null;
        this._listAttr.dividerTemplate = null;

        this.controller.get("topicList").mojo.revealItem(0, true);

        this.topicModel.items.sort( this.sortAlphabeticallyCallback );
        this.controller.modelChanged(this.topicModel);
    },

    sortByCategory: function()
    {
        this.currentSort = "category";
        
        this._listAttr.dividerFunction = this.dividerFunc;
        this._listAttr.dividerTemplate = 'json/divider';

        this.controller.get("topicList").mojo.revealItem(0, true);

        this.topicModel.items.sort( this.sortByCategoryCallback );
        this.controller.modelChanged(this.topicModel);
    },

    // Callback function for the "sort" function used on topicModel.items
    sortAlphabeticallyCallback: function(a, b)
    {
        if ((a.text === undefined) || (b.text === undefined)) {
            return 0;
		}

        var x = a.text.toLowerCase();
        var y = b.text.toLowerCase();
        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    },

    // Callback function for the "sort" function used on topicModel.items
    sortByCategoryCallback: function(a, b)
    {
		return a.categoryKey - b.categoryKey;
    },

    topicListTap: function(event) {
        var topic = this.topicModel.items[event.index];
        this.pushUrl( topic.url );
    },

    pushUrl: function( url )
    {
		var scene;
        if (url && url.length > 0)
        {
            Mojo.Log.info("Going to url=" + url);

            // Redirect pages with "index.html" to new "index.json" files
            url = url.replace( /index\.html/, 'index.json' );

            var urlStripped = url;
            if (urlStripped.indexOf( "#" ))
            {
                urlStripped = urlStripped.split( /#/ );
                urlStripped = urlStripped[0];
            }
            if (urlStripped.indexOf( "?" ))
            {
                urlStripped = urlStripped.split( /\?/ );
                urlStripped = urlStripped[0];
            }

            Mojo.Log.info("Stripped url is: " + urlStripped);

            if ( urlStripped.lastIndexOf(".") === -1 )
            {
                Mojo.Log.Error("No file extension found on path: " + urlStripped);
                return;
            }

            if (urlStripped.toLowerCase().endsWith( ".html"))
            {
                scene = "article";
                var username = this.usernameModel.value;
                var password = this.passwordModel.value;
                Mojo.Log.info("Pushing scene '%s' using username=%s, password=%s", scene, username, password );

                if (username.length > 0 && password.length > 0)
                {
                    var urlStart = "//" + username + ":" + password + "@";
                    url = url.replace( /\/\//, urlStart );
                }
                this.controller.stageController.pushScene( scene, url);
            }
            else if ( urlStripped.toLowerCase().endsWith( ".json"))
            {
                scene = "json";
                Mojo.Log.info("Pushing scene '%s'", scene );
                this.controller.stageController.pushScene( scene, url);
            }
            else if ( urlStripped.toLowerCase().endsWith( ".mp4"))
            {
                this.setSpin(true);
                var args = {
                        appId: "com.palm.app.videoplayer",
                        name: "nowplaying"
                };
                // title is " " so that the URL does not display
                this.controller.stageController.pushScene(args, { target: url, title: " " });
                this.setSpin(false);
            }
            else
            {
                Mojo.Log.error("Unkown url type: url=" + urlStripped);
            }
        }
    },

    showLoadFailed: function( display, message ) 
    {
        var failedElem = this.controller.get('load-failed-message');
        if (display)
        {
	        Mojo.Log.info("Load failed: message=" + message);
            // Only show the load error if the list is empty
            if (this.topicModel.items.length === 0)
            {
                this.controller.get('json-viewer').hide();
                failedElem.show();
                var failedModel = {
                    message:$L("Failed to load the requested page."), 
                    details: message, 
                    url: this.url,
                    locale: UrlManager.locale,
                    carrier: UrlManager.carrier,
                    carrierName: Mojo.Environment.DeviceInfo.carrierName,
                    device: UrlManager.device,
                    deviceModel: UrlManager.deviceModel,
                    deviceName: Mojo.Environment.DeviceInfo.modelName
                };

                failedElem.innerHTML = Mojo.View.render({template:"failed-message-template", object:failedModel});
            }
        }
        else
        {
            this.controller.get('json-viewer').show();
            failedElem.hide();
        }

        this.setSpin(false);
    },

    gotNetworkStatus: function(response) 
    {
        Mojo.Log.info("json: @@@ Got network status: online=", response.isInternetConnectionAvailable);

        // On first launch we alway attempt to connect to the network if we are not online.
        var self = this;
        if (!AppAssistant.Network.online) 
        {
            ConnectionWidget.connect({
                type: 'data',
                onSuccess: function(response) 
                    {
                        // If the connection widget was previously cancelled and we
                        // are under its internal timeout limit then popup the network
                        // alert dialog.
                        if (response === "WiFi-UserCancelled") 
                        {
                            self.connectionAlert();
                        }
                    }
            }, self.controller.stageController);
        }
        else
        {
            if (!this.xmlHttp || (this.xmlHttp.readyState < 2) || (this.xmlHttp.responseText.length <= 0))
            {
                Mojo.Log.info("New connection detected, re-requesting the JOSN file");
                this.requestJSONFile();
            }
        }
    },

    cleanup: function(event) {
        this.base_cleanup();
    },
    activate: function(event) {
        this.base_activate();

        AppAssistant.Network.observe(this.gotNetworkStatus);

    },
    ready: function() {
        this.base_ready();

    },
    deactivate: function(event) {
        this.base_deactivate();
    },

    getDepotObject: function( name )
    {
        // Load the saved information
        this.Depot.get( name, 
                function( retObj )
                {
                    if (!retObj)
                    {
                        this.showLoadFailed( true, $L("Failed to get record from database") );
                        return;
                    }

                    this.populateListWithJSON( retObj );

                }.bind(this), 
                function()
                {
                    this.showLoadFailed( true, $L("Failed to get record from database") );
                }.bind(this)
                );

    },

    saveDepotObject: function( name, obj )
    {
        this.Depot.add( name, obj, 
                function()
                {
                }, 
                function( error )
                {
                });
    },

    usernameAttributes: {
        textReplacement: false,
        maxLength: 64,
        focus: true,
        acceptBack: true,
        hintText: $L('enter username'),
        changeOnKeyPress: true
    },
    passwordAttributes: {
        textReplacement: false,
        maxLength: 64,
        focus: false,
        acceptBack: true,
        hintText: $L('enter password'),
        changeOnKeyPress: true
    }


});
