// TypeScript Version: 2.4

import * as React from 'react'

/* Utils */

export type Updater<T> = (value: T | ((updater: T) => T)) => void
export type Callback<T> = (value: T) => void
export type RenderFn<T> = (value: T) => React.ReactNode

/* Active */

export type ActiveChange = (active: boolean) => void

export type ActiveRender = (
  argument: {
    active: boolean
    bind: { onMouseDown: () => void; onMouseUp: () => void }
  }
) => React.ReactNode

export const Active: React.ComponentType<
  | { onChange?: ActiveChange; render: ActiveRender }
  | { onChange?: ActiveChange; children: ActiveRender }
>

/* Compose */

export const Compose: React.ComponentType

/* Counter */

export type CounterChange = Callback<number>

export type CounterRender = RenderFn<{
  count: number
  inc: () => void
  dec: () => void
  incBy: (step: number) => void
  decBy: (step: number) => void
}>

export const Counter: React.ComponentType<
  | { initial?: number; onChange?: CounterChange; render: CounterRender }
  | { initial?: number; onChange?: CounterChange; children: CounterRender }
>

/* Focus */

export type FocusChange = Callback<boolean>

export type FocusRender = RenderFn<{
  focused: boolean
  bind: { onFocus: () => void; onBlur: () => void }
}>

export const Focus: React.ComponentType<
  | { onChange?: FocusChange; render: FocusRender }
  | { onChange?: FocusChange; children: FocusRender }
>

/* FocusManager */

export type FocusManagerChange = Callback<boolean>

export type FocusManagerRender = RenderFn<{
  focused: boolean
  blur: () => void
  bind: {
    tabIndex: number
    onFocus: () => void
    onBlur: () => void
    onMouseDown: () => void
    onMouseUp: () => void
  }
}>

export const FocusManager: React.ComponentType<
  | { onChange?: FocusManagerChange; render: FocusManagerRender }
  | { onChange?: FocusManagerChange; children: FocusManagerRender }
>

/* Form */

export type FormChange<T> = Callback<T>

export type FormRender<T, K extends keyof T> = RenderFn<{
  values: T
  input: (
    key: K
  ) => {
    value: string
    set: Updater<string>
    bind: {
      value: string
      onChange: (argument: React.ChangeEvent<any>) => void
    }
  }
}>

export type FormProps<T, K extends keyof T> =
  | { initial: T; onChange?: FormChange<T>; render: FormRender<T, K> }
  | { initial: T; onChange?: FormChange<T>; children: FormRender<T, K> }

export interface Hash {
  [key: string]: string
}

export class Form<T extends Hash, K extends keyof T> extends React.Component<
  FormProps<T, K>,
  any
> {}

/* Hover */

export type HoverChange = Callback<boolean>

export type HoverRender = RenderFn<{
  hovered: boolean
  bind: { onMouseEnter: () => void; onMouseLeave: () => void }
}>

export const Hover: React.ComponentType<
  | { onChange?: HoverChange; render: HoverRender }
  | { onChange?: HoverChange; children: HoverRender }
>

/* Input */

export type InputChange = Callback<string>

export type InputRender = RenderFn<{
  value: string
  set: Updater<string>
  bind: { value: string; onChange: (event: React.ChangeEvent<any>) => void }
}>

export const Input: React.ComponentType<
  | { initial?: string; onChange?: InputChange; render: InputRender }
  | { initial?: string; onChange?: InputChange; children: InputRender }
>

/* List */

export type ListChange<T> = Callback<ReadonlyArray<T>>

export type ListRender<T> = RenderFn<{
  list: ReadonlyArray<T>
  first: () => T | void
  last: () => T | void
  set: Updater<ReadonlyArray<T>>
  push: (value: T) => void
  pull: (predicate: (flag: T) => boolean) => void
  sort: (compare: (a: T, b: T) => -1 | 0 | 1) => void
}>

export type ListProps<T> =
  | {
      initial: ReadonlyArray<T>
      onChange?: ListChange<T>
      render: ListRender<T>
    }
  | {
      initial: ReadonlyArray<T>
      onChange?: ListChange<T>
      children: ListRender<T>
    }

export class List<T> extends React.Component<ListProps<T>, any> {}

/* Set */

export type SetChange<T> = Callback<ReadonlyArray<T>>

export type SetRender<T> = RenderFn<{
  values: ReadonlyArray<T>
  add: (key: T) => void
  clear: () => void
  remove: (key: T) => void
  has: (key: T) => boolean
}>

export type SetProps<T> =
  | {
      initial: ReadonlyArray<T>
      onChange?: SetChange<T>
      render: SetRender<T>
    }
  | {
      initial: ReadonlyArray<T>
      onChange?: SetChange<T>
      children: SetRender<T>
    }

export class Set<T> extends React.Component<SetProps<T>> {}

/* Map */

export type MapChange<T> = Callback<T>

export type MapRender<T, K extends keyof T> = RenderFn<{
  values: T
  set: (key: K, value: T[K]) => void
  over: (key: K, fn: (value: T[K]) => T[K]) => void
  get: (key: K) => T[K]
}>

export type MapProps<T, K extends keyof T> =
  | { initial: T; onChange?: MapChange<T>; render: MapRender<T, K> }
  | { initial: T; onChange?: MapChange<T>; children: MapRender<T, K> }

export class Map<T extends {}, K extends keyof T> extends React.Component<
  MapProps<T, K>,
  any
> {}

/* State */

export type StateChange<T> = Callback<T>

export type StateRender<T> = RenderFn<{
  state: T
  setState: (
    updated: Partial<T> | ((state: T) => Partial<T>),
    cb?: () => void
  ) => void
}>

export type StateProps<T> =
  | { initial: T; onChange?: StateChange<T>; render: StateRender<T> }
  | { initial: T; onChange?: StateChange<T>; children: StateRender<T> }

export class State<T extends {}> extends React.Component<StateProps<T>> {}

/* Toggle */

export type ToggleChange = Callback<boolean>

export type ToggleRender = RenderFn<{
  on: boolean
  toggle: () => void
  set: Updater<boolean>
}>

export const Toggle: React.ComponentType<
  | { initial?: boolean; onChange?: ToggleChange; render: ToggleRender }
  | { initial?: boolean; onChange?: ToggleChange; children: ToggleRender }
>

/* Touch */

export type TouchChange = Callback<boolean>

export type TouchRender = RenderFn<{
  touched: boolean
  bind: { onTouchStart: () => void; onTouchEnd: () => void }
}>

export const Touch: React.ComponentType<
  | { onChange?: TouchChange; render: TouchRender }
  | { onChange?: TouchChange; children: TouchRender }
>

/* Value */

export type ValueChange<T> = Callback<T>

export type ValueRender<T> = RenderFn<{
  value: T
  set: Updater<T>
}>

export type ValueProps<T> =
  | { initial: T; onChange?: ValueChange<T>; render: ValueRender<T> }
  | { initial: T; onChange?: ValueChange<T>; children: ValueRender<T> }

export class Value<T> extends React.Component<ValueProps<T>> {}

/* composeEvents */

export interface Events {
  [name: string]: (event: any) => void
}

export function composeEvents(...arguments: Events[]): Events
