'use strict';

process.on('uncaughtException', function (error) {
    //process.exit(0);
    console.log(error);
});

const electron = require('electron');
const fs = require('fs');
const os = require('os');
const path = require('path');
const mkdirp = require('mkdirp');
const targz = require('tar.gz');
const rimraf = require('rimraf');
const configuration = require('./configuration.js');
const syncronizedWorkshopMods = [];
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;
let IsCloudEnabled = false;

app.commandLine.appendSwitch('force-device-scale-factor', '1');
app.commandLine.appendSwitch('high-dpi-support', 'true');

console.log(`Running Electron ${process.versions.electron } (node ${process.version})`);

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

function createWindow(options) {
    // Move old savegames
    if (options != null && options.saveGames == null) {
        options.saveGames = app.moveOldSavegames().map(sg => sg.replace('dton', 'json'));
        app.saveFile('options.json', JSON.stringify(options));
    }

    let lastSessionInfo = options != null && options.lastSessionInfo != null
        ? options.lastSessionInfo
        : {size: {width: 1024, height: 768}, position: {x: null, y: null}};


    // Create the browser window.
    mainWindow = new BrowserWindow({
        width: lastSessionInfo.size.width,
        height: lastSessionInfo.size.height,
        x: lastSessionInfo.position.x,
        y: lastSessionInfo.position.y,
        minWidth: 1024,
        minHeight: 768,
        frame: true,
        icon: `${__dirname}/images/favicon.ico`,
        webPreferences: {
            experimentalFeatures: true
        }
    });

    // Attach app to the BrowserWindow
    mainWindow.app = app;

    // Install AngularJS Batarang dev tools
    const {default: installExtension, ANGULARJS_BATARANG} = require('electron-devtools-installer');
    installExtension(ANGULARJS_BATARANG)
        .then((name) => console.log(`Added Extension:  ${name}`))
        .catch((err) => console.log('An error occurred: ', err));

    // Implement a getCurrentWindow function
    mainWindow.getCurrentWindow = () => mainWindow;

    // and load the index.html of the app.

    let indexUrl = 'file://' + __dirname + '/index.html';
    if(process.argv.includes('--dev')) {
        indexUrl = 'file://' + __dirname + '/index-development.html';
    }
    mainWindow.loadURL(indexUrl);

    mainWindow.setMenu(null);

    // Emitted when the window is closed.
    mainWindow.on('close', function (e) {
        e.preventDefault();
        app.saveWindowPosition(() => {
            app.exit();
        });
    });

    mainWindow.on('closed', function () {
        mainWindow = null;
    });

    console.log('Cloud Enabled: ' + IsCloudEnabled);
}

const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
    // Someone tried to run a second instance, we should focus our window.
    if (mainWindow) {
        if (mainWindow.isMinimized()) mainWindow.restore();
        mainWindow.focus()
    }
});

if (shouldQuit) {
    app.quit()
}

app.on('ready', () => {
    app.loadFile('options.json', rawOptions => {
        createWindow(JSON.parse(rawOptions));
    });
});

app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', function () {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow();
    }
});

app.saveWindowPosition = (cb) => {
    let filename = 'options.json';
    app.loadFile(filename, rawOptions => {
        let options = JSON.parse(rawOptions);
        let position = mainWindow.getPosition();
        let size = mainWindow.getSize();

        options.lastSessionInfo = {
            position: {x: position[0], y: position[1]},
            size: {width: size[0], height: size[1]}
        };

        app.saveFile(filename, JSON.stringify(options), () => cb());
    });
};

app.getFolderPath = () => {
    let folder = os.homedir();
    switch (os.platform()) {
        case "win32":
            folder += '\\Saved Games\\Startup Company\\';
            break;
        case "darwin":
            folder += '/Library/Application Support/Startup Company/SaveData/';
            break;
        default:
            folder += '/.StartupCompany/';
    }

    return folder;
};

app.deleteFolder = (path) => {
    rimraf(path, function () {
    });
};

app.doesFolderExist = (path) => {
    return fs.existsSync(path);
};

app.getAllFiles = result => {
    let folder = app.getFolderPath();
    fs.readdir(folder, (err, files) => {
        result(files);
    });
};

app.saveFile = (fileName, value, cb, disableCloud) => {
    if (cb == null) cb = () => {
    };

    let cloudSaved = false;
    if (IsCloudEnabled && !disableCloud) {
        app.greenworks.saveTextToFile(fileName, value, data => {
            cloudSaved = true;
            cb();
        }, err => {
            console.log('Cloud Error: ' + err)
        })
    }

    let folder = app.getFolderPath();
    let path = folder + fileName;
    fs.writeFile(path, value, function (err) {
        if (err) {
            console.log(err);
            mkdirp(folder, err => {
                console.log(err);
                app.saveFile(fileName, value);
                cb();
            });
        } else {
            cb();
        }

        console.log(`Saved file: ${fileName}: (Cloud-Enabled: ${IsCloudEnabled}, Cloud-Saved: ${ cloudSaved })`);
    });
};

app.writeFile = (filePath, value, cb) => {
    if (cb == null) cb = () => {
    };

    fs.writeFile(filePath, value, function (err) {
        if (err) {
            console.log('Error writing to file: ' + err);
        } else {
            console.log(`Written to file: ` + filePath);
        }

        cb();
    });
};

app.loadFile = (fileName, result) => {
    let loadFromDisk = () => {
        let folder = app.getFolderPath();
        let path = folder + fileName;
        console.log('Reading file from disk: ' + path);

        fs.readFile(path, 'utf8', function (err, data) {
            result(err != null ? null : data);
        });
    };

    if (IsCloudEnabled) {
        console.log('Reading file from cloud: ' + fileName);
        app.greenworks.readTextFromFile(fileName, data => {
            result(data);
        }, err => {
            console.log('Cloud Error: ' + err);
            loadFromDisk();
        });
    } else {
        loadFromDisk();
    }
};

