Ox = Ox || {};

(function() {

    Ox.PNG = function(url) {
        var that = this,
            self = {};
        self.getCanvas = function() {
            var canvas = $("<canvas/>")
                    .attr({
                        width: that.width,
                        height: that.height
                    })[0],
                context = canvas.getContext("2d");
            context.drawImage(self.$png[0], 0, 0);
            return {
                canvas: canvas,
                context: context
            };
        };
        self.getChunks = function() {
            var data = self.getData().substr(8),
                length, chunks = [];
            while (data) {
                length = data.charCodeAt(1) << 16 |
                    data.charCodeAt(2) << 8 |
                    data.charCodeAt(3);
                chunks.push({
                    length: length,
                    type: data.substr(4, 4),
                    data: data.substr(8, length),
                    crc: data.substr(length + 8, 4)
                });
                data = data.substr(length + 12);
            }
            return chunks;
        }
        self.getData = function() {
            return atob(self.getURL().split(",")[1]);
        }
        self.getDimensions = function() {
            var dimensions;
            self.$png
                .hide()
                .appendTo($("body"));
            dimensions = {
                width: self.$png.width(),
                height: self.$png.height()
            }
            self.$png.remove();
            return dimensions;
        }
        self.getHeader = function() {
            return self.getData().substr(0, 8);
        }
        self.getURL = function() {
            return that.canvas.toDataURL();
        }
        self.onLoad = function($png, callback) {
            self.$png = $png;
            var dimensions = self.getDimensions();
            that.width = dimensions.width;
            that.height = dimensions.height;
            var canvas = self.getCanvas();
            that.context = canvas.context;
            that.canvas = canvas.canvas;
            Ox.print(self.getChunks())
            callback();
        };
        that.addChunk = function(options) {
            options = $.extend({
                data: "",
                type: "",
                position: -1
            }, options);
            var len = options.data.length,
                randomType = !options.type,
                chunks = self.getChunks();
                chunk = {
                    length: 0,
                    type: options.type || "",
                    data: options.data,
                    crc: ""
                },
                data = "";
                i;
            //     0    1    2    3    4     ... length: 6
            // IHDR IDAT IDAT IDAT ifOo IEND
            options.position = options.position < 0 ?
                Math.max(chunks.length - 1 - options.position, 0) : 
                Math.min(options.position, chunks.length - 2);
            for (i = 0; i < 4; i++) {
                if (randomType) {
                    // format abCd (ancillary, private, (reserved), safe-to-copy)
                    chunk.type += String.fromCharCode((i == 2 ? 65 : 97) + Math.random() * 26);
                }
                chunk.length += String.fromCharCode(len >> ((3 - i) * 8) & 255);
                chunk.crc += String.fromCharCode(Math.random() * 256);
            }
            chunks.splice(position + 1, 0, chunk);
            // data = ...
        }
        that.appendTo = function(selector) {
            $("<img/>")
                .attr({
                    src: self.getURL()
                })
                .appendTo(selector);
        }
        that.getSize = function() {
            return self.getData().length;
        }
        that.load = function(callback) {
            $("<img/>")
                .attr({
                    src: url
                })
                .load(function() {
                    self.onLoad($(this), callback);
                });
        }
        return that;
    };

})();