"use strict";
var app = angular.module('mindinkControllers');

app.controller('ereaderCtrl',['epubLocation', '$scope', '$routeParams','HighlightService', 'highlighter', '$location', '$cookieStore','NoteService', 'MiEndpoint',
function(epubLocation, $scope, $routeParams, highlightService, highlighter, $location, $cookieStore, noteService, MiEndpoint) {
    var bookISBN = $routeParams.ISBN;
    var chapterHash={};
    var hintNote;
    $scope.showTabletMenu = false;

    renderBook(bookISBN);

    $scope.mi = MiEndpoint;

    $scope.closeReader = function(){
        $location.path("/");
        $scope.$apply();
    };

    $scope.toChapterName = function(x){
        function humanizePath(str){
            var arr = str.split('.');
            arr.pop();
            str = arr.join().split('/').join(' ')
            return str;
        }
        return chapterHash[x] || {label: humanizePath(x)};
    };

    $scope.loadCfi = function(cfi){
        if (Book){
            Book.displayChapter(cfi);
        }
    };

    $scope.saveNote = function(){
        if ($scope.currentNote.id){
            noteService.updateNote($scope.currentNote).then(function(note){
                $scope.groupedNotes = $scope.updateGroupings();
                $scope.currentNote = null;
            }, function(){
                console.error("An error occurred");
            });
        }
        else{
            noteService.createNote($scope.currentNote).then(function(note){
                $scope.bookNotes.push(note);
                $scope.groupedNotes = $scope.updateGroupings();
                $scope.currentNote = null;
            }, function(){
                console.error("An error occurred");
            });
        }
    };

    $scope.updateNote = function(){
        noteService.updateNote($scope.currentNote).then(function(note){
            $scope.groupedNotes = $scope.updateGroupings();
        }, function(){
            console.error("An error occurred");
        });
    };

    $scope.deleteNote = function(){
        noteService.deleteNote($scope.currentNote).then(function(note){
            var oldId =  $scope.currentNote.id;
            $scope.currentNote = null;
            $scope.bookNotes = $scope.bookNotes.filter(function( obj ) {
                return obj.id !==  oldId;
            });
            $scope.groupedNotes = $scope.updateGroupings();
        }, function(){
            console.error("An error occurred");
        });
    }


    $scope.goToNoteLocation = function(obj){
        if (obj && obj.location){
            hintNote = obj;
            if (Book.chapter.href == obj.location.chapterHref){
                Book.render.gotoCfiFragment(obj.location.cfiLocation);
            }
            else{
                Book.gotoCfi(obj.location.cfiLocation);
            }
        }
    };

    $scope.setCurrentNote = function(obj){
        if (obj){
            if (obj.user.id === $scope.currentUser.id){
                obj.editable = true;
            }
            $scope.currentNote = obj;
            return;
        }

        var selection = highlighter.getSelectedText();
        $scope.currentNote = {
            user: $scope.currentUser,
            editable: true,
            selectedText: selection.text,
            location: {
                startOffset: selection.startOffset,
                endOffset: selection.endOffset,
                isbn: bookISBN,
                chapterHref: $scope.chapter.href,
                //This only works for the cfi location of the curent page and not the highlighted note.
                //Will need to manually parse the cfi location of the selected text
                cfiLocation: Book.render.getCfiForElement(selection.element),//Book.getCurrentLocationCfi(),
                chapterId: $scope.toChapterName($scope.chapter.href).label
            }
        }

        cleanView();
    };

    //Throw highlights in here
    var Book;
    var Reader;
    var fontSize;

    $scope.coverUrl = MiEndpoint.getBookCoverPath(bookISBN);

    $scope.updateGroupings = function(){
        var x = {};
        if ($scope.bookNotes){
            for (var i = 0; i < $scope.bookNotes.length; ++i) {
                var obj = $scope.bookNotes[i];

                if (obj){
                    if (x[obj.location.chapterHref] == undefined){
                        x[obj.location.chapterHref] = [];
                    }
                    x[obj.location.chapterHref].push(obj);
                }
            }
        }
        return x;
    };


    $scope.showHighlights = true;
    $scope.showNotes = true;
    $scope.showSocialNotes = true;
    $scope.chapterLoading = true;

    function cleanView(){
       setPopupLocation();
    };

    $scope.cleanView = function(){
        cleanView();
    };

    //Scope function controls for UI
    $scope.nextPage = function(){
        cleanView();
        Book.nextPage();
    };
    $scope.prevPage = function(){
        cleanView();
        Book.prevPage();
    };
    $scope.nextChapter = function(){
        cleanView();
        Book.nextChapter();
    };
    $scope.prevChapter = function(){
        cleanView();
        if (Book.spinePos > 0){
            Book.goto(Book.spine[Book.spinePos-1].href);
        }
    };

    $scope.loadChapter = function(chapter){
        cleanView();
        Book.goto(chapter.href)
    };

    $scope.highlightSelection = function(color){
        cleanView();
        highlighter.highlightSelection(color);
    };

    $scope.removeHighlightSelection = function(){
        cleanView();
        highlighter.removeHighlightSelection(function(removed){
            if (removed.length > 0){
                var arr = [];
                for (var i in removed){
                    arr.push(removed[i]);
                }
                highlightService.deleteHighlights(arr).then(function(response){
                    //Success
                }, function(repsonse){
                    //Error
                });
            }
        });
    };

    $scope.showHighlightColors = function(){
        $scope.popupState = "highlight";
    };

    $scope.showNote = function(note){

    };

    $scope.popupStyle = {
        display: 'none',
        left: '0px',
        top: '0px'
    };
    $scope.popupState = null;

    function setPopupLocation(obj){
        $scope.popupStyle = {
            display: 'none',
            left: '0px',
            top: '0px'
        };
        $scope.popupState = null;
        if (obj && obj.show){
            $scope.popupStyle.display = "block";
            $scope.popupStyle.left = obj.left+"px";
            $scope.popupStyle.top = obj.top+"px";
        }

        if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
            $scope.$apply();
        }

    };

    function updatePositions(cfi){
        $scope.chapterLength = Book.render.displayedPages;
        $scope.chapterPos = Book.render.chapterPos;

        //TODO: move progress bar into own directive
        $scope.progressBarWidth = {
            width: ($scope.chapterPos / $scope.chapterLength)*100 + "%"
        };

        if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
            $scope.$apply();
        };

        //Display a hint for the note if from note service
        if (hintNote){
            highlighter.showNoteHint(hintNote);
            $scope.noteDrawerOpen = false;
            hintNote = null;
        }

        $cookieStore.put(bookISBN, Book.getCurrentLocationCfi());
        cleanView();
    };

    function renderBook(ISBN){
        //Hook events before render
        EPUBJS.Hooks.register("beforeChapterDisplay").addHighlighter = function(callback, render){
            var chapter = render.book.chapter.href;
            highlighter.initializeHighlighter({
                document: render.doc,
                documentElement: render.docEl,
                iframe: render.iframe,
                setSelection: setPopupLocation,
                isTouchDevice: $scope.$root.isTouch,
                tabletShowMenu: function(value){
                    $scope.showTabletMenu = value;
                    if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
                        $scope.$apply();
                    }
                },
                updateHighlights: function(newHighlight){
                    if (newHighlight){
                        highlightService.createHighlight({
                            color: newHighlight.color,
                            location: {
                                startOffset: newHighlight.startOffset,
                                endOffset: newHighlight.endOffset,
                                isbn: bookISBN,
                                chapterHref: $scope.chapter.href,
                                cfiLocation: Book.getCurrentLocationCfi(),
                                chapterId: $scope.toChapterName($scope.chapter.href).label
                            }
                        }).then(function(highlight){
                            $scope.highlights.push(highlight);
                            highlighter.updateHighlights($scope.highlights);
                        });
                    }
                }
            });

            highlightService.getHighlights(bookISBN, chapter).then(function(response){
                $scope.highlights = response.highlights;
                highlighter.updateHighlights(response.highlights);
            });

            if (callback){callback();}
        };
        //Load book


        var locationCfi = $cookieStore.get(ISBN);
        Book = ePub({
            previousLocationCfi: locationCfi
        });


        //load notes
        noteService.getNotesForBook(ISBN).then(function(response){
            $scope.bookNotes = response.notes;
            $scope.groupedNotes = $scope.updateGroupings();

        });

        Book.open(epubLocation+ISBN+"/").then(function(){
            //Render to element]
            Reader = Book.renderTo("mindink-reader");

            //Register Table of Contents
            Book.getToc().then(function(toc){
                $scope.toc = toc;

                for (var i in toc){
                    chapterHash[toc[i].href] = {
                        label: toc[i].label,
                        id: i
                    }
                }

                $scope.chapter = $scope.toc[Book.spinePos];
                $scope.spine = Book.spine;
                $scope.$apply();
            });

            //Register MetaData
            Book.getMetadata().then(function(meta){
                $scope.meta = meta;
                $scope.$apply();
            });

            Book.on("renderer:resized", updatePositions);

            Book.on("renderer:pageChanged", updatePositions);
            Book.on("renderer:chapterDisplayed", function(){
                if ($scope.toc){
                    $scope.chapter = $scope.toc[Book.spinePos];
                }
                $scope.currentChapter = Book.spinePos + 1;
                $scope.chapterLoading = false;
                function doResizeIframe(){
                    Book.render.resizeIframe(document.getElementById("mindink-reader").offsetWidth, document.getElementById("mindink-reader").offsetHeight);
                }
                if( $scope.$root.isIos ) {
                    //iPad code to resize iframe. Possibly should be refactored at some point. Make cleaner etc.

                    window.onresize = function(event) {
                        try{
                            doResizeIframe();
                        }catch(e){}
                    };
                    doResizeIframe();
                }
            });
            Book.on("renderer:chapterUnloaded", function(){
                $scope.chapterLoading = true;
            });
        }, function(error){
           //There was an error loading the book
           $location.path("/");
            $scope.$apply();
        });
    };

    $scope.$on("$destroy", function(){
        Book.destroy();
    });
}])
.service('highlighter', ['coordinatesService',function(coordsService){
    var highlights = {};
    var highlighter;
    var document;
    var documentElement;
    var iframe;
    var setSelection;
    var updateHighlights;
    var tabletShowMenu;
    var setLatestHighlight;
    var colorPrefix = '__mi__';
    var colors = ['blue','yellow','red','green','orange'];
    var initialized = false;
    var isTouchDevice = false;

    var cachedSelection = null;
    var lastSelection = null;

//    START CODE FOR NOTE
//    TODO Move into seperate note service

        var noteHinter;

    var returnSelection = function(){
        if (isTouchDevice){
            return cachedSelection;
        }
        else{
            return rangy.getSelection(document);
        }
    }

    var getSelectionCoordinates = function(e){
        if( isTouchDevice ) {
            // Cache the selection, but allow the button to be pressed
            setTimeout(function(){
                cachedSelection = rangy.getSelection(document);
                tabletShowMenu(!cachedSelection.isCollapsed);
            }, 500);

        }
        else{
            try{
                //Begin Cross Browser Hacks
                var t = (document.all) ? document.selection.createRange().text : document.getSelection();
                var idoc= iframe.contentDocument || iframe.contentWindow.document;
                var range = idoc.getSelection().getRangeAt(0);

                //QUICK FIX FOR IE
                cachedSelection = range;

                var rects = range.getClientRects();
                var iframeRect = iframe.getBoundingClientRect();


                //If the first selection rectangle has no width, or if the offsets are the same
                if ((rects[0].left === rects[0].right && rects.length === 1) || (rects[0].right && rects.length && range.startOffset === range.endOffset)){
                    //Hide the thing
                    throw new Exception("Hide");
                }
                else if (rects.length > 0){
                    // If there is a selection, work out the maximum left and right values and calculate the center
                    // for displaying the popup
                    var left = rects[0].left;
                    var right = rects[0].right;
                    var top = rects[0].top + iframeRect.top;

                    if (rects.length > 1){
                        if (rects[1].left < rects[0].left){
                            left = rects[1].left;
                        }
                    }

                    var middle = (left+right)/2 + iframeRect.left;

                    setSelection({
                        show: true,
                        left: middle,
                        top: top,
                        selection: iframe.contentWindow.getSelection()
                    });
                }
            }
            catch(e){
                cachedSelection = null;
                setSelection({
                    show: false,
                    left: 0,
                    top: 0,
                    selection: null
                });
            }
        }
    };
    var noteHintTimeout;
//        END CODE FOR NOTE

    return {
//        START CODE FOR NOTE
//        TODO Move into seperate note service
        showNoteHint: function(note){
            noteHinter.removeAllHighlights();
            var stringified = coordsService.stringifyOne(note.location.startOffset,note.location.endOffset, "__mi__note_hint");
            noteHinter.deserialize(stringified);
            noteHintTimeout = setTimeout(function(){
                noteHinter.removeAllHighlights();
            }, 6000);
        },

//        END CODE FOR NOTE

        initializeHighlighter: function(args){
            document = args.document;
            documentElement = args.documentElement;
            iframe = args.iframe;

            isTouchDevice = args.isTouchDevice;

            //Function
            setSelection = args.setSelection;
            //Function
            updateHighlights = args.updateHighlights;
            //Function
            tabletShowMenu = args.tabletShowMenu;
            rangy.init();

            if ( isTouchDevice){
                document.addEventListener("selectionchange", getSelectionCoordinates);
            }
            else{
                //This should probably be in it's own service
                document.onmouseup = getSelectionCoordinates;
            }

            highlighter = rangy.createHighlighter(documentElement);

            for (var i in colors){
                highlighter.addClassApplier(rangy.createCssClassApplier(colorPrefix+colors[i], {
                    ignoreWhiteSpace: true,
                    tagNames: ["span", "a"],
                    normalize: true
                }));
            };



            noteHinter = rangy.createHighlighter(documentElement);

            noteHinter.addClassApplier(rangy.createCssClassApplier(colorPrefix+'note_hint', {
                ignoreWhiteSpace: true,
                tagNames: ["span", "a"],
                normalize: true
            }));

            //Add colors to the css of the inner element
            var head =  document.getElementsByTagName('head')[0];
            var timestamp =  +(new Date());
            head.innerHTML += '<link href="/css/ereader/epub_highlights.css?'+ timestamp +'" rel="stylesheet" type="text/css" />"';
            head.innerHTML += '<link media="print" href="/css/print.css?'+ timestamp +'" rel="stylesheet" type="text/css" />"';

            //Disable easy copying of books content
            var scripts = [
                '/js/vendor/shortcut.js',
                '/js/ebooksec.js?'+ timestamp
            ];

            for (var i in scripts){
                var script = document.createElement('script');
                script.type = 'text/javascript';
                script.src = scripts[i];
                document.body.appendChild(script);
            }

            initialized = true;
        },
        updateHighlights: function(highlights){
            for (var i = 0; i < highlights.length; i++){
                //Make sure that a color actually will work
                if (highlights[i].color.indexOf(colorPrefix) != 0){
                    highlights[i].color = colorPrefix+colors[0];
                }
            }
            highlighter.deserialize(coordsService.stringify(highlights));
        },
        removeHighlightSelection: function(callback){

            var selection = returnSelection();
            var removed = highlighter.unhighlightSelection(selection);
            var arr = [];
            for (var i in removed){
                arr.push({
                    id: coordsService.getId(removed[i].id)
                });
            }
            if (callback){
                callback(arr);
            }
        },
        highlightSelection: function (color){
            var selection = returnSelection();
            var type = colorPrefix + color;

            var latestHighlight = highlighter.serializeSelection(selection);

            updateHighlights({
                    startOffset: latestHighlight.start,
                    endOffset: latestHighlight.end,
                    color:  type
                }
            );
            setSelection();
        },
        getSelectedText: function(){
            var selection = returnSelection();
            var latestHighlight = highlighter.serializeSelection(selection);
            return {
                text: selection.toString(),
                startOffset: latestHighlight.start,
                endOffset: latestHighlight.end,
                element: selection.anchorNode.parentElement
            };
        },
        reloadHighlights: function(hls){
            highlighter.removeAllHighlights();
            highlighter.deserialize(coordsService.stringify(hls));
        }
    };
}])
.service('coordinatesService',[function(){
    //Coordinate Delimiter
    var coordDel = "|";
    //Property Delimiter
    var propDel = "$";
    var mapping = {};
    return {
        //Takes string and returns array of coordinates objects
        parse: function(str, callback){
            //type:textContent|621$1331$1$highlight$|1616$1912$2$note$
            var coordStrings = str.split(coordDel);
            var coords = [];

            //First split will be the type:textContent which we don't need
            for (var i = 1; i < coordStrings.length; i++){
                var coord = coordStrings[i].split(propDel);
                coords.push({
                    location:{
                        startOffset: parseInt(coord[0]),
                        endOffset: parseInt(coord[1])
                    },
                    id: parseInt(coord[2]),
                    color: coord[3]
                });
            }

            if (callback){
                callback(coords);
            }
            else{
                return coords;
            }

        },
        //Takes array of coordinates objects and returns string
        stringify: function(coords, callback){
            var str = "type:textContent";
            if (coords){
                mapping = {};
                for (var i = 0; i < coords.length; i++){
                    str +=  coordDel +  coords[i].location.startOffset +
                        propDel +   coords[i].location.endOffset +
                        propDel +   (i+1) +
                        propDel +   coords[i].color;

                    mapping[i+1] = coords[i].id;

                }
            }
            if (callback){
                callback(str);
            }
            else{
                return str;
            }
        },
        stringifyOne: function(start, end, color, callback){
            var str = "type:textContent" +
                coordDel +  start +
                propDel +   end +
                propDel +   1 +
                propDel +   color;

            if (callback){
                callback(str);
            }
            else{
                return str;
            }
        },
        getId: function(id){
            return mapping[id];
        }
    }
}])
.service('HighlightService',['Restangular', function(Restangular){
    return {
        getHighlights: function(book, chapter) {
            return Restangular.all('highlights').getList({isbn:book, chapter:chapter});
        },
        createHighlight: function(highlight){
            var payload = Restangular.one('highlights');

            payload.color = highlight.color;
            payload.location =  {
                startOffset: highlight.location.startOffset,
                endOffset: highlight.location.endOffset,
                isbn: highlight.location.isbn,
                chapterHref: highlight.location.chapterHref,
                cfiLocation: highlight.location.cfiLocation,
                chapterId: highlight.location.chapterId
            };

            return payload.post();
        },
        deleteHighlights: function(highlights){
            var payload = Restangular.one('highlights');
            payload.highlights = highlights;
            return payload.remove();
        }
    }
}])
.service('NoteService',['Restangular', function(Restangular){
    return {
        getNotesForBook: function(book){
            return Restangular.one('notes').get({isbn:book});
        },
        createNote: function(note){
            var payload = Restangular.one('notes');
            payload.location = {
                startOffset: note.location.startOffset,
                endOffset: note.location.endOffset,
                isbn: note.location.isbn,
                cfiLocation: note.location.cfiLocation,
                chapterHref: note.location.chapterHref,
                chapterId: note.location.chapterId
            };
            payload.selectedText = note.selectedText;
            payload.noteText = note.noteText;
            payload.subject = note.subject;

            return payload.post();
        },
        updateNote: function(note){
            var payload = Restangular.one('notes');
            payload.id = note.id;
            payload.noteText = note.noteText;
            payload.subject = note.subject;
            payload.user = note.user;
            payload.location = {
                startOffset: note.location.startOffset,
                endOffset: note.location.endOffset,
                isbn: note.location.isbn,
                cfiLocation: note.location.cfiLocation,
                chapterHref: note.location.chapterHref,
                chapterId: note.location.chapterId
            };

            return payload.put();
        },
        deleteNote: function(note){
            var payload = Restangular.one('notes');
            payload.notes = [{
                id: note.id
            }];
            return payload.remove();
        }
    }
}])
.directive('miNotePopup',['MiEndpoint',function(mi){
    return {
        restrict: "E",
        scope: {
            "note": "=",
            "save": "&",
            "delete": "&"
        },
        templateUrl: "/notePopup",
        link: function($scope, element, attrs){
            var master = {};
            $scope.closeNote = function(){
                $scope.note = null;
            };
            $scope.$watch('note', function(){
                if ($scope.note){
                    $scope.buttonText = $scope.note.id ? "UPDATE" : "POST";
                    $scope.showDelete = ($scope.note.id && $scope.note.editable);
                    master.subject = $scope.note.subject;
                    master.noteText = $scope.note.noteText;

                    $scope.avatar = mi.getAvatarPath($scope.note.user.id, "large");
                    $scope.cover = mi.getBookCoverPath($scope.note.location.isbn);
                }
            });

            $scope.isUnchanged = function(){
                return $scope.note.subject === master.subject && $scope.note.noteText == master.noteText;
            };


        }
    };
}]);