/*
 * #%L
 * share-po
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.po.share.site.document;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.alfresco.po.HtmlPage;
import org.alfresco.po.RenderElement;
import org.alfresco.po.RenderTime;
import org.alfresco.po.exception.PageException;
import org.alfresco.po.exception.PageOperationException;
import org.alfresco.po.share.Pagination;
import org.alfresco.po.share.enums.ViewType;
import org.alfresco.po.share.site.SitePage;
import org.alfresco.po.share.util.PageUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.utils.URIBuilder;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebElement;

/**
 * Site document library page object, holds all element of the HTML page relating to share's site document library page.
 *
 * @author Michael Suzuki
 * @author Shan Nagarajan
 * @since 1.0
 */
public class DocumentLibraryPage extends SitePage
{
    protected static final String JS_DOCUMENT_VIEW_TYPE = "return Alfresco.util.ComponentManager.findFirst('Alfresco.DocumentList').options.viewRendererName;";
    private static final String JS_SCRIPT_CHECK_DOCLIST = "return Alfresco.util.ComponentManager.findFirst('Alfresco.DocumentList').widgets.dataTable._getViewRecords();";
    private static Log logger = LogFactory.getLog(DocumentLibraryPage.class);
    private static final String PAGINATION_BUTTON_NEXT = "a.yui-pg-next";
    private static final String PAGINATION_BUTTON_PREVIOUS = "a.yui-pg-previous";
    public static final String FILES_AND_DOCUMENTS_TABLE_CSS = "table#yuievtautoid-0 > tbody.yui-dt-data > tr";
    private static final String ALL_TAGS_PRESENT_ON_DOC_LIB = "div.filter>ul>li>span.tag>a.tag-link";
    private static final String FILE_UPLOAD_INSTRUCTION = "div.docListInstructionsWithDND";
    private static final String BOTTOM_PAGINATOR_LOCATION = "div[id$='_default-paginatorBottom']";

    private static final By THUMBNAIL_IMAGE = By.cssSelector("td[class$='yui-dt-col-thumbnail'] img");
    private static final By DOCUMENTS_TREE_CSS = By.cssSelector("div.filter.doclib-filter h2");
    private static final By CATEGORIES_TREE_CSS = By.cssSelector("div[class='categoryview filter'] > h2");
    private static final By MY_FAVOURITES = By.cssSelector("span.favourites > a");
    private static final By RECENTLY_MODIFIED = By.cssSelector("span.recentlyModified > a");
    private static final By RECENTLY_ADDED = By.cssSelector("span.recentlyAdded > a");
    private static final By ALL_DOCUMENTS = By.cssSelector("span.all > a");
    private static final By CATEGORIES_IN_TREE = By.cssSelector("div[class^='categoryview'] span[id^='ygtvlabelel']");
    private final String subfolderName = null;
    private boolean shouldHaveFiles;
    private String contentName = null;
    private ViewType viewType = ViewType.DETAILED_VIEW;
    private static final String TEMPLATE_LIST = "//div[contains(@class, 'menu visible')]//div[@class='bd']//ul//ul//li";
    private final By submitButton = By.cssSelector("button[id$='default-createFolder-form-submit-button']");
    private static final By ALL_CATEGORIES_PRESENT_ON_DOC_LIB = By.xpath("//span[text()='Category Root']/ancestor-or-self::table/parent::div/child::div/div");
    private static String CATEGORY_ROOT_SPACER = "//span[text()='Category Root']/ancestor-or-self::table[contains(@class, 'depth0')]";
    private static By CATEGORY_ROOT_SPACER_LINK = By.xpath(CATEGORY_ROOT_SPACER + "//a");
    private static final String CHECK_BOX = "input[id^='checkbox-yui']";
    private static final By SYNC_MESSAGE = By.xpath(".//span[contains(text(),'Sync was created')]");

    public enum Optype
    {
        REQ_TO_SYNC, SYNC, UNSYNC;
    }

    /**
     * Getter method
     */
    public String getContentName()
    {
        return contentName;
    }

    public void setContentName(String contentName)
    {
        this.contentName = contentName;
    }

    /**
     * Check if javascript message is displayed. The message details are loading document library message.
     *
     * @return if message displayed
     */
    private boolean isDocumentLibLoading()
    {
        // Check url to see if the document lib page is present.
        return (isJSMessageDisplayed() || pageLoadingMessageDisplayed());
    }

