/*globals $, gettext, fabric, PDFJS*/
fabric.Barcodearea = fabric.util.createClass(fabric.Rect, {
    type: 'barcodearea',

    initialize: function (text, options) {
        options || (options = {});

        this.callSuper('initialize', text, options);
        this.set('label', options.label || '');
    },

    toObject: function () {
        return fabric.util.object.extend(this.callSuper('toObject'), {});
    },

    _render: function (ctx) {
        this.callSuper('_render', ctx);

        ctx.font = '16px Helvetica';
        ctx.fillStyle = '#fff';
        ctx.fillText(gettext('QR Code'), -this.width / 2, -this.height / 2 + 20);
    },
});
fabric.Barcodearea.fromObject = function (object, callback, forceAsync) {
    return fabric.Object._fromObject('Barcodearea', object, callback, forceAsync);
};
fabric.Textarea = fabric.util.createClass(fabric.Textbox, {
    type: 'textarea',

    initialize: function (text, options) {
        options || (options = {});

        this.callSuper('initialize', text, options);
        this.set('content', options.content || '');
    },

    toObject: function(propertiesToInclude) {
        return this.callSuper('toObject', ['content'].concat(propertiesToInclude));
    }
});
fabric.Textarea.fromObject = function (object, callback, forceAsync) {
    return fabric.Object._fromObject('Textarea', object, callback, forceAsync, 'text');
};


