import Command from "@ckeditor/ckeditor5-core/src/command";
import Modal from "@packaged-ui/modal/src/modal";
import Request from "@packaged-ui/request/src/request";
import CKEditorError from "@ckeditor/ckeditor5-utils/src/ckeditorerror";

import './style.css';

const defaultFilerConfig = {
  filerUrl: null,
  sort: null,
  filter: null
};

export default class FilerCommand extends Command
{
  constructor(editor)
  {
    super(editor);

    this.config = Object.assign(defaultFilerConfig, editor.config.get('filer'));

    // Remove default document listener to lower its priority.
    this.stopListening(this.editor.model.document, 'change');

    // Lower this command listener priority to be sure that refresh() will be called after link & image refresh.
    this.listenTo(this.editor.model.document, 'change', () => this.refresh(), {priority: 'lowest'});
  }

  refresh()
  {
    const imageCommand = this.editor.commands.get('imageInsert');
    const linkCommand = this.editor.commands.get('link');

    // The filer command is enabled when one of imageInsert or link command is enabled.
    this.isEnabled = (imageCommand && imageCommand.isEnabled) || (linkCommand && linkCommand.isEnabled);
  }

  execute()
  {
    this.getItems('');
  }

  getItems(path)
  {
    const self = this;
    const config = this.config;

    if(!config.filerUrl)
    {
      throw new CKEditorError('Not a valid url for filer interface');
    }

    config.sortField = '';

    let modal = this.getModal();
    let request = new Request(config.filerUrl);
    request.setData({path: path});
    request.send().then(
      function (r)
      {
        let items = JSON.parse(r);
        items = self._processItems(items);
        self.drawItems(items);
      }
    );

    modal.show();
  }

  _processItems(items)
  {
    // filter
    if(typeof (this.config.filter) === 'function')
    {
      items = items.filter(this.config.filter);
    }
    else if(this.config.filter)
    {
      for(var filterProperty in this.config.filter)
      {
        if(this.config.filter.hasOwnProperty(filterProperty))
        {
          items = items.filter(_getFilterFn(filterProperty, this.config.filter[filterProperty]));
        }
      }
    }

    // sort
    items = items.sort(_getSortFn(this.config.sort));

    return items;
  }

  getModal()
  {
    this.modal = this.modal || new Modal();
    return this.modal;
  }

  drawItems(items)
  {
    let modal = this.getModal();
    modal.clear();
    let itemContainer = document.createElement('div');
    itemContainer.classList.add('filer-items');
    modal.appendChild(itemContainer);
    for(let i in items)
    {
      if(items.hasOwnProperty(i))
      {
        itemContainer.appendChild(this.createItem(items[i]));
      }
    }
  }

  createItem(item)
  {
    const self = this;
    let itemEle = document.createElement('div');
    itemEle.setAttribute('type', item.type);
    itemEle.setAttribute('path', item.path);
    itemEle.classList.add('filer-item', 'filer-type-' + item.type);

    let itemImg = document.createElement('div');
    itemImg.classList.add('filer-img');
    if(item.type === 'dir')
    {
      itemImg.addEventListener('dblclick', () => self.getItems(item.path));
    }
    else if(item.mime.indexOf('image/') === 0)
    {
      itemImg.style.backgroundImage = 'url("' + item.path + '")';
      itemImg.addEventListener('dblclick', () => insertImages(this.editor, [item.path]));
    }
    else
    {
      itemImg.addEventListener('dblclick', () => insertLink(this.editor, item.path));
    }
    itemEle.appendChild(itemImg);

    let itemText = document.createElement('div');
    itemText.classList.add('filer-item-name');
    itemText.innerText = item.name;
    itemEle.appendChild(itemText);

    return itemEle;
  }
}

function insertLink(editor, url)
{
  const cmd = editor.commands.get('link');

  // Check if inserting an image is actually possible - it might be possible to only insert a link.
  if(!cmd.isEnabled)
  {
    const notification = editor.plugins.get('Notification');

    notification.showWarning(
      'Could not insert link at the current position.',
      {
        title: 'Inserting image failed',
        namespace: 'filer'
      }
    );

    return;
  }
  cmd.execute(url);
}

function insertImages(editor, urls)
{
  const cmd = editor.commands.get('imageInsert');

  // Check if inserting an image is actually possible - it might be possible to only insert a link.
  if(!cmd.isEnabled)
  {
    const notification = editor.plugins.get('Notification');

    notification.showWarning(
      'Could not insert image at the current position.',
      {
        title: 'Inserting image failed',
        namespace: 'filer'
      }
    );

    return;
  }
  cmd.execute({source: urls});
}

function _getSortFn(sortProps)
{
  if(typeof (sortProps) === 'function')
  {
    return sortProps;
  }

  if(!Array.isArray(sortProps))
  {
    sortProps = [sortProps];
  }
  return function (a, b)
  {
    // dir first
    if(a.type === 'dir' && b.type !== 'dir')
    {
      return -1;
    }
    if(a.type !== 'dir' && b.type === 'dir')
    {
      return 1;
    }
    if(a.type === 'dir' && b.type === 'dir')
    {
      return 0;
    }

    // props second
    for(let p in sortProps)
    {
      if(sortProps.hasOwnProperty(p))
      {
        let prop = sortProps[p];
        if(a[prop] < b[prop])
        {
          return -1;
        }
        if(a[prop] > b[prop])
        {
          return 1;
        }
        return 0;
      }
    }
  }
}

function _getFilterFn(prop, filter)
{
  if(!Array.isArray(filter))
  {
    filter = [filter];
  }
  return function (item)
  {
    if(item.type === 'dir')
    {
      return true;
    }

    let pass = true;
    for(let m in filter)
    {
      if(filter.hasOwnProperty(m))
      {
        pass = false;
        let filterX = filter[m];
        let propValue = item[prop];
        if(filterX instanceof RegExp)
        {
          if(filterX.test(propValue))
          {
            return true;
          }
        }
        else if(typeof (filterX) === 'string')
        {
          if(propValue === filterX)
          {
            return true;
          }
        }
      }
    }
    return pass;
  }
}