    @SuppressWarnings("unchecked")
    @Override
    public synchronized DocumentLibraryPage render(RenderTime timer)
    {
        while (true)
        {
            try
            {
                timer.start();
                synchronized (this)
                {
                    try
                    {
                        this.wait(100L);
                    }
                    catch (InterruptedException e)
                    {
                    }
                }

                // Case 1: When we have no files we check upload instruction appears and we are not expecting files
                if (isFileUploadInstructionDisplayed() && !shouldHaveFiles)
                {
                    if (paginatorRendered())
                    {
                        boolean hasNoFiles = !hasFiles();
                        if (hasNoFiles)
                        {
                            if (logger.isTraceEnabled())
                            {
                                logger.trace("upload message appears and has no files is: " + hasNoFiles);
                            }
                            break;
                        }
                    }
                }
                // Case 2: When we have some files or expecting to see a file.
                // Check if loading document library message is gone
                if (!isDocumentLibLoading())
                {
                    // Check if were in correct folder
                    if (subfolderName != null)
                    {
                        if (isSubFolderDocLib(subfolderName))
                        {
                            // Give time to reload the document library page
                            synchronized (this)
                            {
                                try
                                {
                                    this.wait(100L);
                                }
                                catch (InterruptedException e)
                                {
                                }
                            }
                            if (dataRendered())
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (dataRendered())
                        {
                            break;
                        }
                    }
                }
            }
            catch (StaleElementReferenceException ste)
            {
                // DOM has changed therefore page should render once change is completed
            }
            finally
            {
                timer.end();
            }
        }

        viewType = getNavigation().getViewType();
        return this;
    }

    /**
     * Checks to see if document list bottom paginator is displayed
     *
     * @return true if displayed
     */
    public boolean paginatorRendered()
    {
        try
        {
            return driver.findElement(By.cssSelector(BOTTOM_PAGINATOR_LOCATION)).isDisplayed();
        }
        catch (NoSuchElementException e)
        {
        }
        return false;
    }

    private boolean pageLoadingMessageDisplayed()
    {
        try
        {
            WebElement loadingElement = driver.findElement(By.cssSelector("table>tbody>tr>td.yui-dt-empty>div"));
            return (loadingElement.isDisplayed() && loadingElement.getText().contains("Loading"));

        }
        catch (NoSuchElementException e)
        {
        }
        return false;
    }

    /**
     * Render logic to check if files or folders are present on the document library page or if empty file folder help message is displayed.
     *
     * @return true if page has rendered
     */
    private boolean dataRendered()
    {
        // First check bottom pagination is rendered
        if (paginatorRendered())
        {
            // If result is expected
            if (shouldHaveFiles)
            {
                if (logger.isTraceEnabled())
                {
                    logger.trace("dataRendered check with shouldHaveFiles ");
                }
                return hasData();
            }
            // otherwise let it be true
            return true;
        }
        return false;
    }

    /**
     * Checks if file upload message appears, this indicates there are no files or folders in the document library page
     *
     * @return true if message is displayed
     */
    private boolean hasNoData()
    {
        try
        {
            WebElement td = driver.findElement(By.cssSelector("tbody.yui-dt-message tr td"));
            boolean visible = td.isDisplayed();
            return visible;
        }
        catch (NoSuchElementException e)
        {
        }
        return false;

    }

    /**
     * Check to see if document library contains files or folders with tags.
     *
     * @return true if files or folders are displayed
     */
    private boolean hasData()
    {
        // Look for results
        try
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("starting hasData check");
            }
            if (hasNoData())
            {
                if (logger.isTraceEnabled())
                {
                    logger.trace("hasNoData returned true so returning false");
                }
                return false;
            }
            if (logger.isTraceEnabled())
            {
                logger.trace("checking has content rows");
            }
            boolean hasContentRows = hasFiles();
            if (logger.isTraceEnabled())
            {
                logger.trace("checking has content row done hasContentRows: " + hasContentRows);
            }
            return hasContentRows;
        }
        catch (NoSuchElementException e)
        {
        }
        return false;
    }


    /**
     * Checks to verify if we are in the correct sub folder document library page.
     *
     * @return true if bread crumb match location of the sub folder name.
     */
    private boolean isSubFolderDocLib(final String name)
    {
        // If we are expected to be in sub folder assert by checking the bread crumb
        try
        {
            List<WebElement> list = driver.findElements(By.cssSelector("div[id$='default-breadcrumb']>div.crumb>span.label>a"));
            WebElement element = list.get(list.size() - 1);
            String folderName = element.getText();
            if (name.equalsIgnoreCase(folderName))
            {
                return true;
            }
        }
        catch (Exception e)
        {
            logger.debug("Unable to determine if in sub folder of: " + subfolderName);
        }

        return false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public DocumentLibraryPage render()
    {
        return render(new RenderTime(maxPageLoadingTime));
    }

    public DocumentLibraryPage renderItem(final long time, String itemName)
    {
        elementRender(new RenderTime(time), RenderElement.getVisibleRenderElement(By.xpath(String.format("//h3/span/a[text()='%s']/../../../../..", itemName))));
        return this;
    }

    /**
     * Verify if the page viewed is the document library page.
     */
    public boolean isDocumentLibrary()
    {
        try
        {
            return driver.findElement(By.cssSelector("#alfresco-documentlibrary")).isDisplayed();
        }
        catch (NoSuchElementException e)
        {
        }
        return false;
    }

    DocumentLibraryNavigation documentLibraryNavigation;
    /**
     * Get document library sub navigation.
     *
     * @return {@link DocumentLibraryNavigation} object.
     */
    public DocumentLibraryNavigation getNavigation()
    {
        return documentLibraryNavigation;
    }

    /**
     * Get filmstrip view actions
     *
     * @return {@link FilmstripActions} object.
     */
    public FilmstripActions getFilmstripActions()
    {
        return factoryPage.instantiatePage(driver,FilmstripActions.class).render();
    }
    TreeMenuNavigation treeMenuNavigation;
    /**
     * Get document library left hand navigation menu trees.
     *
     * @return {@link TreeMenuNavigation} object.
     */
    public TreeMenuNavigation getLeftMenus()
    {
        return treeMenuNavigation;
    }

    /**
     * Check is instruction message is displayed. If it is displayed then it is a good indication that there are no files or folder on the page.
     *
     * @return if displayed
     */
    public boolean isFileUploadInstructionDisplayed()
    {
        try
        {
            boolean displayed = driver.findElement(By.cssSelector(FILE_UPLOAD_INSTRUCTION)).isDisplayed();
            if (logger.isTraceEnabled())
            {
                logger.trace(String.format("** File upload instruction is displayed: %s", displayed));
            }
            return displayed;
        }
        catch (NoSuchElementException e)
        {
        }
        return false;
    }

    /**
     * Checks document list is populated by injecting a javascript in to an alfresco component that renders the document list.
     *
     * @return true if collection of documents exists
     */
    public boolean hasFiles()
    {
        try
        {
            ArrayList<?> objs = (ArrayList<?>) executeJavaScript(JS_SCRIPT_CHECK_DOCLIST);
            if (!objs.isEmpty())
            {
                return true;
            }
        }
        catch (Exception e)
        {
        }
        return false;
    }

    /**
     * Extracts the results from result table that matches the file name.
     *
     * @return Collection of {@link FileDirectoryInfo} relating to result
     */
    public List<FileDirectoryInfo> getFiles()
    {
        try
        {
            boolean noFiles = !hasFiles();
            if (logger.isTraceEnabled())
            {
                logger.trace(String.format("Documet list has no files: %s", noFiles));
            }

            if (noFiles)
            {
                return Collections.emptyList();
            }

            List<WebElement> results = driver.findElements(By.cssSelector("tr.yui-dt-rec "));
            if (logger.isTraceEnabled())
            {
                logger.trace(String.format("getFiles list is empty: %s file size %d", results.isEmpty(), results.size()));
            }

            if (!results.isEmpty())
            {
                List<FileDirectoryInfo> fileDirectoryList = new ArrayList<FileDirectoryInfo>();
                for (WebElement result : results)
                {
                    String nodeRef = result.findElement(By.cssSelector("input[type='checkbox']")).getAttribute("value");
                    FileDirectoryInfo file = getFileDirectoryInfo(nodeRef, result);
                    logger.debug("adding file" + file.getName());
                    if (logger.isTraceEnabled())
                    {
                        logger.trace("adding file" + file.getName());
                    }
                    fileDirectoryList.add(file);
                }
                return fileDirectoryList;
            }
            // Try again as we are expecting results.
            return getFiles();
        }
        catch (NoSuchElementException e)
        {
        }
        catch (StaleElementReferenceException e)
        {
            if (logger.isTraceEnabled())
            {
                logger.debug("found stale element retrying get files");
            }
        }
        // Try again as we should have results else upload instructions view would be piked up.
        return getFiles();
    }

    /**
     * Selects the title of the document link.
     *
     * @param title String file title
     * @return DocumentDetailsPage page response object
     */
    public HtmlPage selectFile(final String title)
    {
        selectEntry(title).click();
        waitUntilAlert();
        return getCurrentPage();
    }

    
    /**
     * Selects the title of the document link edited offline.
     *
     * @param title String file title
     * @return DocumentDetailsPage page response object
     */
    public DocumentEditOfflinePage selectFileEditedOffline(final String title)
    {
        selectEntry(title).click();
        waitUntilAlert();
        return factoryPage.instantiatePage(driver, DocumentEditOfflinePage.class);
    }
    
    
    /**
     * Selects the title of the folder link.
     *
     * @param title String folder title
     * @return HtmlPage page response object
     */

    public HtmlPage selectFolder(final String title)
    {
        try
        {
            selectEntry(title).click();
            waitUntilAlert();
        }
        catch(TimeoutException e)
        {
            throw new PageOperationException("Timeout locating folder: " + title, e);
        }
        return getCurrentPage();
    }

    /**
     * Selects an entry regardless of type (file or folder).
     * The entry selected is based on the name displayed and not the actual
     * file name hence we are using the function normalize-space().
     * An example:
     * An entry may display Hello world.txt but in the html it would be
     * Hello        world.txt
     * 
     *
     * @return WebElement
     */
    protected WebElement selectEntry(final String title)
    {
        if (title == null || title.isEmpty())
            throw new IllegalArgumentException("Title is required");
        String xpath = "//h3/span/a[normalize-space(text())='%s']";

        switch (viewType)
        {
            case TABLE_VIEW:
                xpath = "//td[contains(@class,'yui-dt-col-name')]/div/span/a[normalize-space(text())='%s']";
                break;
            case DETAILED_VIEW:    
                xpath = "//a[text()='%s']";
                break;
            default:
                break;
        }

        if(title == null || title.isEmpty()) {throw new IllegalArgumentException("Title is required");}
        String search = String.format(xpath, title);
        try
        {
            return findAndWait(By.xpath(search), getDefaultWaitTime());
        }
        catch(Exception e)
        {
            throw new NoSuchElementException("Unabel to find element, query: " + search);
        }
    }

    /**
     * Selects file or folder from page.
     *
     * @param name identifier
     * @return true if selected
     */
    private synchronized FileDirectoryInfo findFileOrFolder(final String name)
    {
        if (name == null || name.isEmpty())
        {
            throw new IllegalArgumentException("Name is required");
        }
        if (logger.isTraceEnabled())
        {
            logger.trace("Look in collection for: " + name);
        }
        List<FileDirectoryInfo> files = getFiles();
        for (FileDirectoryInfo file : files)
        {
            String fileName = file.getName();
            if (logger.isTraceEnabled())
            {
                logger.trace(String.format("matching %s against %s", name, fileName));
            }
            if (name.equalsIgnoreCase(fileName))
            {
                return file;
            }
        }
        if (logger.isTraceEnabled())
        {
            logger.trace(String.format("content: %s not found", name));
        }
        throw new PageOperationException("Unable to locate fileName: " + name);
    }

    /**
     * Checks to see if file is visible on the page.
     *
     * @param fileName String title
     * @return true if file exists on the page
     */
    public synchronized boolean isFileVisible(final String fileName)
    {
        if (fileName == null || fileName.isEmpty())
        {
            throw new UnsupportedOperationException("File name required");
        }
        try
        {
            for (FileDirectoryInfo file : getFiles())
            {
                if (fileName.equalsIgnoreCase(file.getName()))
                {
                    return true;
                }
            }
        }
        catch (PageOperationException e)
        {
        }
        return false;
    }

    /**
     * Checks to see if files is on the page.
     *
     * @return true if file exists on the page
     */
    public boolean isFilesVisible()
    {
        try
        {
            WebElement content = driver.findElement(By.cssSelector("div[id$='default-documents']"));
            return content.isDisplayed();
        }
        catch (NoSuchElementException nse)
        {
            return false;
        }
    }

    /**
     * The number of comments value displayed on the span comment count.
     *
     * @return {@link Integer} total number of comments
     */
    public Integer getCommentCount()
    {
        try
        {
            WebElement span = driver.findElement(By.cssSelector("span.comment-count"));
            return Integer.valueOf(span.getText());
        }
        catch (NoSuchElementException nse)
        {
            return 0;
        }
    }

    /**
     * Checks if pagination next button is active.
     *
     * @return true if next page exists
     */
    public boolean hasNextPage()
    {
        return Pagination.hasPaginationButton(driver, PAGINATION_BUTTON_NEXT);
    }

    /**
     * Checks if pagination previous button is active.
     *
     * @return true if next page exists
     */
    public boolean hasPreviousPage()
    {
        return Pagination.hasPaginationButton(driver, PAGINATION_BUTTON_PREVIOUS);
    }

    /**
     * Selects the button next on the pagination bar.
     */
    public HtmlPage selectNextPage()
    {
        Pagination pagination = (Pagination)factoryPage.instantiatePageElement(driver,Pagination.class);
        return pagination.selectPaginationButton(driver, PAGINATION_BUTTON_NEXT);
    }

    /**
     * Selects the button previous on the pagination bar.
     */
    public HtmlPage selectPreviousPage()
    {
        Pagination pagination = (Pagination)factoryPage.instantiatePageElement(driver,Pagination.class);
        return pagination.selectPaginationButton(driver, PAGINATION_BUTTON_PREVIOUS);
    }

    /**
     * Locates the file or folder and deletes it.
     *
     * @param name String identifier
     * @return page response
     */
    public HtmlPage deleteItem(final String name)
    {
        if (name == null)
        {
            throw new IllegalArgumentException("require name value");
        }
        FileDirectoryInfo item = getFileDirectoryInfo(name);
        item.selectDelete();
        confirmDelete();
        return getCurrentPage();
    }

    /**
     * Locates the file or folder and deletes it.
     *
     * @param number int identifier
     * @return page response
     */
    public HtmlPage deleteItem(final int number)
    {
        FileDirectoryInfo item = getFileDirectoryInfo(number);
        item.selectDelete();
        confirmDelete();
        return getCurrentPage();
    }

    /**
     * Action of selecting ok on confirm delete pop up dialog.
     */
    private void confirmDelete()
    {
        WebElement confirmDelete = driver.findElement(By.cssSelector("div#prompt div.ft span span button"));
        confirmDelete.click();
        if (logger.isTraceEnabled())
        {
            logger.trace("deleting");
        }
    }

    /**
     * Returns the ShareContentRow for the selected contentName.
     *
     * @param name String content name identifier
     * @return {@link FileDirectoryInfo}
     * @deprecated use getFileDirectoryInfo
     */
    public synchronized FileDirectoryInfo getContentRow(final String name)
    {
        if (name == null || name.isEmpty())
        {
            throw new UnsupportedOperationException("Name input value is required");
        }
        return findFileOrFolder(name);
    }

    /**
     * This method does the clicking on given tag name, which presents under the Tags Tree menu on Document Library page.
     * <br>depricated Use {@link TreeMenuNavigation#selectTagNode(String)} instead.
     *
     * @param tagName String
     * @return {@link DocumentLibraryPage}
     *
     */
    @Deprecated
    public HtmlPage clickOnTagNameUnderTagsTreeMenuOnDocumentLibrary(String tagName)
    {
        if (tagName == null)
        {
            throw new UnsupportedOperationException("TagName is required.");
        }

        String text = null;
        List<WebElement> tags = getAllTags();

        for (WebElement tag : tags)
        {
            text = tag.getText();

            if (text != null && text.equalsIgnoreCase(tagName))
            {
                tag.click();
                return getCurrentPage();
            }
        }

        throw new PageException("Not able to find the given tag : " + tagName);

    }

    /**
     * This method gets the all tag elements present on document library Tags tree menu.
     *
     * @return List<WebElement>
     */
    private List<WebElement> getAllTags()
    {
        try
        {
            return findAndWaitForElements(By.cssSelector(ALL_TAGS_PRESENT_ON_DOC_LIB));
        }
        catch (TimeoutException e)
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("Exceeded the time to find the All Tags css.", e);
            }
            return Collections.emptyList();
        }
    }

    /**
     * This method gets the list of tag names present on document library Tags tree menu.
     *
     * @return List<String>
     */
    public List<String> getAllTagNames()
    {
        List<String> tagNames = new ArrayList<String>();
        String text = null;

        List<WebElement> tags = getAllTags();

        for (WebElement tag : tags)
        {
            text = tag.getText();

            if (text != null)
            {
                tagNames.add(text);
            }
        }
        return tagNames;
    }


    /**
     * This method gets the list of tag names present on document library Categories tree menu.
     *
     * @return List<String>
     */
    public List<String> getAllCategoriesNames()
    {
        List<String> categoryNames = new ArrayList<>();
        String text;
        openCategoriesTree();
        WebElement spacer = findAndWait(By.xpath(CATEGORY_ROOT_SPACER), 5000);
        if(spacer.getAttribute("class").contains("collapsed"))
        {
            findAndWait(CATEGORY_ROOT_SPACER_LINK).click();
            waitUntilElementPresent(CATEGORY_ROOT_SPACER_LINK, 5);
        }

        List<WebElement> categories = getAllCategories();

        for (WebElement category : categories)
        {
            text = category.getText();

            if (text != null)
            {
                categoryNames.add(text);
            }
        }
        return categoryNames;
    }

    /**
     * This method gets the all categories elements present on document library Categories tree menu.
     *
     * @return List<WebElement>
     */
    private List<WebElement> getAllCategories()
    {
        try
        {
            return findAndWaitForElements(ALL_CATEGORIES_PRESENT_ON_DOC_LIB);
        }
        catch (TimeoutException e)
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("Exceeded the time to find the All Categories css.", e);
            }
            return Collections.emptyList();
        }
    }

    public boolean isItemVisble(String contentName)
    {
        PageUtils.checkMandotaryParam("contentName", contentName);
        try
        {
            return null != findFileOrFolder(contentName);
        }
        catch (Exception e)
        {
        }
        return false;
    }

    /**
     * Select a particular file directory info row based on the count, the accepted range is 1-50.
     *
     * @param number Integer item row
     * @return {@link FileDirectoryInfo} page response
     */
    public FileDirectoryInfo getFileDirectoryInfo(final Integer number)
    {
        if (number == null || !((number > 0) && (number < 50)))
        {
            throw new IllegalArgumentException("A valid number range of 1 to 50 is required");
        }
        try
        {
            WebElement row = driver.findElement(By.cssSelector(String.format("tbody.yui-dt-data tr:nth-of-type(%d)", number)));
            String nodeRef = row.findElement(THUMBNAIL_IMAGE).getAttribute("id");
            return getFileDirectoryInfo(nodeRef, row);
        }
        catch (NoSuchElementException e)
        {
            throw new PageException(String.format("File directory info row %d was not found", number), e);
        }
    }

    /**
     * Select a particular file directory info row based on the title.
     *
     * @param title String item title
     * @return {@link FileDirectoryInfo} page response
     */
    public FileDirectoryInfo getFileDirectoryInfo(final String title)
    {
        if (title == null || title.isEmpty())
        {
            throw new IllegalArgumentException("Title is required");
        }
        WebElement row;
        String nodeRef;
        try
        {
            String query = String.format("//h3/span/a[text()='%s']/../../../../..", title);
            row = driver.findElement(By.xpath(query));
            nodeRef = row.findElement(THUMBNAIL_IMAGE).getAttribute("id");
            return getFileDirectoryInfo(nodeRef, row);
        }
        catch (NoSuchElementException e)
        {
            throw new PageOperationException(String.format("File directory info with title %s was not found", title), e);
        }
        catch (TimeoutException e)
        {
            throw new PageOperationException(String.format("File directory info with title %s was not found", title), e);
        }
    }

    public boolean isShouldHaveFiles()
    {
        return shouldHaveFiles;
    }

    public void setShouldHaveFiles(boolean shouldHaveFiles)
    {
        this.shouldHaveFiles = shouldHaveFiles;
    }

    /**
     * @param optype Optype
     * @return boolean
     */
    public boolean isMessagePresent(Optype optype)
    {

        try
        {
            String message = driver.findElement(By.cssSelector(".message")).getText();
            switch (optype)
            {
                case SYNC:
                    return "Sync was created".equals(message) ? true : false;
                case UNSYNC:
                    return "Sync has been removed".equals(message) ? true : false;
                case REQ_TO_SYNC:
                    return "Successfully requested Sync".equalsIgnoreCase(message) ? true : false;
                default:
                    throw new PageOperationException(message);
            }
        }
        catch (TimeoutException toe)
        {
            logger.error("Message element not found!!", toe);
        }
        throw new PageOperationException("Message element not found!!");
    }
    
    /**
     * Returns true if Sync message is present
     *
     * @return boolean
     */
    
    public boolean isSyncMessagePresent()
    {
        try
        {
            waitForElement(SYNC_MESSAGE, SECONDS.convert(getDefaultWaitTime(), MILLISECONDS));
            WebElement syncMessage = driver.findElement(SYNC_MESSAGE);
            if (syncMessage != null)
            {
                return true;
            }
        }
        catch(TimeoutException toe)
        {
            logger.error("Message element not found!!", toe);
            return false;
        }
        return false;
    }
    

    /**
     * Returns true if Cloud Sync sign up dialog is visible
     *
     * @return boolean
     */
    public boolean isSignUpDialogVisible()
    {
        try
        {
            return findAndWait(By.cssSelector("form.cloud-auth-form")).isDisplayed();
        }
        catch (TimeoutException te)
        {
            return false;
        }
    }

    /**
     * This method returns the count for the given tag string.
     * <br>depricated Use {@link TreeMenuNavigation#getTagCount(String)}
     *
     * @param tagName String
     * @return int
     *
     */
    @Deprecated
    public int getTagsCountUnderTagsTreeMenu(String tagName)
    {
        if (tagName == null)
        {
            throw new UnsupportedOperationException("TagName is required.");
        }

        try
        {
            String count = findAndWait(By.xpath(String.format(".//ul[@class='filterLink']//a[@rel='%s']/..", tagName.toLowerCase()))).getText();

            return Integer.parseInt(count.substring(count.indexOf("(") + 1, count.indexOf(")")));
        }
        catch (TimeoutException te)
        {
            logger.error("Exceeded time to find out the " + tagName + " count: ", te);
        }
        catch (NumberFormatException ne)
        {
            logger.error("Unable to convert tags count string value into int : ", ne);
        }

        throw new PageException("Unable to find the given tag count : " + tagName);
    }

    /**
     * Check the documents tree is expanded or not , on DocumentLibraryPage or Repository Page.
     *
     * @return - boolean
     */
    public boolean isDocumentsTreeExpanded()
    {
        try
        {
            WebElement documents = driver.findElement(DOCUMENTS_TREE_CSS);
            if (documents.getAttribute("class").contains("open"))
            {
                return true;
            }
        }
        catch (NoSuchElementException e)
        {
            logger.error("Exceeded time to find the documents tree.", e);
        }

        return false;
    }

    /**
     * This method is used to click on the documents tree, on DocumentLibraryPage or Repository Page.
     *
     * @return - DocumentLibraryPage
     */
    public HtmlPage clickDocumentsTreeExpanded()
    {
        try
        {
            findAndWait(DOCUMENTS_TREE_CSS).click();
            return getCurrentPage();
        }
        catch (TimeoutException e)
        {
            logger.error("Exceeded time to find the documents tree.", e);
        }
        throw new PageException("Unable to find the Documents Tree link.");
    }

    /**
     * This method is used to open on the categories tree, on DocumentLibraryPage or Repository Page.
     *
     * @return - DocumentLibraryPage
     */
    public HtmlPage openCategoriesTree()
    {
        try
        {
            WebElement cat = findAndWait(CATEGORIES_IN_TREE);
            if(cat.getAttribute("class").contains("closed"))
                findAndWait(CATEGORIES_TREE_CSS).click();
            return getCurrentPage();
        }
        catch (TimeoutException e)
        {
            logger.error("Exceeded time to find the categories tree.", e);
        }
        throw new PageException("Unable to find the Categories Tree link.");
    }


    private FileDirectoryInfo getFileDirectoryInfo(String nodeRef, WebElement webElement)
    {
        if (viewType == null)
        {
            throw new UnsupportedOperationException("Document Library page render is needed.");
        }

        return FactoryShareFileDirectoryInfo.getPage(nodeRef, webElement, driver, viewType, factoryPage);
    }

    protected void setViewType(ViewType viewType)
    {
        this.viewType = viewType;
    }

    /**
     * Returns the current view type if it set already or by calling render it sets the view type.
     *
     * @return {@link ViewType}
     */
    public ViewType getViewType()
    {
        return viewType;
    }

    /**
     * Click on "My Favourites" it will take you to document library/Repository page.
     *
     * @return HtmlPage
     */
    public HtmlPage clickOnMyFavourites()
    {
        try
        {
            WebElement element = driver.findElement(MY_FAVOURITES);
            if (element.isDisplayed())
            {
                element.click();
                waitUntilAlert();
                return getCurrentPage();
            }

        }
        catch (NoSuchElementException nse)
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("My Favourites is not loaded", nse);
            }
        }
        throw new PageOperationException("My Favourites not loaded - My Favourites tape may not be displayed.");
    }

    /**
     * Click on "Recently Modified" it will take you to document library/Repository page.
     *
     * @return DocumentLibraryPage
     */
    public HtmlPage clickOnRecentlyModified()
    {
        try
        {
            WebElement element = driver.findElement(RECENTLY_MODIFIED);
            if (element.isDisplayed())
            {
                element.click();
                waitUntilAlert();
                return getCurrentPage();
            }

        }
        catch (NoSuchElementException nse)
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("Recently Modified is not loaded", nse);
            }
        }
        throw new PageOperationException("Recently Modified not loaded - Recently Modified tape may not be displayed.");
    }

    /**
     * Click on "Recently Added" it will take you to document library/Repository page.
     *
     * @return DocumentLibraryPage
     */
    public HtmlPage clickOnRecentlyAdded()
    {
        try
        {
            WebElement element = driver.findElement(RECENTLY_ADDED);
            if (element.isDisplayed())
            {
                element.click();
                waitUntilAlert();
                return getCurrentPage();
            }

        }
        catch (NoSuchElementException nse)
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("Recently Added is not loaded", nse);
            }
        }
        throw new PageOperationException("Recently Added not loaded - Recently Added tape may not be displayed.");
    }

    public HtmlPage clickOnAllDocuments()
    {
        try
        {
            WebElement element = driver.findElement(ALL_DOCUMENTS);
            if (element.isDisplayed())
            {
                element.click();
                waitUntilAlert();
                return getCurrentPage();
            }

        }
        catch (NoSuchElementException nse)
        {
            if (logger.isTraceEnabled())
            {
                logger.trace("All Documents is not loaded", nse);
            }
        }
        throw new PageOperationException("All Documents - All Documents tape may not be displayed.");
    }

    /**
     * Returned Object mimic all action with Pagination.
     *
     * @return PaginationForm
     */
    public PaginationForm getBottomPaginationForm()
    {
        PaginationForm bottomPagination = new PaginationForm(driver, By.xpath("//div[contains(@id,'default-paginatorBottom')]"));
        if (bottomPagination.isDisplay())
        {
            return bottomPagination;
        }
        logger.trace("Can't find Bottom pagination on Page");
        throw new PageOperationException("Can't find Bottom pagination on Page");
    }


    /**
     * Click on Category link in left tree menu.
     *
     * @param categoryName Categories
     * @return PaginationForm
     */
    public HtmlPage clickOnCategory(Categories categoryName)
    {
        List<WebElement> categories = findAndWaitForElements(CATEGORIES_IN_TREE);
        for (WebElement category : categories)
        {
            if (category.getText().equals(categoryName.getValue()))
            {
                category.click();
                waitUntilAlert();
                return getCurrentPage().render();
            }
        }
        throw new PageOperationException(String.format("Category didn't found [%s]", categoryName));
    }

    /**
     * Create content from template
     *
     * @param templateName String
     * @return {@link DocumentLibraryPage}
     */
    public HtmlPage createContentFromTemplate(String templateName)
    {
        try
        {
            if(!templateName.isEmpty())
            {
                getNavigation().selectCreateContentFromTemplate();
                WebElement template = getTemplate(templateName);
                template.click();

                return  getCurrentPage();
        }
        }
        catch (StaleElementReferenceException ste)
        {
            // DOM has changed therefore page should render once change is completed
        }

        throw new PageOperationException(String.format("Template didn't found [%s]", templateName));

    }

    /**
     * Create folder from template
     *
     * @param templateName String
     * @return {@link DocumentLibraryPage}
     */
    public HtmlPage createFolderFromTemplate(String templateName)
    {
        try{
            if(!templateName.isEmpty()){
                getNavigation().selectCreateFolderFromTemplate();
                WebElement template = getTemplate(templateName);
                template.click();
                WebElement okButton = findAndWait(submitButton);
                okButton.click();
                waitUntilMessageAppearAndDisappear("Folder", SECONDS.convert(maxPageLoadingTime, MILLISECONDS));
                DocumentLibraryPage page = getCurrentPage().render();
                page.setShouldHaveFiles(true);
                return getCurrentPage();
            }
        }
        catch (StaleElementReferenceException ste)
        {
            // DOM has changed therefore page should render once change is completed
        }

        throw new PageOperationException(String.format("Template didn't found [%s]", templateName));

    }

    /**
     * find all existing templates.
     * 'Create content from template' or 'Create folder from template' menu must be chosen
     *
     * @return List<WebElement> of all existing templates
     */
    public List<WebElement> getTemplateList(){
        getCurrentPage().render();
        waitUntilNotVisibleWithParitalText(By.xpath(TEMPLATE_LIST), "Loading...", SECONDS.convert(maxPageLoadingTime, MILLISECONDS));
        findAndWaitForElements(By.xpath(TEMPLATE_LIST));
        return findDisplayedElements(By.xpath(TEMPLATE_LIST));
    }

    /**
     * find needed template from lists of templates
     *
     * @param templateName String
     * @return WebElement of needed template
     */
    public WebElement getTemplate(String templateName){
        List<WebElement> list = getTemplateList();
        if(list.get(0).getText().contains("Loading")){
            while (list.get(0).getText().contains("Loading")){
                list = getTemplateList();
                getCurrentPage().render();
            }

        }
        for(WebElement template : list){
            if(template.getText().equals(templateName)){
                return  template;
            }
        }
        throw new PageOperationException(String.format("Template [%s] didn't found on page.", templateName));
    }

    /**
     * Create content from template
     *
     * @param templateName String
     * @return {@link DocumentLibraryPage}
     */
    public HtmlPage createContentFromTemplateHover(String templateName)
    {
        try{
            if(!templateName.isEmpty()){
                getNavigation().selectCreateContentFromTemplateHover().render();
                WebElement template = getTemplate(templateName);
                template.click();

                return  getCurrentPage();
            }
        }
        catch (StaleElementReferenceException ste)
        {
        }

        throw new PageOperationException(String.format("Template didn't found [%s]", templateName));

    }

    /**
     * Create folder from template
     *
     * @param templateName String
     * @return {@link DocumentLibraryPage}
     */
    public DocumentLibraryPage createFolderFromTemplateHover(String templateName)
    {
        try{
            if(!templateName.isEmpty()){
                getNavigation().selectCreateFolderFromTemplateHover().render();
//                WebElement template = getTemplate(templateName);
//                template.click();
                findAndWait(By.xpath("//div[@class='bd']//span[contains(text(), '" + templateName + "')]")).click();
//                getCurrentPage().render();
                WebElement okButton = findAndWait(submitButton);
                okButton.click();
                waitUntilMessageAppearAndDisappear("Folder", SECONDS.convert(maxPageLoadingTime, MILLISECONDS));
                DocumentLibraryPage page = getCurrentPage().render();
                page.setShouldHaveFiles(true);
                return page;
            }
        }
        catch (StaleElementReferenceException ste)
        {
        }

        throw new PageOperationException(String.format("Template didn't found [%s]", templateName));

    }

    /**
     * Method checks if check-box present at a page
     *
     * @return true if present
     */
    public boolean isCheckBoxPresent()
    {
        try
        {
            return findAndWait(By.cssSelector(CHECK_BOX)).isDisplayed();
        }
        catch (NoSuchElementException nse)
        {
            logger.error(CHECK_BOX + " isn't present at a page");
            return false;
        }
    }


    /**
     * Create folder from template
     *
     * @param templateName String
     * @return {@link DocumentLibraryPage}
     */
    public HtmlPage openFolderFromTemplateHover(String templateName)
    {
        try{
            if(!templateName.isEmpty()){
                getNavigation().selectCreateFolderFromTemplateHover().render();
                findAndWait(By.xpath("//div[@class='bd']//span[contains(text(), '" + templateName + "')]")).click();
                findAndWait(submitButton);
                
                return getCurrentPage();
            }
        }
        catch (StaleElementReferenceException ste)
        {
        }
        throw new PageOperationException(String.format("Template didn't found [%s]", templateName));

    }

    /**
     * The method helps to navigate to a folder or a file from document library.
     * @param title String
     * @return HtmlPage
     */
    public HtmlPage browseToEntry(String title) throws Exception
    {
        DocumentLibraryPage documentLibraryPage = getCurrentPage().render();
        FileDirectoryInfo fileInfo = documentLibraryPage.getFileDirectoryInfo(title);

        if (fileInfo.isFolder())
        {
            String url = selectEntry(title).getAttribute("href");
            String param = selectEntry(title).getAttribute("rel");
            param = param.substring(1, param.length());

            URIBuilder b = new URIBuilder(url);
            b.addParameter("filter", param);
            url = b.build().toString();
            driver.navigate().to(url);
        }
        else
        {
            String url = selectEntry(title).getAttribute("href");
            driver.navigate().to(url);
        }

        return getCurrentPage();
    }
}
