this.cbrjs = {};

cbrjs.open = function(url) {
    cbrjs.url = url
    cbrjs.loadSettings()
    Ox.load('UI', function() {
        var $body = Ox.$('body')
                .css({
                    backgroundColor: 'rgb(255, 255, 255)',
                    overflowX: 'hidden'
                });
        window.app = cbrjs.CBRViewer(Ox.extend({
            url: url,
        }, cbrjs.settings)
        ).bindEvent({
            page: cbrjs.updateSettings,
            fitMode: cbrjs.updateSettings,
        }).appendTo($body);
        Ox.$window.on({
            resize: app.resize
        });

    });

};

cbrjs.loadSettings = function() {
    var settings = {};
    try {
        settings =  JSON.parse(localStorage['cbrjs.' + cbrjs.url]);
    } catch(e) {
        settings.page = 1;
        settings.fitMode = 'B';
    }
    cbrjs.settings = settings;
}

cbrjs.updateSettings = function(data) {
    Ox.forEach(data, function(value, key) {
        cbrjs.settings[key] = value;
    });
    localStorage['cbrjs.' + cbrjs.url] = JSON.stringify(cbrjs.settings);
}

cbrjs.CBRViewer = function(options, self) {
    self = self || {};
    var that = Ox.Element({}, self)
        .defaults({
            url: '',
            page: 1
        })
        .options(options || {})
        .update({
            page: setPage,
            fitMode: function() {
                resize()
                that.triggerEvent('fitMode', {fitMode: self.options.fitMode});
            },
            url: loadBook
        }),
        moveTimeout,
        canvas;
    
    self.pages = [];
    self.rotateTimes = 0;
    self.hflip = false;
    self.vflip = false;

    self.mimeTypes = {
        'png': 'image/png',
        'jpg': 'image/jpeg',
        'jpeg': 'image/jpeg',
        'gif': 'image/gif',
    };

    self.$frame = Ox.Element()
        .on({
            mousemove: showMenu,
            mousedown: function() {
                self.$frame.gainFocus();
            },
        })
        .bindEvent({
            key_down: function() {
                self.$frame.scrollTop(self.$frame.scrollTop() + self.$frame.height())
            },
            key_left: function() {
                that.options({
                    page: Math.max(self.options.page - 1, 1)
                })
            },
            key_pagedown: function() {
                that.options({
                    page: self.pages.length
                })
            },
            key_pageup: function() {
                that.options({
                    page: 1
                })
            },
            key_right: function() {
                that.options({
                    page: Math.min(self.options.page + 1, self.pages.length)
                })
            },
            key_up: function() {
                self.$frame.scrollTop(self.$frame.scrollTop() - self.$frame.height())
            },
            key_n: function() {
                that.options({
                    fitMode: 'N'
                })
            },
            key_h: function() {
                that.options({
                    fitMode: 'H'
                })
            },
            key_b: function() {
                that.options({
                    fitMode: 'B'
                })
            },
            key_w: function() {
                that.options({
                    fitMode: 'W'
                })
            },
            key_l: function() {
                self.rotateTimes--;
                if (self.rotateTimes < 0) {
                    self.rotateTimes = 3;
                }
                setPage();
            },
            key_f: function() {
                if (!self.hflip && !self.vflip) {
                    self.hflip = true;
                } else if(self.hflip == true) {
                    self.vflip = true;
                    self.hflip = false;
                } else if(self.vflip == true) {
                    self.vflip = false;
                }
                setPage();
            },
            key_space: function() {
                var old = self.$frame.scrollTop()
                self.$frame.scrollTop(self.$frame.scrollTop() + self.$frame.height())
                if (self.$frame.scrollTop() == old) {
                    that.options({
                        page: Math.min(self.options.page + 1, self.pages.length)
                    })
                }
            },
            singleclick: function(data) {
                if (data.clientX < (that.width() / 2)) {
                    that.options({
                        page: Math.max(self.options.page - 1, 1)
                    });
                } else {
                    that.options({
                        page: Math.min(self.options.page + 1, self.pages.length)
                    });
                }
            }
        })
        .css({
            margin: 0,
            overflow: 'auto',
            padding: 0,
            textAlign: 'center'
        });
    self.$canvas = Ox.Element('<canvas>')
        .appendTo(self.$frame);
    canvas = self.$canvas[0];
    self.$scrollbar = Ox.Range({
        arrows: true,
        max: self.options.page,
        min: 1,
        orientation: 'horizontal',
        step: 1,
        value: self.options.page,
        thumbValue: true,
        thumbSize: 64 
    }).bindEvent({
        change: function(data) {
            Ox.print('change', data);
            that.options({
                page: data.value
            })
        }
    });

    self.$panel = Ox.SplitPanel({})
        .appendTo(that);
    that.setElement(
        self.$mainPanel = Ox.SplitPanel({
            elements: [
                {
                    element: self.$frame,
                },
                {
                    element: self.$scrollbar,
                    size: 16
                }
            ],
            orientation: 'vertical'
        })
    );

    self.$loading = Ox.LoadingScreen({
            size: 16,
        })
        .css({
            margin: 'auto',
            position: 'absolute'
        })
        .start()
        .appendTo(that);

    self.$menu = Ox.Element().css({
            position: 'fixed',
            right: (Ox.UI.SCROLLBAR_SIZE + 4) + 'px',
            top: '4px',
            opacity: 0
        })
        .addClass('menu')
        .appendTo(that);

    self.$zoom = Ox.Button({
            style: 'symbol',
            title: 'fill',
            tooltip: Ox._('Zoom to fill'),
            type: 'image'
        }).css({
            width: '16px',
            height: '16px',
        }).on({
            mousedown: function(event) {
                if (self.$zoom.options('title') == 'fill') {
                    self.$zoom.options({
                        title: 'fit',
                        tooltip: Ox._('Zoom to fit')
                    })
                    that.options({
                        fitMode: 'W'
                    })
                } else {
                    self.$zoom.options({
                        title: 'fill',
                        tooltip: Ox._('Zoom to fill')
                    })
                    that.options({
                        fitMode: 'B'
                    })
                }
                event.stopPropagation()
                event.preventDefault();
            }
        })
        .appendTo(self.$menu);

    loadBook();

    function createURLFromArray(array, mimeType) {
        var blob = new Blob([array], {type: mimeType});
        blob = blob.slice(array.byteOffset, array.byteOffset + array.byteLength, mimeType);
        return URL.createObjectURL(blob);
    }

    function isImage(filename) {
        var extension = filename.split('.').pop().toLowerCase();
        return !Ox.isUndefined(self.mimeTypes[extension]);    
    }

    function loadBook() {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', self.options.url, true);
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            loadFromArrayBuffer(this.response, function(pages, progress, total) {
                self.pages = pages;
                self.$scrollbar.options({
                    max: Math.max(pages.length, self.options.page),
                    size: that.width()
                })
                if (pages.length == self.options.page) {
                    setPage();
                    if (self.$loading) {
                        self.$loading.remove();
                        delete self.$loading;
                    }
                }
            });
        };
        xhr.send(null);
    }

    function loadFromArrayBuffer(buffer, progress) {
        var extract,
            start = (new Date).getTime(),
            h = new Uint8Array(buffer, 0, 10),
            pathToBitJS = $('script').map(function(i, script) {
                return script.src;
            }).filter(function(i, url) {
                return url.indexOf('bitjs') > -1;
            })[0].replace('archive.js', '');

        var pages = [], filenames = [], total;

        if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { // RAR
            extract = new bitjs.archive.Unrarrer(buffer, pathToBitJS);
        } else if (h[0] == 80 && h[1] == 75) { // ZIP
            extract = new bitjs.archive.Unzipper(buffer, pathToBitJS);
        } else { // try tar otherwise
            extract = new bitjs.archive.Untarrer(buffer, pathToBitJS);
        }
        if (extract) {
            extract.addEventListener(
                bitjs.archive.UnarchiveEvent.Type.PROGRESS,
                function(e) {
                  var percentage = e.currentBytesUnarchived / e.totalUncompressedBytesInArchive;
                  total = e.totalFilesInArchive
                  progress([], percentage, total);
                }
            );
            extract.addEventListener(
                bitjs.archive.UnarchiveEvent.Type.INFO,
                function(e) {
                    Ox.Log('', e.msg);
                }
            );
            extract.addEventListener(
                bitjs.archive.UnarchiveEvent.Type.EXTRACT,
                function(e) {
                    // convert DecompressedFile into a bunch of ImageFiles
                    if (e.unarchivedFile) {
                        var f = e.unarchivedFile;
                        // add any new pages based on the filename
                        if (!Ox.contains(filenames, f.filename) && isImage(f.filename)) {
                            filenames.push(f.filename);
                            pages.push(f);
                        } else {
                            total--;
                        }
                        progress(pages, pages.length/total, total);
                    }
                }
            );
            extract.addEventListener(
                bitjs.archive.UnarchiveEvent.Type.FINISH,
                function(e) {
                    total = pages.length;
                    progress(pages, 1, pages.length);
                    var diff = ((new Date).getTime() - start)/1000;
                    Ox.Log('', 'Unarchiving done in ' + diff + 's');
                }
            );
            extract.start();
        } else {
            Ox.Log('', 'bitjs.archive failed to open file');
        }
    }

    function resize(clear) {
        canvas.style.width = '';
        canvas.style.height = '';
        canvas.style.maxWidth = '';
        canvas.style.maxHeight = '';
        var maxheight = self.$frame.height() - 4;
        if (clear || self.options.fitMode == 'N') {

        } else if (self.options.fitMode == 'B') {
            canvas.style.maxWidth = '100%';
            canvas.style.maxHeight = maxheight + 'px';
        } else if (self.options.fitMode == 'H') {
            canvas.style.height = maxheight + 'px';
        } else if (self.options.fitMode == 'W') {
            canvas.style.width = '100%';
        }
        self.$scrollbar.options({
            size: that.width()
        });
    }

    function setImage(url) {
        var ctx = canvas.getContext('2d'),
            img = new Image();
        img.onerror = function(e) {
          canvas.width = innerWidth - 100;
          canvas.height = 300;
          resize(true);
          ctx.fillStyle = 'orange';
          ctx.font = '50px sans-serif';
          ctx.strokeStyle = 'black';
          ctx.fillText('Page #' + (currentImage+1) + ' (' +
              imageFiles[currentImage].filename + ')', 100, 100)
          ctx.fillStyle = 'red';
          ctx.fillText('Is corrupt or not an image', 100, 200);
        };
        img.onload = function() {
          var h = img.height, 
              w = img.width, 
              sw = w, 
              sh = h;
          self.rotateTimes =  (4 + self.rotateTimes) % 4;
          ctx.save();
          if (self.rotateTimes % 2 == 1) { sh = w; sw = h;}
          canvas.height = sh;
          canvas.width = sw;
          ctx.translate(sw/2, sh/2);
          ctx.rotate(Math.PI/2 * self.rotateTimes);
          ctx.translate(-w/2, -h/2);
          if (self.vflip) {
            ctx.scale(1, -1)
            ctx.translate(0, -h);
          }
          if (self.hflip) {
            ctx.scale(-1, 1)
            ctx.translate(-w, 0);
          }
          canvas.style.display = 'none';
          scrollTo(0,0);
          ctx.drawImage(img, 0, 0);
          
          resize();
            
          canvas.style.display = '';
          document.body.style.overflowY = '';
          ctx.restore();
        };
        img.src = url;
    }

    function setPage() {
        var file = self.pages[self.options.page - 1],
            filename = file.filename,
            extension = file.filename.split('.').pop().toLowerCase(),
            mimeType = self.mimeTypes[extension];
        setImage(createURLFromArray(file.fileData, mimeType));
        self.$scrollbar.options({value: self.options.page});
        that.triggerEvent('page', {page: self.options.page});
    }
    function showMenu() {
        if (moveTimeout) {
            clearTimeout(moveTimeout);
        } else {
            self.$menu.animate({opacity: 1}, 250)
        }
        moveTimeout = setTimeout(function() {
            self.$menu.animate({opacity: 0}, 250)
            moveTimeout = null
        }, 5000);
    }

    that.resize = function() {
        resize();
    };
    return that;
};