var editor = {
    $pdfcv: null,
    $fcv: null,
    $cva: null,
    $fabric: null,
    objects: [],
    history: [],
    clipboard: [],
    pdf_page: null,
    pdf_scale: 1,
    pdf_viewport: null,
    _history_pos: 0,
    _history_modification_in_progress: false,
    dirty: false,
    pdf_url: null,
    uploaded_file_id: null,
    _window_loaded: false,
    _fabric_loaded: false,

    _px2mm: function (v) {
        return v / editor.pdf_scale / 72 * editor.pdf_page.userUnit * 25.4;
    },

    _mm2px: function (v) {
        return v * editor.pdf_scale * 72 / editor.pdf_page.userUnit / 25.4;
    },

    _px2pt: function (v) {
        return v / editor.pdf_scale * editor.pdf_page.userUnit;
    },

    _pt2px: function (v) {
        return v * editor.pdf_scale / editor.pdf_page.userUnit;
    },

    dump: function (objs) {
        var d = [];
        objs = objs || editor.fabric.getObjects();

        for (var i in objs) {
            var o = objs[i];
            var top = o.top;
            var left = o.left;
            if (o.group) {
                top += o.group.top + o.group.height / 2;
                left += o.group.left + o.group.width / 2;
            }
            if (o.type === "textarea") {
                var col = (new fabric.Color(o.getFill()))._source;
                d.push({
                    type: "textarea",
                    left: editor._px2mm(left).toFixed(2),
                    bottom: editor._px2mm(editor.pdf_viewport.height - o.height - top).toFixed(2),
                    fontsize: editor._px2pt(o.getFontSize()).toFixed(1),
                    color: col,
                    //lineheight: o.lineHeight,
                    fontfamily: o.fontFamily,
                    bold: o.fontWeight === 'bold',
                    italic: o.fontStyle === 'italic',
                    width: editor._px2mm(o.width).toFixed(2),
                    content: o.content,
                    text: o.text,
                    align: o.textAlign,
                });
            } else  if (o.type === "barcodearea") {
                d.push({
                    type: "barcodearea",
                    left: editor._px2mm(left).toFixed(2),
                    bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2),
                    size: editor._px2mm(o.height * o.scaleY).toFixed(2)
                });
            }
        }
        return d;
    },

    _add_from_data: function (d) {
        if (d.type === "barcodearea") {
            o = editor._add_qrcode();
            o.scaleToHeight(editor._mm2px(d.size));
        } else if (d.type === "textarea" || o.type === "text") {
            o = editor._add_text();
            o.setColor('rgb(' + d.color[0] + ',' + d.color[1] + ',' + d.color[2] + ')');
            o.setFontSize(editor._pt2px(d.fontsize));
            //o.setLineHeight(d.lineheight);
            o.setFontFamily(d.fontfamily);
            o.setFontWeight(d.bold ? 'bold' : 'normal');
            o.setFontStyle(d.italic ? 'italic' : 'normal');
            o.setWidth(editor._mm2px(d.width));
            o.content = d.content;
            o.setTextAlign(d.align);
            if (d.content === "other") {
                o.setText(d.text);
            } else {
                o.setText(editor._get_text_sample(d.content));
            }
        }

        var new_top = editor.pdf_viewport.height - editor._mm2px(d.bottom) - (o.height * o.scaleY);
        o.set('left', editor._mm2px(d.left));
        o.set('top', new_top);
        o.setCoords();
        return o;
    },

    load: function(data) {
        editor.fabric.clear();
        for (var i in data) {
            var d = data[i], o;
            editor._add_from_data(d);
        }
        editor.fabric.renderAll();
        editor._update_toolbox_values();
    },

    _get_text_sample: function (key) {
        if (key.startsWith('meta:')) {
            return key.substr(5);
        }
        return $('#toolbox-content option[value='+key+']').attr('data-sample') || '';
    },

    _load_pdf: function (dump) {
        // TODO: Loading indicators
        var url = editor.pdf_url;
        // TODO: Handle cross-origin issues if static files are on a different origin
        PDFJS.workerSrc = editor.$pdfcv.attr("data-worker-url");

        // Asynchronous download of PDF
        var loadingTask = PDFJS.getDocument(url);
        loadingTask.promise.then(function (pdf) {
            console.log('PDF loaded');

            // Fetch the first page
            var pageNumber = 1;
            pdf.getPage(pageNumber).then(function (page) {
                console.log('Page loaded');
                var canvas = document.getElementById('pdf-canvas');

                var scale = editor.$cva.width() / page.getViewport(1.0).width;
                var viewport = page.getViewport(scale);

                // Prepare canvas using PDF page dimensions
                var context = canvas.getContext('2d');
                context.clearRect(0, 0, canvas.width, canvas.height);
                canvas.height = viewport.height;
                canvas.width = viewport.width;

                editor.pdf_page = page;
                editor.pdf_scale = scale;
                editor.pdf_viewport = viewport;

                // Render PDF page into canvas context
                var renderContext = {
                    canvasContext: context,
                    viewport: viewport
                };
                var renderTask = page.render(renderContext);
                renderTask.then(function () {
                    console.log('Page rendered');
                    editor._init_fabric(dump);
                });
            });
        }, function (reason) {
            var msg = gettext('The PDF background file could not be loaded for the following reason:');
            editor._error(msg + ' ' + reason);
        });
    },

    _init_fabric: function (dump) {
        editor.$fcv.get(0).width = editor.$pdfcv.get(0).width;
        editor.$fcv.get(0).height = editor.$pdfcv.get(0).height;
        editor.fabric = new fabric.Canvas('fabric-canvas');

        editor.fabric.on('object:modified', editor._create_savepoint);
        editor.fabric.on('object:added', editor._create_savepoint);
        editor.fabric.on('selection:cleared', editor._update_toolbox);
        editor.fabric.on('selection:created', editor._update_toolbox);
        editor.fabric.on('object:selected', editor._update_toolbox);
        editor.fabric.on('object:moving', editor._update_toolbox_values);
        editor.fabric.on('object:modified', editor._update_toolbox_values);
        editor.fabric.on('object:scaling', editor._update_toolbox_values);
        editor._update_toolbox();

        $("#toolbox-content-other").hide();
        $(".add-buttons button").prop('disabled', false);

        if (dump) {
            editor.load(dump);
        } else {
            var data = $.trim($("#editor-data").text());
            if (data) {
                editor.load(JSON.parse(data));
            }
        }
        editor.history = [];
        editor._create_savepoint();
        editor.dirty = !!dump;

        if ($("#loading-upload").is(":visible")) {
            $("#loading-container, #loading-upload").hide();
        }

        editor._fabric_loaded = true;
        console.log("Fabric loaded");
        if (editor._window_loaded) {
            editor._ready();
        }
    },

    _window_load_event: function () {
        editor._window_loaded = true;
        console.log("Window loaded");
        if (editor._fabric_loaded) {
            editor._ready();
        }
    },

    _ready: function () {
        $("#editor-loading").hide();
        $("#editor-start").removeClass("sr-only");
        $("#editor-start").click(function () {
            $("#loading-container").hide();
            $("#loading-initial").remove();
        });
    },

    _update_toolbox_values: function () {
        var o = editor.fabric.getActiveObject();
        if (!o) {
            o = editor.fabric.getActiveGroup();
            if (!o) {
                return;
            }
        }
        $("#toolbox-position-x").val(editor._px2mm(o.left).toFixed(2));
        $("#toolbox-position-y").val(editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - o.top).toFixed(2));

        if (o.type === "barcodearea") {
            $("#toolbox-squaresize").val(editor._px2mm(o.height * o.scaleY).toFixed(2));
        } else if (o.type === "text" || o.type === "textarea") {
            var col = (new fabric.Color(o.getFill()))._source;
            $("#toolbox-col").val("#" + ((1 << 24) + (col[0] << 16) + (col[1] << 8) + col[2]).toString(16).slice(1));
            $("#toolbox-fontsize").val(editor._px2pt(o.fontSize).toFixed(1));
            //$("#toolbox-lineheight").val(o.lineHeight);
            $("#toolbox-fontfamily").val(o.fontFamily);
            $("#toolbox").find("button[data-action=bold]").toggleClass('active', o.fontWeight === 'bold');
            $("#toolbox").find("button[data-action=italic]").toggleClass('active', o.fontStyle === 'italic');
            $("#toolbox").find("button[data-action=left]").toggleClass('active', o.textAlign === 'left');
            $("#toolbox").find("button[data-action=center]").toggleClass('active', o.textAlign === 'center');
            $("#toolbox").find("button[data-action=right]").toggleClass('active', o.textAlign === 'right');
            $("#toolbox-textwidth").val(editor._px2mm(o.width).toFixed(2));
            if (o.type === "textarea") {
                $("#toolbox-content").val(o.content);
                $("#toolbox-content-other").toggle($("#toolbox-content").val() === "other");
                if (o.content === "other") {
                    $("#toolbox-content-other").val(o.text);
                } else {
                    $("#toolbox-content-other").val("");
                }
            }
        }
    },

    _update_values_from_toolbox: function () {
        var o = editor.fabric.getActiveObject();
        if (!o) {
            o = editor.fabric.getActiveGroup();
            if (!o) {
                return;
            }
        }

        var new_top = editor.pdf_viewport.height - editor._mm2px($("#toolbox-position-y").val()) - o.height * o.scaleY;
        o.set('left', editor._mm2px($("#toolbox-position-x").val()));
        o.set('top', new_top);

        if (o.type === "barcodearea") {
            var new_h = editor._mm2px($("#toolbox-squaresize").val());
            new_top += o.height * o.scaleY - new_h;
            o.setHeight(new_h);
            o.setWidth(new_h);
            o.setScaleX(1);
            o.setScaleY(1);
            o.set('top', new_top)
        } else if (o.type === "textarea" || o.type === "text") {
            o.setColor($("#toolbox-col").val());
            o.setFontSize(editor._pt2px($("#toolbox-fontsize").val()));
            //o.setLineHeight($("#toolbox-lineheight").val());
            o.setFontFamily($("#toolbox-fontfamily").val());
            o.setFontWeight($("#toolbox").find("button[data-action=bold]").is('.active') ? 'bold' : 'normal');
            o.setFontStyle($("#toolbox").find("button[data-action=italic]").is('.active') ? 'italic' : 'normal');
            var align = $("#toolbox-align").find(".active").attr("data-action");
            if (align) {
                o.setTextAlign(align);
            }
            o.setWidth(editor._mm2px($("#toolbox-textwidth").val()));
            $("#toolbox-content-other").toggle($("#toolbox-content").val() === "other");
            o.content = $("#toolbox-content").val();
            if ($("#toolbox-content").val() === "other") {
                o.setText($("#toolbox-content-other").val());
            } else {
                o.setText(editor._get_text_sample($("#toolbox-content").val()));
            }
        }

        o.setCoords();
        editor.fabric.renderAll();
    },

    _update_toolbox: function () {
        if (editor.fabric.getActiveGroup()) {
            $("#toolbox").attr("data-type", "group");
            $("#toolbox-heading").text(gettext("Group of objects"));
            var g = editor.fabric.getActiveGroup();
        } else if (editor.fabric.getActiveObject()) {
            var o = editor.fabric.getActiveObject();
            $("#toolbox").attr("data-type", o.type);
            if (o.type === "textarea" || o.type === "text") {
                $("#toolbox-heading").text(gettext("Text object"));
            } else if (o.type === "barcodearea") {
                $("#toolbox-heading").text(gettext("Barcode area"));
            } else {
                $("#toolbox-heading").text(gettext("Object"));
            }
        } else {
            $("#toolbox").removeAttr("data-type");
            $("#toolbox-heading").text(gettext("Ticket design"));
            $("#pdf-info-width").val(editor._px2mm(editor.pdf_viewport.width).toFixed(2));
            $("#pdf-info-height").val(editor._px2mm(editor.pdf_viewport.height).toFixed(2));
        }
        editor._update_toolbox_values();
    },

    _error: function (msg) {
        editor.$cva.before("<div class='alert alert-danger'>" + msg + "</div>");
    },

    _add_text: function () {
        var text = new fabric.Textarea(editor._get_text_sample('event_name'), {
            left: 100,
            top: 100,
            width: editor._mm2px(50),
            lockRotation: true,
            fontFamily: 'Open Sans',
            lineHeight: 1,
            content: 'item',
            editable: false,
            fontSize: editor._pt2px(13)
        });
        text.setControlsVisibility({
            'tr': false,
            'tl': false,
            'mt': false,
            'br': false,
            'bl': false,
            'mb': false,
            'mr': true,
            'ml': true,
            'mtr': false
        });
        editor.fabric.add(text);
        editor._create_savepoint();
        return text;
    },

    _add_qrcode: function () {
        var rect = new fabric.Barcodearea({
            left: 100,
            top: 100,
            width: 100,
            height: 100,
            lockRotation: true,
            lockUniScaling: true,
            fill: '#666',
        });
        rect.setControlsVisibility({'mtr': false});
        editor.fabric.add(rect);
        editor._create_savepoint();
        return rect;
    },

    _cut: function () {
        editor._history_modification_in_progress = true;
        var thing = editor.fabric.getActiveObject() ? editor.fabric.getActiveObject() : editor.fabric.getActiveGroup();
        if (thing.type === "group") {
            editor.clipboard = editor.dump(thing._objects);
            thing.forEachObject(function (o) {
                o.remove();
            });
            thing.remove();
        } else {
            editor.clipboard = editor.dump([thing]);
            thing.remove();
        }
        editor.fabric.discardActiveGroup();
        editor.fabric.discardActiveObject();
        editor._history_modification_in_progress = false;
        editor._create_savepoint();
    },

    _copy: function () {
        editor._history_modification_in_progress = true;
        var thing = editor.fabric.getActiveObject() ? editor.fabric.getActiveObject() : editor.fabric.getActiveGroup();
        if (thing.type === "group") {
            editor.clipboard = editor.dump(thing._objects);
        } else {
            editor.clipboard = editor.dump([thing]);
        }
        editor._history_modification_in_progress = false;
        editor._create_savepoint();
    },

    _paste: function () {
        if (editor.clipboard.length < 1) {
            return;
        }
        editor._history_modification_in_progress = true;
        var objs = [];
        for (var i in editor.clipboard) {
            objs.push(editor._add_from_data(editor.clipboard[i]));
        }
        editor.fabric.discardActiveObject();
        editor.fabric.discardActiveGroup();
        if (editor.clipboard.length > 1) {
            var group = new fabric.Group(objs, {
                originX: 'left',
                originY: 'top',
                left: 100,
                top: 100,
            });
            group.setCoords();
            editor.fabric.setActiveGroup(group);
        } else {
            editor.fabric.setActiveObject(objs[0]);
        }
        editor._history_modification_in_progress = false;
        editor._create_savepoint();
    },

    _delete: function () {
        var thing = editor.fabric.getActiveObject() ? editor.fabric.getActiveObject() : editor.fabric.getActiveGroup();
        if (thing.type === "group") {
            thing.forEachObject(function (o) {
                o.remove();
            });
            thing.remove();
            editor.fabric.discardActiveGroup();
        } else {
            thing.remove();
            editor.fabric.discardActiveObject();
        }
        editor._create_savepoint();
    },

    _on_keydown: function (e) {
        var step = e.shiftKey ? editor._mm2px(10) : editor._mm2px(1);
        var thing = editor.fabric.getActiveObject() ? editor.fabric.getActiveObject() : editor.fabric.getActiveGroup();
        switch (e.keyCode) {
            case 38:  /* Up arrow */
                thing.set('top', thing.get('top') - step);
                thing.setCoords();
                editor._create_savepoint();
                break;
            case 40:  /* Down arrow */
                thing.set('top', thing.get('top') + step);
                thing.setCoords();
                editor._create_savepoint();
                break;
            case 37:  /* Left arrow  */
                thing.set('left', thing.get('left') - step);
                thing.setCoords();
                editor._create_savepoint();
                break;
            case 39:  /* Right arrow  */
                thing.set('left', thing.get('left') + step);
                thing.setCoords();
                editor._create_savepoint();
                break;
            case 46:  /* Delete */
                editor._delete();
                break;
            case 89:  /* Y */
                if (e.ctrlKey) {
                    editor._redo();
                }
                break;
            case 90:  /* Z */
                if (e.ctrlKey) {
                    editor._undo();
                }
                break;
            case 88:  /* X */
                if (e.ctrlKey) {
                    editor._cut();
                }
                break;
            case 86:  /* V */
                if (e.ctrlKey) {
                    editor._paste();
                }
                break;
            case 67:  /* C */
                if (e.ctrlKey) {
                    editor._copy();
                }
                break;
            default:
                return;
        }
        e.preventDefault();
        editor.fabric.renderAll();
        editor._update_toolbox_values();
    },

    _create_savepoint: function () {
        if (editor._history_modification_in_progress) {
            return;
        }
        var state = editor.dump();
        if (editor._history_pos > 0) {
            editor.history.splice(-1 * editor._history_pos, editor._history_pos);
            editor._history_pos = 0;
        }
        editor.history.push(state);
        editor.dirty = true;
    },

    _undo: function undo() {
        if (editor._history_pos < editor.history.length - 1) {
            editor._history_modification_in_progress = true;
            editor._history_pos += 1;
            editor.fabric.clear().renderAll();
            editor.load(editor.history[editor.history.length - 1 - editor._history_pos]);
            editor._history_modification_in_progress = false;
            editor.dirty = true;
        }
    },

    _redo: function redo() {
        if (editor._history_pos > 0) {
            editor._history_modification_in_progress = true;
            editor._history_pos -= 1;
            editor.load(editor.history[editor.history.length - 1 - editor._history_pos]);
            editor._history_modification_in_progress = false;
            editor.dirty = true;
        }
    },

    _save: function () {
        $("#editor-save").prop('disabled', true).prepend('<span class="fa fa-cog fa-spin"></span>');
        var dump = editor.dump();
        $.post(window.location.href, {
            'data': JSON.stringify(dump),
            'csrfmiddlewaretoken': $("input[name=csrfmiddlewaretoken]").val(),
            'background': editor.uploaded_file_id,
        }, function (data) {
            if (data.status === 'ok') {
                $("#editor-save span").remove();
                $("#editor-save").prop('disabled', false);
                editor.dirty = false;
                editor.uploaded_file_id = null;
            } else {
                alert(gettext('Saving failed.'));
            }
        }, 'json');
        return false;
    },

    _preview: function () {
        $("#preview-form input[name=data]").val(JSON.stringify(editor.dump()));
        $("#preview-form input[name=background]").val(editor.uploaded_file_id);
        $("#preview-form").get(0).submit();
    },

    _replace_pdf_file: function (url) {
        editor.pdf_url = url;
        d = editor.dump();
        editor.fabric.dispose();
        editor._load_pdf(d);
    },

    _source_show: function () {
        $("#source-textarea").text(JSON.stringify(editor.dump()));
        $("#source-container").show();
    },

    _source_close: function () {
        $("#source-container").hide();
    },

    _source_save: function () {
        editor.load(JSON.parse($("#source-textarea").val()));
        $("#source-container").hide();
    },

    init: function () {
        editor.$pdfcv = $("#pdf-canvas");
        editor.pdf_url = editor.$pdfcv.attr("data-pdf-url");
        editor.$fcv = $("#fabric-canvas");
        editor.$cva = $("#editor-canvas-area");
        editor._load_pdf();
        $("#editor-add-qrcode").click(editor._add_qrcode);
        $("#editor-add-text").click(editor._add_text);
        editor.$cva.get(0).tabIndex = 1000;
        editor.$cva.on("keydown", editor._on_keydown);
        $("#editor-save").on("click", editor._save);
        $("#editor-preview").on("click", editor._preview);
        window.onbeforeunload = function () {
            if (editor.dirty) {
                return gettext("Do you really want to leave the editor without saving your changes?");
            }
        };
        $("#source-container").hide();


        $('#fileupload').fileupload({
            url: location.href,
            dataType: 'json',
            done: function (e, data) {
                if (data.result.status === "ok") {
                    editor.uploaded_file_id = data.result.id;
                    editor._replace_pdf_file(data.result.url);
                } else {
                    alert(data.result.error || gettext("Error while uploading your PDF file, please try again."));
                    $("#loading-container, #loading-upload").hide();
                }
                $("#fileupload").prop('disabled', false);
                $(".fileinput-button").removeClass("disabled");
            },
            add: function (e, data) {
                data.formData = {
                    'csrfmiddlewaretoken': $("input[name=csrfmiddlewaretoken]").val()
                };
                $("#loading-container, #loading-upload").show();
                $("#loading-upload .progress").show();
                $('#loading-upload .progress-bar').css('width', 0);
                $("#fileupload").prop('disabled', true);
                $(".fileinput-button").addClass("disabled");
                data.process().done(function () {
                    data.submit();
                });
            },
            progressall: function (e, data) {
                var progress = parseInt(data.loaded / data.total * 100, 10);
                $('#loading-upload .progress-bar').css('width', progress + '%');
            }
        }).prop('disabled', !$.support.fileInput).parent().addClass($.support.fileInput ? undefined : 'disabled');

        $("#toolbox input[type=number], #toolbox textarea, #toolbox input[type=text]").bind('change keydown keyup' +
            ' input', editor._update_values_from_toolbox);
        $("#toolbox input[type=number], #toolbox textarea, #toolbox input[type=text], #toolbox input[type=radio]").bind('change', editor._create_savepoint);
        $("#toolbox label.btn").bind('click change', editor._update_values_from_toolbox);
        $("#toolbox select").bind('change', editor._update_values_from_toolbox);
        $("#toolbox select").bind('change', editor._create_savepoint);
        $("#toolbox button.toggling").bind('click change', function () {
            if ($(this).is(".option")) {
                $(this).addClass("active");
                $(this).parent().siblings().find("button").removeClass("active");
            } else {
                $(this).toggleClass("active");
            }
            editor._update_values_from_toolbox();
            editor._create_savepoint();
        });
        $("#toolbox .colorpickerfield").bind('changeColor', editor._update_values_from_toolbox);
        $("#toolbox-copy").bind('click', editor._copy);
        $("#toolbox-cut").bind('click', editor._cut);
        $("#toolbox-delete").bind('click', editor._delete);
        $("#toolbox-paste").bind('click', editor._paste);
        $("#toolbox-undo").bind('click', editor._undo);
        $("#toolbox-redo").bind('click', editor._redo);
        $("#toolbox-source").bind('click', editor._source_show);
        $("#source-close").bind('click', editor._source_close);
        $("#source-save").bind('click', editor._source_save);
    }
};

$(function () {
    editor.init();
});
$(window).bind('load', editor._window_load_event);
