// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import m from 'mithril';

// Check if a mithril component vnode has children
export function hasChildren<T>({children}: m.Vnode<T>): boolean {
  return (
    Array.isArray(children) &&
    children.length > 0 &&
    children.some((value) => value)
  );
}

// A component which simply passes through it's children.
// Can be used for having something to attach lifecycle hooks to without having
// to add an extra HTML element to the DOM.
export const Passthrough = {
  view({children}: m.VnodeDOM) {
    return children;
  },
};

export interface GateAttrs {
  open: boolean;
}

// The gate component is a wrapper which can either be open or closed.
// - When open, children are rendered inside a div where display = contents.
// - When closed, children are rendered inside a div where display = none
// Use this component when we want to conditionally render certain children,
// but we want to maintain their state.
export const Gate = {
  view({attrs, children}: m.VnodeDOM<GateAttrs>) {
    return m(
      '',
      {
        style: {display: attrs.open ? 'contents' : 'none'},
      },
      children,
    );
  },
};

/**
 * Utility function to pre-bind some mithril attrs of a component, and leave
 * the others unbound and passed at run-time.
 * Example use case: the Page API Passes to the registered page a PageAttrs,
 * which is {subpage:string}. Imagine you write a MyPage component that takes
 * some extra input attrs (e.g. the App object) and you want to bind them
 * onActivate(). The results looks like this:
 *
 * interface MyPageAttrs extends PageAttrs { app: App; }
 *
 * class MyPage extends m.classComponent<MyPageAttrs> {... view() {...} }
 *
 * onActivate(app: App) {
 *   pages.register(... bindMithrilApps(MyPage, {app: app});
 * }
 *
 * The return value of bindMithrilApps is a mithril component that takes in
 * input only a {subpage: string} and passes down to MyPage the combination
 * of pre-bound and runtime attrs, that is {subpage, app}.
 */
export function bindMithrilAttrs<BaseAttrs, Attrs>(
  component: m.ComponentTypes<Attrs>,
  boundArgs: Omit<Attrs, keyof BaseAttrs>,
): m.Component<BaseAttrs> {
  return {
    view(vnode: m.Vnode<BaseAttrs>) {
      const attrs = {...vnode.attrs, ...boundArgs} as Attrs;
      const emptyAttrs: m.CommonAttributes<Attrs, {}> = {}; // Keep tsc happy.
      return m<Attrs, {}>(component, {...attrs, ...emptyAttrs});
    },
  };
}