app.deleteFile = fileName => {
    let cloudDeleted = false;
    if (IsCloudEnabled) {
        app.greenworks.deleteFile(fileName, data => {
            cloudDeleted = true;
        }, err => {
            console.log('Cloud Error: ' + err)
        })
    }
    let folder = app.getFolderPath();
    let path = folder + fileName;

    fs.unlink(path, err => {
        if (err != null) console.log(err);
        console.log(`Deleted file: ${fileName} (Cloud: ${ cloudDeleted })`);
    });
};

app.getMods = result => {
    let gameFolder = app.getFolderPath();
    let workshopMods = [];
    let steamId;

    let loadDirectories = () => {
        fs.readdir(gameFolder + "/mods", (err, folders) => {
            let files = [];

            if (folders == null) {
                result(files);
                return;
            }

            folders.forEach(folder => {
                let modFolder = `${gameFolder}/mods/${folder}/`;
                let metaFilePath = modFolder + 'mod.json';
                if (fs.existsSync(metaFilePath)) {
                    let workshopItem = workshopMods.find(x => x.fileName == `${folder}.tar`);
                    let mod = JSON.parse(fs.readFileSync(metaFilePath));
                    mod.startPath = modFolder + "start.js";
                    mod.modPath = modFolder;
                    mod.thumbnailPath = modFolder + "thumbnail.png";
                    mod.id = folder;
                    mod.fromWorkshop = workshopItem != null;
                    mod.owned = !mod.fromWorkshop || (workshopItem != null && workshopItem.steamIDOwner == steamId);
                    mod.publishedFileId = workshopItem != null ? workshopItem.publishedFileId : null;
                    files.push(mod);
                }
            });

            result(files);
        });
    };

    if (app.greenworks.initAPI()) {
        steamId = app.greenworks.getSteamId().steamId;

        app.greenworks.ugcGetUserItems(
            app.greenworks.UGCMatchingType.Items,
            app.greenworks.UserUGCListSortOrder.CreationOrderDesc,
            app.greenworks.UserUGCList.Subscribed,
            mods => {
                workshopMods = mods;
                loadDirectories();
            }, err => {
                console.log(err);
            }
        );
    } else {
        loadDirectories();
    }
};

app.restart = () => {
    app.relaunch();
    app.quit();
};

app.openLink = link => {
    electron.shell.openExternal(link);
};

app.syncWorkshopItems = () => {
    let modsFolder = app.getFolderPath() + "/mods";
    app.greenworks.ugcSynchronizeItems(modsFolder, mods => {
        mods.forEach(mod => {
            let tarPath = modsFolder + "/" + mod.fileName;
            let modId = mod.fileName.replace('.tar', '');
            let target = modsFolder + "/" + modId;
            let folderExists = app.doesFolderExist(target);
            syncronizedWorkshopMods.push(modId);

            if (!folderExists || mod.isUpdated) {
                app.extractTar(tarPath, target);
                console.log(`Updated mod: ${mod.title}`);
            }
        });
    }, err => {
        console.log('error syncing', err);
    }, progress => {
        console.log('rog', progress);
    });
};

// Greenworks
process.activateUvLoop();
app.greenworks = require('./greenworks/greenworks.js');
if (app.greenworks.initAPI()) {
    IsCloudEnabled = app.greenworks.isCloudEnabled();
    app.syncWorkshopItems();
}

app.tarFolder = (source, target, cb) => {
    // Using callbacks
    targz().compress(source, target, function (err) {
        if (err)
            console.log('Something is wrong ', err.stack);
        console.log(source + ' tar done!');
        if (cb != null) cb();
    });
};

app.extractTar = (source, target, cb) => {
    if (cb == null) cb = () => {
    };
    targz({}, {
        strip: 1,
    }).extract(source, target).then(function () {
        console.log(source + ' extracted!');
        cb();
    }).catch(function (err) {
        console.log('Something is wrong ', err.stack);
        cb();
    });
};


//
// COPY OLD SAVEGAMES
//
app.moveOldSavegames = () => {
    let oldPath = os.homedir();
    switch (os.platform()) {
        case "win32":
        case "darwin":
            oldPath += '/Documents/Startup Company/';
            break;
        default:
            oldPath += '/.config/StartupCompany/';
    }

    let newPath = app.getFolderPath();
    let files = copyFolderRecursiveSync(oldPath, newPath);
    return files.filter(file => file.startsWith('sg_'));
};

function copyFileSync(source, target) {
    let targetFile = target;

    //if target is a directory a new file with the same name will be created
    if (fs.existsSync(target)) {
        if (fs.lstatSync(target).isDirectory()) {
            targetFile = path.join(target, path.basename(source));
        }
    }

    fs.writeFileSync(targetFile.replace('dton', 'json'), fs.readFileSync(source));
}

function copyFolderRecursiveSync(source, target) {
    let files = [];

    //check if folder needs to be created or integrated
    let targetFolder = target;

    if (!fs.existsSync(targetFolder)) {
        fs.mkdirSync(targetFolder);
    }

    //copy
    if (fs.lstatSync(source).isDirectory()) {
        files = fs.readdirSync(source);

        files.forEach(function (file) {
            let curSource = path.join(source, file);
            if (fs.lstatSync(curSource).isDirectory()) {
                copyFolderRecursiveSync(curSource, targetFolder);
            } else {
                copyFileSync(curSource, targetFolder);
            }
        });
    }

    return files;
}