// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Caml from "rescript/lib/es6/caml.js";
import * as React from "react";
import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
import * as $$Navigation from "./Navigation.res.mjs";
import * as Caml_option from "rescript/lib/es6/caml_option.js";

function make(initialRoute) {
  return {
          currentRoute: initialRoute,
          state: {
            TAG: "Idle",
            _0: initialRoute
          },
          listeners: [],
          historyStack: [initialRoute],
          historyIndex: 0
        };
}

function notify(manager, $$event) {
  Belt_Array.forEach(manager.listeners, (function (listener) {
          listener($$event);
        }));
}

function subscribe(manager, listener) {
  manager.listeners = Belt_Array.concat(manager.listeners, [listener]);
  return function () {
    manager.listeners = Belt_Array.keep(manager.listeners, (function (l) {
            return l !== listener;
          }));
  };
}

function determineDirection(manager, newRoute, eq) {
  if (manager.historyIndex <= 0) {
    return "Forward";
  }
  var prevRoute = manager.historyStack[manager.historyIndex - 1 | 0];
  if (prevRoute !== undefined && eq(Caml_option.valFromOption(prevRoute), newRoute)) {
    return "Backward";
  } else {
    return "Forward";
  }
}

function startTransition(manager, newRoute, direction, eq) {
  var from = manager.currentRoute;
  if (eq(from, newRoute)) {
    return ;
  }
  var dir = direction !== undefined ? direction : determineDirection(manager, newRoute, eq);
  manager.state = {
    TAG: "Transitioning",
    from: from,
    to: newRoute,
    direction: dir,
    progress: 0.0
  };
  notify(manager, {
        TAG: "TransitionStart",
        from: from,
        to: newRoute,
        direction: dir
      });
}

function updateProgress(manager, progress) {
  var t = manager.state;
  if (t.TAG === "Idle") {
    return ;
  }
  manager.state = {
    TAG: "Transitioning",
    from: t.from,
    to: t.to,
    direction: t.direction,
    progress: progress
  };
  notify(manager, {
        TAG: "TransitionProgress",
        from: t.from,
        to: t.to,
        progress: progress
      });
}

function completeTransition(manager) {
  var match = manager.state;
  if (match.TAG === "Idle") {
    return ;
  }
  var to = match.to;
  manager.currentRoute = to;
  manager.state = {
    TAG: "Idle",
    _0: to
  };
  switch (match.direction) {
    case "Forward" :
        manager.historyStack = Belt_Array.concat(Belt_Array.slice(manager.historyStack, 0, manager.historyIndex + 1 | 0), [to]);
        manager.historyIndex = manager.historyIndex + 1 | 0;
        break;
    case "Backward" :
        manager.historyIndex = Caml.int_max(0, manager.historyIndex - 1 | 0);
        break;
    case "Replace" :
        Belt_Array.set(manager.historyStack, manager.historyIndex, to);
        break;
    case "Initial" :
        break;
    
  }
  notify(manager, {
        TAG: "TransitionEnd",
        from: match.from,
        to: to
      });
}

function cancelTransition(manager) {
  var match = manager.state;
  if (match.TAG === "Idle") {
    return ;
  }
  var from = match.from;
  manager.state = {
    TAG: "Idle",
    _0: from
  };
  notify(manager, {
        TAG: "TransitionCancel",
        from: from,
        to: match.to
      });
}

function navigateTo(manager, route, eq) {
  startTransition(manager, route, undefined, eq);
  completeTransition(manager);
}

function isTransitioning(manager) {
  var match = manager.state;
  if (match.TAG === "Idle") {
    return false;
  } else {
    return true;
  }
}

function getCurrentRoute(manager) {
  return manager.currentRoute;
}

function getState(manager) {
  return manager.state;
}

function linear(t) {
  return t;
}

function easeInOut(t) {
  if (t < 0.5) {
    return 2.0 * t * t;
  } else {
    return 1.0 - Math.pow(-2.0 * t + 2.0, 2.0) / 2.0;
  }
}

