var Preferences = {
    // Units = px.
    minSidebarWidth: 100,
    minFlowViewHeight: 0,
    sidebarResizerWidth: 3,
    contentViewsResizerWidth: 20,
};

// Sets up the BookmarksView page and acts as an interface with to JS
// controller.
var BookmarksView = {
    pageLoaded: function()
    {
        // Give the window focus so that subsequent focus events within the
        // window will be taken up.
        window.focus();
        
        this.setupPage();
        
        // In the case where a request to add a new folder has arrived before
        // the entire page is finished loaded (such as when the user selects
        // Add New Folder from the Bookmarks Menu), save the request until the
        // end of setting up the page.
        this._unprocessedAddNewFolderRequest = false;
        
        BookmarksViewController.loaded();
        
        this.rootBookmark = BookmarksViewController.rootBookmark;
        this.listViewController = new BookmarksDataGridController(this.listViewContainerElement);
        this.sidebarController = new SidebarController(this.rootBookmark, this.listViewController);
        
        // Keep track of the last focused component for the cases where the focus
        // happens on a part of an element without any text to select (causing
        // window.getSelection to return null).
        this._lastFocusedComponent = this.Component.None;
        // Default focus is on sidebar.
        this.focusSidebar();
        
        if (this._unprocessedAddNewFolderRequest)
            this.handleAddNewFolderRequest();
    },

    setupPage: function()
    {
        // Set up Resizers
        
        // Sidebar Resizer
        this.sidebarResizerElement = document.getElementById("sidebarResizerVertical");
        
        this.sidebarElement = document.getElementById("sidebar");
        this.sidebarElement.component = this.Component.Sidebar;
        this.contentContainerElement = document.getElementById("contentContainer");
        
        this.addToSidebarContainerElement = document.getElementById("addToSidebarContainer");
        this.addToSidebarContainerElement.component = this.Component.Sidebar;
        this.addToListViewContainerElement = document.getElementById("addToListViewContainer");
        this.addToListViewContainerElement.component = this.Component.ListView;
        
        // Bind (curry) all the parameters except the maximum width (which will vary
        // with the window size) and the point to which it is being dragged.
        this.updateSidebarWidth = Dragger.updateElementPosition.bind(this, this.sidebarResizerElement,
            Direction.HORIZONTAL, [this.sidebarElement, this.addToSidebarContainerElement],
            [this.contentContainerElement, this.addToListViewContainerElement],
            Preferences.sidebarResizerWidth, Preferences.minSidebarWidth);
        
        this.sidebarResizerElement.addEventListener("mousedown", this.startSidebarDragging.bind(this), false);
        
        this.updateSidebarWidth(window.innerWidth / 2);
        
        // Resizer between the Flow View and the List View of the contents.
        this.contentViewsResizerElement = document.getElementById("contentResizerHorizontal");

        this.flowViewContainerElement = document.getElementById("flowViewContainer");
        this.flowViewContainerElement.component = this.Component.None;
        this.listViewContainerElement = document.getElementById("listViewContainer");
        this.listViewContainerElement.component = this.Component.ListView;
        
        this.updateFlowViewHeight = Dragger.updateElementPosition.bind(this, this.contentViewsResizerElement,
            Direction.VERTICAL, [this.flowViewContainerElement], [this.listViewContainerElement],
            Preferences.contentViewsResizerWidth, Preferences.minFlowViewHeight);
        
        this.contentViewsResizerElement.addEventListener("mousedown", this.startFlowViewDragging.bind(this), false);
        
        this.updateFlowViewHeight(window.innerHeight);
    },
        
    startSidebarDragging: function(event)
    {
        Dragger.elementDragStart(this.sidebarResizeElement, this.sidebarDragging.bind(this),
            this.endDragging.bind(this), event, "col-resize");
    },
    
    sidebarDragging: function(event)
    {
        this.updateSidebarWidth(window.innerWidth / 2, event.pageX);
        event.preventDefault();
    },
    
    startFlowViewDragging: function(event)
    {
        Dragger.elementDragStart(this.contentViewResizeElement, this.flowViewDragging.bind(this),
            this.endDragging.bind(this), event, "row-resize");
    },
    
    flowViewDragging: function(event)
    {
        this.updateFlowViewHeight(window.innerHeight, event.pageY);
        event.preventDefault();
    },
    
    endDragging: function(event)
    {
        Dragger.elementDragEnd(event);
    },
    
    handleAddNewFolderRequest: function()
    {
        if (!this.sidebarController || !this.listViewController) {
            // The page hasn't finished setting itself up yet. Defer
            // handling of this request until it has.
            this._unprocessedAddNewFolderRequest = true;
            return;
        }
        
        if (this.focusedComponent() == this.Component.Sidebar)
            this.addNewFolderToSidebar();
        else
            this.addNewFolderToListView();
        
        this._unprocessedAddNewFolderRequest = false;
    },
         
    addNewFolderToSidebar: function()
    {
        if (!this.sidebarController)
            return;
        
        this.sidebarController.addNewFolder();
        this.focusSidebar();
    },
    
    addNewFolderToListView: function()
    {
        if (!this.listViewController)
            return;
            
        this.listViewController.addNewFolder();
        this.focusListView();
    },
    
    // FIXME: Consider consolidating these methods into a bookmarkNotification
    // method that takes as a parameter an enum value dictating the type of 
    // notification.
    
    handleBookmarkAddedNotification: function(bookmark)
    {
        if (!this.sidebarController || !this.listViewController)
            return;
            
        this.sidebarController.handleBookmarkAddedNotification(bookmark);
        this.listViewController.handleBookmarkAddedNotification(bookmark);
    },
    
    handleBookmarkRemovedNotification: function(bookmark)
    {
        if (!this.sidebarController || !this.listViewController)
            return;

        this.sidebarController.handleBookmarkRemovedNotification(bookmark);
        this.listViewController.handleBookmarkRemovedNotification(bookmark);
    },
    
    handleBookmarkChangedNotification: function(bookmark)
    {
        if (!this.sidebarController || !this.listViewController)
            return;
        
        this.sidebarController.handleBookmarkChangedNotification(bookmark);
        this.listViewController.handlebookmarkChangedNotification(bookmark);
    },
    
    focusSidebar: function()
    {
        if (!this.sidebarController)
            return;
        
        this.sidebarController.focus();
        
        // If the mouse event causing the focus to happen did not happen over
        // an element with text to be selected, then the window selection will be
        // null. Hence, the lastFocusedComponent needs to be set explicitly here.
        this._lastFocusedComponent = this.Component.Sidebar;
    },
    
    focusListView: function()
    {
        if (!this.listViewController)
            return;
        
        this.listViewController.focus();
        
        // If the mouse event causing the focus to happen did not happen over
        // an element with text to be selected, then the window selection will be
        // null. Hence, the lastFocusedComponent needs to be set explicitly here.
        this._lastFocusedComponent = this.Component.ListView;
    },
    
    // Returns the component that currently has focus.
    focusedComponent: function()
    {
        var focusNode = window.getSelection().focusNode;
        if (!focusNode) {
            // If the user has not selected anything, then there will not be a
            // selected node. There is no way to programmatically select a node
            // (focus doesn't give it selection) without visibly selecting the
            // text, so rely on the user not having selected any other component.
            if (this._lastFocusedComponent == this.Component.None) {
                // Assume that the user has not interacted with the page yet
                // (otherwise something would be selected), so focus should be
                // on the sidebar.
                this._lastFocusedComponent = this.Component.Sidebar;
            }
            return this._lastFocusedComponent;
        }
        
        // Search up the DOM Tree to find the containing component.
        var component = focusNode.component;
        while (!component && focusNode) {
            focusNode = focusNode.parentNode;
            if (focusNode)
                component = focusNode.component;
        }
        
        if (!component)
            component = this._lastFocusedComponent;
        else
            this._lastFocusedComponent = component;
            
        return component;
    },
    
    Component: { None: 0, Sidebar: 1, ListView: 2 }
};

