import browserSync, { BrowserSyncInstance } from "browser-sync";
import GulpClient, { TaskFunction } from "gulp";
import path from "upath";
import { Context } from "./gulp/Context.js";

const { dest, parallel, series, src, watch } = GulpClient;
const { join } = path;

const context = new Context();

/**
 * Builds javascript files.
 *
 * @returns
 * The resulting stream.
 */
export function JavaScript(): NodeJS.ReadWriteStream
{
    return src(context.SourcePath(context.JSDirName, "**", "*.js")).pipe(
        dest(context.StaticPath(context.JSDirName)));
}

/**
 * Builds css files.
 *
 * @returns
 * The resulting stream.
 */
export function Styles(): NodeJS.ReadWriteStream
{
    return src(context.SourcePath(context.StyleDirName, "**", "*.css")).pipe(
        dest(context.StaticPath(context.StyleDirName)));
}

/**
 * Builds asset files.
 *
 * @returns
 * The resulting stream.
 */
export function Assets(): NodeJS.ReadWriteStream
{
    return src(context.SourcePath(context.AssetDirName, "**", "*")).pipe(
        dest(context.StaticPath(context.AssetDirName)));
}

/**
 * Builds the web pages.
 *
 * @returns
 * The resulting stream.
 */
export function WebPages(): NodeJS.ReadWriteStream
{
    return src(context.SourcePath("**", "*.html")).pipe(
        dest(context.StaticPath()));
}

/**
 * Builds all files.
 */
export function Build(): Promise<void>
{
    return new Promise<void>(
        (resolve, reject) =>
        {
            parallel(
                [
                    JavaScript,
                    Styles,
                    Assets,
                    WebPages
                ])(
                    (error) =>
                    {
                        if (error)
                        {
                            reject(error);
                        }
                        else
                        {
                            resolve();
                        }
                    });
        });
}

/**
 * Reloads all browsers using `browser-sync`.
 *
 * @param syncer
 * The browser-sync instance to work on.
 *
 * @param filePath
 * A glob-path which points to the files which must be reloaded.
 *
 * @returns
 * The actual task.
 */
function BrowserSync(syncer: BrowserSyncInstance, filePath?: string): TaskFunction
{
    let BrowserSync: TaskFunction = (done) =>
    {
        if (filePath)
        {
            syncer.reload(filePath);
        }
        else
        {
            syncer.reload();
        }

        done();
    };

    return BrowserSync;
}

/**
 * Builds and watches the files for changes.
 */
export let Watch: TaskFunction = async (): Promise<void> =>
{
    return new Promise<void>(
        (resolve, reject) =>
        {
            series(Build)(
                (error) =>
                {
                    if (error)
                    {
                        reject(error);
                    }
                    else
                    {
                        let syncer = browserSync.create();

                        syncer.init({
                            open: false,
                            server: context.StaticPath(),
                            online: false
                        });

                        watch(
                            context.SourcePath(context.JSDirName),
                            series(
                                JavaScript,
                                BrowserSync(syncer, "*.js")));

                        watch(
                            context.SourcePath(context.StyleDirName, "**", "*.css"),
                            series(
                                Styles,
                                BrowserSync(syncer, "*.css")));

                        watch(
                            context.SourcePath(context.AssetDirName),
                            series(
                                Assets,
                                BrowserSync(syncer, join(context.AssetDirName, "**", "*"))));

                        watch(
                            context.SourcePath("**", "*.html"),
                            series(
                                WebPages,
                                BrowserSync(syncer, "*.html")));
                    }
                });
        });
};