function easeOut(t) {
  return 1.0 - Math.pow(1.0 - t, 2.0);
}

function easeIn(t) {
  return t * t;
}

function animate(manager, newRoute, duration, easingOpt, eq, onComplete) {
  var easing = easingOpt !== undefined ? easingOpt : easeInOut;
  startTransition(manager, newRoute, undefined, eq);
  var startTime = {
    contents: undefined
  };
  var frameId = {
    contents: undefined
  };
  var loop = function (timestamp) {
    var s = startTime.contents;
    var start = s !== undefined ? s : (startTime.contents = timestamp, timestamp);
    var elapsed = timestamp - start;
    var rawProgress = Math.min(1.0, elapsed / duration);
    var easedProgress = easing(rawProgress);
    updateProgress(manager, easedProgress);
    if (rawProgress < 1.0) {
      frameId.contents = requestAnimationFrame(loop);
      return ;
    } else {
      completeTransition(manager);
      if (onComplete !== undefined) {
        return onComplete();
      } else {
        return ;
      }
    }
  };
  frameId.contents = requestAnimationFrame(loop);
  return function () {
    var id = frameId.contents;
    if (id !== undefined) {
      cancelAnimationFrame(id);
      return cancelTransition(manager);
    }
    
  };
}

var $$Animation = {
  linear: linear,
  easeInOut: easeInOut,
  easeOut: easeOut,
  easeIn: easeIn,
  animate: animate
};

function useTransition(manager) {
  var match = React.useState(function () {
        return manager.state;
      });
  var setState = match[1];
  React.useEffect((function () {
          return subscribe(manager, (function ($$event) {
                        var exit = 0;
                        switch ($$event.TAG) {
                          case "TransitionStart" :
                          case "TransitionProgress" :
                              exit = 1;
                              break;
                          case "TransitionEnd" :
                          case "TransitionCancel" :
                              exit = 2;
                              break;
                          
                        }
                        switch (exit) {
                          case 1 :
                              return setState(function (param) {
                                          return manager.state;
                                        });
                          case 2 :
                              return setState(function (param) {
                                          return manager.state;
                                        });
                          
                        }
                      }));
        }), [manager]);
  return match[0];
}

function useProgress(manager) {
  var state = useTransition(manager);
  if (state.TAG === "Idle") {
    return 0.0;
  } else {
    return state.progress;
  }
}

function useDirection(manager) {
  var state = useTransition(manager);
  if (state.TAG === "Idle") {
    return ;
  } else {
    return state.direction;
  }
}

var React$1 = {
  useTransition: useTransition,
  useProgress: useProgress,
  useDirection: useDirection
};

function make$1(manager, parse, toString, eq, animationDurationOpt) {
  var animationDuration = animationDurationOpt !== undefined ? animationDurationOpt : 300.0;
  var push = function (route) {
    animate(manager, route, animationDuration, undefined, eq, (function () {
            $$Navigation.pushUrl(toString(route));
          }));
  };
  var replace = function (route) {
    startTransition(manager, route, "Replace", eq);
    completeTransition(manager);
    $$Navigation.replaceUrl(toString(route));
  };
  var back = function () {
    $$Navigation.back();
  };
  var forward = function () {
    $$Navigation.forward();
  };
  var subscribeToNav = function () {
    return $$Navigation.onUrlChange(function (url) {
                var route = parse(url);
                if (route !== undefined) {
                  animate(manager, Caml_option.valFromOption(route), animationDuration, undefined, eq, undefined);
                  return ;
                }
                
              });
  };
  return {
          push: push,
          replace: replace,
          back: back,
          forward: forward,
          subscribe: subscribeToNav
        };
}

var WithNavigation = {
  make: make$1
};

export {
  make ,
  notify ,
  subscribe ,
  determineDirection ,
  startTransition ,
  updateProgress ,
  completeTransition ,
  cancelTransition ,
  navigateTo ,
  isTransitioning ,
  getCurrentRoute ,
  getState ,
  $$Animation ,
  React$1 as React,
  WithNavigation ,
}
/* react Not a pure module */
