/**
 * Dialog.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

define(
  'tinymce.plugins.imagetools.ui.Dialog',
  [
    'ephox.imagetools.api.ImageTransformations',
    'ephox.imagetools.api.ResultConversions',
    'ephox.sand.api.URL',
    'global!Math',
    'global!setTimeout',
    'tinymce.core.dom.DOMUtils',
    'tinymce.core.ui.Factory',
    'tinymce.core.util.Promise',
    'tinymce.core.util.Tools',
    'tinymce.plugins.imagetools.core.UndoStack',
    'tinymce.plugins.imagetools.ui.ImagePanel'
  ],
  function (ImageTransformations, ResultConversions, URL, Math, setTimeout, DOMUtils, Factory, Promise, Tools, UndoStack, ImagePanel) {
    function createState(blob) {
      return {
        blob: blob,
        url: URL.createObjectURL(blob)
      };
    }

    function destroyState(state) {
      if (state) {
        URL.revokeObjectURL(state.url);
      }
    }

    function destroyStates(states) {
      Tools.each(states, destroyState);
    }

    function open(editor, currentState, resolve, reject) {
      var win, undoStack = new UndoStack(), mainPanel, filtersPanel, tempState,
        cropPanel, resizePanel, flipRotatePanel, imagePanel, sidePanel, mainViewContainer,
        invertPanel, brightnessPanel, huePanel, saturatePanel, contrastPanel, grayscalePanel,
        sepiaPanel, colorizePanel, sharpenPanel, embossPanel, gammaPanel, exposurePanel, panels,
        width, height, ratioW, ratioH;

      function recalcSize(e) {
        var widthCtrl, heightCtrl, newWidth, newHeight;

        widthCtrl = win.find('#w')[0];
        heightCtrl = win.find('#h')[0];

        newWidth = parseInt(widthCtrl.value(), 10);
        newHeight = parseInt(heightCtrl.value(), 10);

        if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
          if (e.control.settings.name === 'w') {
            newHeight = Math.round(newWidth * ratioW);
            heightCtrl.value(newHeight);
          } else {
            newWidth = Math.round(newHeight * ratioH);
            widthCtrl.value(newWidth);
          }
        }

        width = newWidth;
        height = newHeight;
      }

      function floatToPercent(value) {
        return Math.round(value * 100) + '%';
      }

      function updateButtonUndoStates() {
        win.find('#undo').disabled(!undoStack.canUndo());
        win.find('#redo').disabled(!undoStack.canRedo());
        win.statusbar.find('#save').disabled(!undoStack.canUndo());
      }

      function disableUndoRedo() {
        win.find('#undo').disabled(true);
        win.find('#redo').disabled(true);
      }

      function displayState(state) {
        if (state) {
          imagePanel.imageSrc(state.url);
        }
      }

      function switchPanel(targetPanel) {
        return function () {
          var hidePanels = Tools.grep(panels, function (panel) {
            return panel.settings.name !== targetPanel;
          });

          Tools.each(hidePanels, function (panel) {
            panel.hide();
          });

          targetPanel.show();
          targetPanel.focus();
        };
      }

      function addTempState(blob) {
        tempState = createState(blob);
        displayState(tempState);
      }

      function addBlobState(blob) {
        currentState = createState(blob);
        displayState(currentState);
        destroyStates(undoStack.add(currentState).removed);
        updateButtonUndoStates();
      }

      function crop() {
        var rect = imagePanel.selection();

        ResultConversions.blobToImageResult(currentState.blob).
          then(function (ir) {
            ImageTransformations.crop(ir, rect.x, rect.y, rect.w, rect.h).
              then(imageResultToBlob).
              then(function (blob) {
                addBlobState(blob);
                cancel();
              });
          });
      }

      function tempAction(fn) {
        var args = [].slice.call(arguments, 1);

        return function () {
          var state = tempState || currentState;

          ResultConversions.blobToImageResult(state.blob).
            then(function (ir) {
              fn.apply(this, [ir].concat(args)).then(imageResultToBlob).then(addTempState);
            });
        };
      }

      function action(fn) {
        var args = [].slice.call(arguments, 1);

        return function () {
          ResultConversions.blobToImageResult(currentState.blob).
            then(function (ir) {
              fn.apply(this, [ir].concat(args)).then(imageResultToBlob).then(addBlobState);
            });
        };
      }

      function cancel() {
        displayState(currentState);
        destroyState(tempState);
        switchPanel(mainPanel)();
        updateButtonUndoStates();
      }

      function waitForTempState(times, applyCall) {
        if (tempState) {
          applyCall();
        } else {
          setTimeout(function () {
            if (times-- > 0) {
              waitForTempState(times, applyCall);
            } else {
              editor.windowManager.alert('Error: failed to apply image operation.');
            }
          }, 10);
        }
      }

      function applyTempState() {
        if (tempState) {
          addBlobState(tempState.blob);
          cancel();
        } else {
          waitForTempState(100, applyTempState);
        }
      }

      function zoomIn() {
        var zoom = imagePanel.zoom();

        if (zoom < 2) {
          zoom += 0.1;
        }

        imagePanel.zoom(zoom);
      }

      function zoomOut() {
        var zoom = imagePanel.zoom();

        if (zoom > 0.1) {
          zoom -= 0.1;
        }

        imagePanel.zoom(zoom);
      }

      function undo() {
        currentState = undoStack.undo();
        displayState(currentState);
        updateButtonUndoStates();
      }

      function redo() {
        currentState = undoStack.redo();
        displayState(currentState);
        updateButtonUndoStates();
      }

      function save() {
        resolve(currentState.blob);
        win.close();
      }

      function createPanel(items) {
        return Factory.create('Form', {
          layout: 'flex',
          direction: 'row',
          labelGap: 5,
          border: '0 0 1 0',
          align: 'center',
          pack: 'center',
          padding: '0 10 0 10',
          spacing: 5,
          flex: 0,
          minHeight: 60,
          defaults: {
            classes: 'imagetool',
            type: 'button'
          },
          items: items
        });
      }

      var imageResultToBlob = function (ir) {
        return ir.toBlob();
      };

      function createFilterPanel(title, filter) {
        return createPanel([
          { text: 'Back', onclick: cancel },
          { type: 'spacer', flex: 1 },
          { text: 'Apply', subtype: 'primary', onclick: applyTempState }
        ]).hide().on('show', function () {
          disableUndoRedo();

          ResultConversions.blobToImageResult(currentState.blob).
            then(function (ir) {
              return filter(ir);
            }).
            then(imageResultToBlob).
            then(function (blob) {
              var newTempState = createState(blob);

              displayState(newTempState);
              destroyState(tempState);
              tempState = newTempState;
            });
        });
      }

      function createVariableFilterPanel(title, filter, value, min, max) {
        function update(value) {
          ResultConversions.blobToImageResult(currentState.blob).
            then(function (ir) {
              return filter(ir, value);
            }).
            then(imageResultToBlob).
            then(function (blob) {
              var newTempState = createState(blob);
              displayState(newTempState);
              destroyState(tempState);
              tempState = newTempState;
            });
        }

        return createPanel([
          { text: 'Back', onclick: cancel },
          { type: 'spacer', flex: 1 },
          {
            type: 'slider',
            flex: 1,
            ondragend: function (e) {
              update(e.value);
            },
            minValue: min,
            maxValue: max,
            value: value,
            previewFilter: floatToPercent
          },
          { type: 'spacer', flex: 1 },
          { text: 'Apply', subtype: 'primary', onclick: applyTempState }
        ]).hide().on('show', function () {
          this.find('slider').value(value);
          disableUndoRedo();
        });
      }

      function createRgbFilterPanel(title, filter) {
        function update() {
          var r, g, b;

          r = win.find('#r')[0].value();
          g = win.find('#g')[0].value();
          b = win.find('#b')[0].value();

          ResultConversions.blobToImageResult(currentState.blob).
          then(function (ir) {
            return filter(ir, r, g, b);
          }).
          then(imageResultToBlob).
          then(function (blob) {
            var newTempState = createState(blob);
            displayState(newTempState);
            destroyState(tempState);
            tempState = newTempState;
          });
        }

        return createPanel([
          { text: 'Back', onclick: cancel },
          { type: 'spacer', flex: 1 },
          {
            type: 'slider', label: 'R', name: 'r', minValue: 0,
            value: 1, maxValue: 2, ondragend: update, previewFilter: floatToPercent
          },
          {
            type: 'slider', label: 'G', name: 'g', minValue: 0,
            value: 1, maxValue: 2, ondragend: update, previewFilter: floatToPercent
          },
          {
            type: 'slider', label: 'B', name: 'b', minValue: 0,
            value: 1, maxValue: 2, ondragend: update, previewFilter: floatToPercent
          },
          { type: 'spacer', flex: 1 },
          { text: 'Apply', subtype: 'primary', onclick: applyTempState }
        ]).hide().on('show', function () {
          win.find('#r,#g,#b').value(1);
          disableUndoRedo();
        });
      }

      cropPanel = createPanel([
        { text: 'Back', onclick: cancel },
        { type: 'spacer', flex: 1 },
        { text: 'Apply', subtype: 'primary', onclick: crop }
      ]).hide().on('show hide', function (e) {
        imagePanel.toggleCropRect(e.type === 'show');
      }).on('show', disableUndoRedo);

      function toggleConstrain(e) {
        if (e.control.value() === true) {
          ratioW = height / width;
          ratioH = width / height;
        }
      }

      resizePanel = createPanel([
        { text: 'Back', onclick: cancel },
        { type: 'spacer', flex: 1 },
        { type: 'textbox', name: 'w', label: 'Width', size: 4, onkeyup: recalcSize },
        { type: 'textbox', name: 'h', label: 'Height', size: 4, onkeyup: recalcSize },
        { type: 'checkbox', name: 'constrain', text: 'Constrain proportions', checked: true, onchange: toggleConstrain },
        { type: 'spacer', flex: 1 },
        { text: 'Apply', subtype: 'primary', onclick: 'submit' }
      ]).hide().on('submit', function (e) {
        var width = parseInt(win.find('#w').value(), 10),
          height = parseInt(win.find('#h').value(), 10);

        e.preventDefault();

        action(ImageTransformations.resize, width, height)();
        cancel();
      }).on('show', disableUndoRedo);

      flipRotatePanel = createPanel([
        { text: 'Back', onclick: cancel },
        { type: 'spacer', flex: 1 },
        { icon: 'fliph', tooltip: 'Flip horizontally', onclick: tempAction(ImageTransformations.flip, 'h') },
        { icon: 'flipv', tooltip: 'Flip vertically', onclick: tempAction(ImageTransformations.flip, 'v') },
        { icon: 'rotateleft', tooltip: 'Rotate counterclockwise', onclick: tempAction(ImageTransformations.rotate, -90) },
        { icon: 'rotateright', tooltip: 'Rotate clockwise', onclick: tempAction(ImageTransformations.rotate, 90) },
        { type: 'spacer', flex: 1 },
        { text: 'Apply', subtype: 'primary', onclick: applyTempState }
      ]).hide().on('show', disableUndoRedo);

      invertPanel = createFilterPanel("Invert", ImageTransformations.invert);
      sharpenPanel = createFilterPanel("Sharpen", ImageTransformations.sharpen);
      embossPanel = createFilterPanel("Emboss", ImageTransformations.emboss);

      brightnessPanel = createVariableFilterPanel("Brightness", ImageTransformations.brightness, 0, -1, 1);
      huePanel = createVariableFilterPanel("Hue", ImageTransformations.hue, 180, 0, 360);
      saturatePanel = createVariableFilterPanel("Saturate", ImageTransformations.saturate, 0, -1, 1);
      contrastPanel = createVariableFilterPanel("Contrast", ImageTransformations.contrast, 0, -1, 1);
      grayscalePanel = createVariableFilterPanel("Grayscale", ImageTransformations.grayscale, 0, 0, 1);
      sepiaPanel = createVariableFilterPanel("Sepia", ImageTransformations.sepia, 0, 0, 1);
      colorizePanel = createRgbFilterPanel("Colorize", ImageTransformations.colorize);
      gammaPanel = createVariableFilterPanel("Gamma", ImageTransformations.gamma, 0, -1, 1);
      exposurePanel = createVariableFilterPanel("Exposure", ImageTransformations.exposure, 1, 0, 2);

      filtersPanel = createPanel([
        { text: 'Back', onclick: cancel },
        { type: 'spacer', flex: 1 },
        { text: 'hue', icon: 'hue', onclick: switchPanel(huePanel) },
        { text: 'saturate', icon: 'saturate', onclick: switchPanel(saturatePanel) },
        { text: 'sepia', icon: 'sepia', onclick: switchPanel(sepiaPanel) },
        { text: 'emboss', icon: 'emboss', onclick: switchPanel(embossPanel) },
        { text: 'exposure', icon: 'exposure', onclick: switchPanel(exposurePanel) },
        { type: 'spacer', flex: 1 }
      ]).hide();

      mainPanel = createPanel([
        { tooltip: 'Crop', icon: 'crop', onclick: switchPanel(cropPanel) },
        { tooltip: 'Resize', icon: 'resize2', onclick: switchPanel(resizePanel) },
        { tooltip: 'Orientation', icon: 'orientation', onclick: switchPanel(flipRotatePanel) },
        { tooltip: 'Brightness', icon: 'sun', onclick: switchPanel(brightnessPanel) },
        { tooltip: 'Sharpen', icon: 'sharpen', onclick: switchPanel(sharpenPanel) },
        { tooltip: 'Contrast', icon: 'contrast', onclick: switchPanel(contrastPanel) },
        { tooltip: 'Color levels', icon: 'drop', onclick: switchPanel(colorizePanel) },
        { tooltip: 'Gamma', icon: 'gamma', onclick: switchPanel(gammaPanel) },
        { tooltip: 'Invert', icon: 'invert', onclick: switchPanel(invertPanel) }
        //{text: 'More', onclick: switchPanel(filtersPanel)}
      ]);

      imagePanel = ImagePanel.create({
        flex: 1,
        imageSrc: currentState.url
      });

      sidePanel = Factory.create('Container', {
        layout: 'flex',
        direction: 'column',
        border: '0 1 0 0',
        padding: 5,
        spacing: 5,
        items: [
          { type: 'button', icon: 'undo', tooltip: 'Undo', name: 'undo', onclick: undo },
          { type: 'button', icon: 'redo', tooltip: 'Redo', name: 'redo', onclick: redo },
          { type: 'button', icon: 'zoomin', tooltip: 'Zoom in', onclick: zoomIn },
          { type: 'button', icon: 'zoomout', tooltip: 'Zoom out', onclick: zoomOut }
        ]
      });

      mainViewContainer = Factory.create('Container', {
        type: 'container',
        layout: 'flex',
        direction: 'row',
        align: 'stretch',
        flex: 1,
        items: [sidePanel, imagePanel]
      });

      panels = [
        mainPanel,
        cropPanel,
        resizePanel,
        flipRotatePanel,
        filtersPanel,
        invertPanel,
        brightnessPanel,
        huePanel,
        saturatePanel,
        contrastPanel,
        grayscalePanel,
        sepiaPanel,
        colorizePanel,
        sharpenPanel,
        embossPanel,
        gammaPanel,
        exposurePanel
      ];

      win = editor.windowManager.open({
        layout: 'flex',
        direction: 'column',
        align: 'stretch',
        minWidth: Math.min(DOMUtils.DOM.getViewPort().w, 800),
        minHeight: Math.min(DOMUtils.DOM.getViewPort().h, 650),
        title: 'Edit image',
        items: panels.concat([mainViewContainer]),
        buttons: [
          { text: 'Save', name: 'save', subtype: 'primary', onclick: save },
          { text: 'Cancel', onclick: 'close' }
        ]
      });

      win.on('close', function () {
        reject();
        destroyStates(undoStack.data);
        undoStack = null;
        tempState = null;
      });

      undoStack.add(currentState);
      updateButtonUndoStates();

      imagePanel.on('load', function () {
        width = imagePanel.imageSize().w;
        height = imagePanel.imageSize().h;
        ratioW = height / width;
        ratioH = width / height;

        win.find('#w').value(width);
        win.find('#h').value(height);
      });

      imagePanel.on('crop', crop);
    }

    function edit(editor, imageResult) {
      return new Promise(function (resolve, reject) {
        return imageResult.toBlob().then(function (blob) {
          open(editor, createState(blob), resolve, reject);
        });
      });
    }

    //edit('img/dogleft.jpg');

    return {
      edit: edit
    };
  }
);
