package com.atlassian.webdriver.bitbucket.element;

import com.atlassian.pageobjects.elements.PageElement;
import com.atlassian.pageobjects.elements.WebDriverLocatable;
import com.atlassian.pageobjects.elements.query.Poller;
import com.atlassian.pageobjects.elements.query.TimedCondition;
import com.atlassian.pageobjects.elements.timeout.TimeoutType;
import com.google.common.base.Supplier;
import org.openqa.selenium.By;

import java.util.List;

public class DiffFileContent extends FileContent {

    public DiffFileContent(PageElement pageElement) {
        super(pageElement);
    }

    public DiffFileContent(By locator, TimeoutType timeoutType) {
        super(locator, timeoutType);
    }

    public DiffFileContent(By locator, WebDriverLocatable parent, TimeoutType timeoutType) {
        super(locator, parent, timeoutType);
    }

    public DiffToolbar getToolbar() {
        return waitForSourceContainer(new Supplier<DiffToolbar>() {
            @Override
            public DiffToolbar get() {
                return elementFinder.find(By.className("file-toolbar"), DiffToolbar.class);
            }
        });
    }

    public DiffCommentForm getNewFileCommentForm() {
        return this.find(By.cssSelector(".file-comments .new-comment-form"), DiffCommentForm.class, TimeoutType.COMPONENT_LOAD);
    }

    public DiffCommentForm openNewFileCommentForm() {
        Poller.waitUntilTrue(this.find(By.className("fully-loaded")).timed().isPresent());
        this.find(By.className("add-file-comment-trigger")).click();
        return getNewFileCommentForm();
    }

    public List<Comment> getFileComments() {
        return this.findAll(By.cssSelector(".file-comments .comment"), Comment.class);
    }

    protected <T> T waitForSourceContainer(Supplier<T> supplier) {
        waitUntilPageLoaded();
        return supplier.get();
    }

    public static class Line extends FileContent.Line {

        public Line(By locator, TimeoutType timeoutType) {
            super(locator, timeoutType);
        }

        public Line(WebDriverLocatable locatable, TimeoutType timeoutType) {
            super(locatable, timeoutType);
        }

        public boolean hasFocus() {
            return hasClass("last-focus");
        }

        public TimedCondition hasFocusCondition() {
            return timed().hasClass("last-focus");
        }

        public boolean commentsHaveFocus() {
            return hasClass("last-comment-focus");
        }

        public TimedCondition commentsHaveFocusCondition() {
            return timed().hasClass("last-comment-focus");
        }

        public boolean isAdd() {
            return hasClass("added");
        }

        public boolean isDelete() {
            return hasClass("removed");
        }

        public int getLine() {
            return Integer.parseInt(this.find(By.className("line-number-marker")).getAttribute("data-line-number"));
        }

        public String getLineType() {
            return this.find(By.className("line-number-marker")).getAttribute("data-line-type");
        }

        public Comment getComment(long id) {
            return pageBinder.bind(Comment.class, By.cssSelector(".comment[data-id=\"" + id + "\"]"));
        }

        public List<Comment> getComments() {
            return this.findAll(By.className("comment"), Comment.class);
        }

        /**
         * Optional add comment element
         */
        public PageElement getAddComment() {
            PageElement addComment = this.find(By.className("add-comment-trigger"));
            Poller.waitUntilTrue(addComment.timed().isVisible());
            return addComment;
        }

        public DiffCommentForm getNewCommentForm() {
            return this.find(By.className("new-comment-form"), DiffCommentForm.class, TimeoutType.COMPONENT_LOAD);
        }

        public DiffCommentForm openNewCommentForm() {
            getAddComment().click();
            return getNewCommentForm();
        }

        @Override
        public String getSourceText() {
            String text = find(By.tagName("pre")).getText();
            // CodeMirror adds a &nbsp; to empty lines.
            // With this implementation we still can't differentiate between a real line with one space and an empty line,
            // but we'll cross that bridge when we get there.
            return " ".equals(text) ? "" : text;
        }

        public void scrollIntoView() {
            this.javascript().execute(
                    "var $this = jQuery(arguments[0]);" +
                            "var cm = $this.closest('.CodeMirror')[0].CodeMirror;" +
                            "var where = { ch: 0, line: $this.prevAll().length };" +
                            "var margin = 200;" +
                            "cm.scrollIntoView(where, margin);");
        }
    }

    public static class DiffToolbar extends LegacyElementPageObject {

        public DiffToolbar(PageElement pageElement) {
            super(pageElement);
        }

        public DiffToolbar(By locator) {
            super(locator);
        }

        public String getBreadCrumbs() {
            return this.find(By.className("breadcrumbs")).getText();
        }

        public String getChangeType() {
            return this.find(By.className("change-type-lozenge")).getText();
        }

        public String getSourceLinkUrl() {
            return this.find(By.className("source-view-link")).getAttribute("href");
        }
    }
}
