import Vector1 from "../../core/geom/Vector1.js";
import { RemainingTimeEstimator } from "../../core/process/task/RemainingTimeEstimator.js";

import TaskState from '../../core/process/task/TaskState.js';
import Clock from "../../engine/Clock.js";
import LabelView from '../common/LabelView.js';
import { LocalizedLabelView } from "../common/LocalizedLabelView.js";

import dom from '../DOM.js';
import SmoothProgressBar from '../elements/progress/SmoothProgressBar.js';
import View from '../View.js';

class TaskProgressView extends View {
    /**
     *
     * @param {Task|TaskGroup} task
     * @param {Localization} [localization]
     * @constructor
     */
    constructor({ task, localization=null }) {
        super();

        function makeNameId() {
            const taskName = typeof task.name === "string" ? task.name : "unknown";

            return `system_task.${taskName.replace(/ /g, '_')}.name`
        }

        const taskName = new LocalizedLabelView({
            id: makeNameId(),
            localization,
            classList: ['name']
        });

        const progressBar = new SmoothProgressBar();

        const processFailure = () => {
            //find failed subtasks/dependencies
            if (task.children !== undefined) {
                const failedChildren = task.children.filter(function (t) {
                    return t.state.getValue() === TaskState.FAILED;
                });
                failedChildren.forEach((t) => {
                    this.addChild(new LabelView("Failed subtask: " + t.name));
                });
            }

        }

        /**
         *
         * @param {TaskState} state
         */
        function getStateName(state) {

            for (let stateName in TaskState) {
                if (TaskState[stateName] === state) {
                    return stateName;
                }
            }

            return "Unknown State";
        }

        const state = new LabelView(task.state, {
            format: function (v) {
                return getStateName(v);
            },
            classList: ['state']
        });

        function updateState() {

            function className(n) {
                return `state-${n}`;
            }

            for (let stateName in TaskState) {
                state.removeClass(className(stateName));
            }

            const v = task.state.getValue();

            if (v === TaskState.FAILED) {
                processFailure();
            }

            const name = getStateName(v);
            state.addClass(className(name));
        }

        state.on.linked.add(updateState);
        state.bindSignal(task.state.onChanged, updateState);

        const remainingTimeEstimator = new RemainingTimeEstimator(100);
        const remainingTime = new Vector1(0);

        const lRemainingTime = new LabelView(remainingTime, {
            format: function (v) {
                return v.toFixed(0);
            },
            classList: ['remaining-time']
        });

        this.el = dom().addClass('task-progress-view').el;

        this.addChild(taskName);
        this.addChild(state);
        this.addChild(progressBar);
        this.addChild(lRemainingTime);

        progressBar.max = 1;

        const clock = new Clock();

        clock.start();

        const update = () => {
            const computedProgressValue = task.computeProgress();

            const taskProgress = Number.isNaN(computedProgressValue) ? 0 : computedProgressValue;

            progressBar.value = taskProgress;

            remainingTimeEstimator.update(clock.updateElapsedTime(), taskProgress);

            const computedEstimate = remainingTimeEstimator.estimate(taskProgress);

            const estimate = Number.isNaN(computedEstimate) ? 0 : computedProgressValue;

            remainingTime.set(estimate);

            const state = task.state.getValue();

            if (state !== TaskState.FAILED && state !== TaskState.SUCCEEDED && this.isLinked) {
                requestAnimationFrame(update);
            }
        }

        this.on.linked.add(update);
    }
}


export default TaskProgressView;
