'use strict';

/* global CloudCmd, DOM */

require('../../css/view.css');

const itype = require('itype/legacy');
const rendy = require('rendy/legacy');
const exec = require('execon');
const currify = require('currify/legacy');
const {promisify} = require('es6-promisify');

const modal = require('@cloudcmd/modal');
const createElement = require('@cloudcmd/create-element');

const {time} = require('../../common/util');
const {FS} = require('../../common/cloudfunc');

const Files = require('../dom/files');
const Events = require('../dom/events');
const load = require('load.js');
const Images = require('../dom/images');

const {encode} = require('../../common/entity');

const testRegExp = currify((name, reg) => reg.test(name));
const lifo = currify((fn, el, cb, name) => fn(name, el, cb));

const addEvent = lifo(Events.add);
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');

module.exports.show = show;
module.exports.hide = hide;

let Loading = false;

const Name = 'View';
CloudCmd[Name] = module.exports;

const Info = DOM.CurrentInfo;
const Key = CloudCmd.Key;
const basename = (a) => a.split('/').pop();

let El, TemplateAudio, Overlay;

const Config = {
    beforeShow: (callback) => {
        Images.hide();
        Key.unsetBind();
        exec(callback);
    },
    beforeClose: (callback) => {
        Events.rmKey(listener);
        Key.setBind();
        exec(callback);
    },
    afterShow: (callback) => {
        El.focus();
        exec(callback);
    },
    onOverlayClick,
    afterClose      : exec,
    autoSize        : false,
    helpers: {
        title: {},
    },
};

module.exports.init = promisify((fn) => {
    Loading = true;
    
    exec.series([
        loadAll,
        (callback) => {
            Loading = false;
            callback();
        }
    ], fn);
    
    const events = [
        'click',
        'contextmenu',
    ];
    
    events.forEach(addEvent(Overlay, onOverlayClick));
});

function show(data, options) {
    const prefixUrl = CloudCmd.PREFIX_URL + FS;
    
    if (Loading)
        return;
    
    if (!options || options.bindKeys !== false)
        Events.addKey(listener);
    
    El = createElement('div', {
        className: 'view',
        notAppend: true,
    });
    
    El.tabIndex = 0;
    
    if (data) {
        El.append(data);
        modal.open(El, initConfig(Config, options));
        return;
    }
    
    Images.show.load();
    
    const path = prefixUrl + Info.path;
    const type = getType(path);
    
    switch(type) {
    default:
        return viewFile();
    
    case 'image':
        return viewImage(path, prefixUrl);
    
    case 'media':
        return viewMedia(path);
    }
}

function viewMedia(path) {
    getMediaElement(path, (element) => {
        const allConfig = {
            ...Config,
            ...{
                autoSize: true,
                afterShow: () => {
                    element
                        .querySelector('audio, video')
                        .focus();
                }
            }
        };
        
        modal.open(element, allConfig);
    });
}

function viewFile() {
    Info.getData((error, data) => {
        if (error)
            return Images.hide();
        
        const element = document.createTextNode(data);
        const options = {
            ...Config,
        };
        
        if (CloudCmd.config('showFileName'))
            options.title = Info.name;
        
        El.append(element);
        modal.open(El, options);
    });
}

function initConfig(Config, options) {
    const config = {
        ...Config
    };
    
    if (!options)
        return config;
    
    Object.keys(options).forEach((name) => {
        const isConfig = !!config[name];
        const item = options[name];
        const isFunc = itype.function(item);
        
        if (!isFunc || !isConfig) {
            config[name] = options[name];
            return;
        }
        
        const func = config[name];
        config[name] = () => {
            exec.series([func, item]);
        };
    });
    
    return config;
}

function hide() {
    modal.close();
}

function viewImage(href, prefixUrl) {
    const makeTitle = (path) => {
        return {
            href: prefixUrl + path,
            title: encode(basename(path)),
        };
    };
    
    const names = Info.files
        .map(DOM.getCurrentPath)
        .filter(isImage);
    
    const titles = names
        .map(makeTitle);
    
    const index = names.indexOf(Info.path);
    const imageConfig = {
        index,
        autoSize    : true,
        arrows      : true,
        keys        : true,
        helpers     : {
            title   : {}
        }
    };
    
    const config = {
        ...Config,
        ...imageConfig,
    };
    
    modal.open(titles, config);
}

function isImage(name) {
    const images = [
        'jp(e|g|eg)',
        'gif',
        'png',
        'bmp',
        'webp',
        'svg',
        'ico'
    ];
    
    return images
        .map(getRegExp)
        .some(testRegExp(name));
}

function isMedia(name) {
    return isAudio(name) || isVideo(name);
}

function isAudio(name) {
    return /\.(mp3|ogg|m4a)$/i.test(name);
}

function isVideo(name) {
    return /\.(mp4|avi)$/i.test(name);
}

function getType(name) {
    if (isImage(name))
        return 'image';
    
    if (isMedia(name))
        return 'media';
}

function getMediaElement(src, callback) {
    check(src, callback);
    
    Files.get('view/media-tmpl', (error, template) => {
        const {name} = Info;
        
        if (error)
            return alert(error);
        
        if (!TemplateAudio)
            TemplateAudio   = template;
        
        const is = isAudio(name);
        const type =  is ? 'audio' : 'video';
        
        const innerHTML = rendy(TemplateAudio, {
            src,
            type,
            name,
        });
        
        const element = createElement('div', {
            innerHTML,
        });
        
        callback(element);
    });
}

function check(src, callback) {
    if (typeof src !== 'string')
        throw Error('src should be a string!');
    
    if (typeof callback !== 'function')
        throw Error('callback should be a function');
}

/**
 * function loads css and js of FancyBox
 * @callback   -  executes, when everything loaded
 */
function loadAll(callback) {
    time(Name + ' load');
    
    const {PREFIX} = CloudCmd;
    load.css(PREFIX + '/dist/view.css', callback);
    
    callback();
}

function onOverlayClick(event) {
    const position = {
        x: event.clientX,
        y: event.clientY
    };
      
    setCurrentByPosition(position);
}

function setCurrentByPosition(position) {
    const element = DOM.getCurrentByPosition(position);
    
    if (!element)
        return;
    
    const {
        files,
        filesPassive,
    } = Info;
    
    const isFiles = ~files.indexOf(element);
    const isFilesPassive = ~filesPassive.indexOf(element);
    
    if (!isFiles && !isFilesPassive)
        return;
    
    const isCurrent = DOM.isCurrentFile(element);
    
    if (isCurrent)
        return;
    
    DOM.setCurrentFile(element);
}

function listener({keyCode}) {
    if (keyCode === Key.ESC)
        hide();
}

