// Displays a find/replace dialog

import * as React from 'react';
import { useMemo, useState, useEffect } from 'react';

import { EditorSettings } from './types';
import { _ } from '@joplin/lib/locale';
import { TextInput, View, Text, StyleSheet, ViewStyle } from 'react-native';
import { Theme } from '@joplin/lib/themes/type';
import IconButton from '../IconButton';
import { SearchState } from '@joplin/editor/types';
import { SearchControl } from './types';
import BackButtonService from '../../services/BackButtonService';

const buttonSize = 48;

type OnChangeCallback = (text: string)=> void;
type Callback = ()=> void;

export const defaultSearchState: SearchState = {
	useRegex: false,
	caseSensitive: false,

	searchText: '',
	replaceText: '',
	dialogVisible: false,
};

export interface SearchPanelProps {
	searchControl: SearchControl;
	searchState: SearchState;
	editorSettings: EditorSettings;
}

interface ActionButtonProps {
	styles: ReturnType<typeof useStyles>;
	themeId: number;
	iconName: string;
	title: string;
	onPress: Callback;
}

const ActionButton = (props: ActionButtonProps) => {
	return (
		<IconButton
			themeId={props.themeId}
			containerStyle={props.styles.button}
			onPress={props.onPress}
			description={props.title}
			iconName={`material ${props.iconName}`}
			iconStyle={props.styles.buttonText}
		/>
	);
};

interface ToggleButtonProps {
	styles: ReturnType<typeof useStyles>;
	themeId: number;
	iconName: string;
	title: string;
	active: boolean;
	onToggle: Callback;
}

const ToggleButton = (props: ToggleButtonProps) => {
	const active = props.active;

	return (
		<IconButton
			themeId={props.themeId}
			containerStyle={{
				...props.styles.toggleButton,
				...(active ? props.styles.toggleButtonActive : {}),
			}}
			onPress={props.onToggle}

			accessibilityState={{
				checked: props.active,
			}}
			description={props.title}
			accessibilityRole='switch'

			iconName={`material ${props.iconName}`}
			iconStyle={
				active ? props.styles.activeButtonText : props.styles.buttonText
			}
		/>
	);
};


const useStyles = (theme: Theme) => {
	return useMemo(() => {
		const buttonStyle: ViewStyle = {
			width: buttonSize,
			height: buttonSize,
			backgroundColor: theme.backgroundColor4,
			alignItems: 'center',
			justifyContent: 'center',
			flexShrink: 1,
		};
		const buttonTextStyle = {
			color: theme.color4,
			fontSize: 30,
		};

		return StyleSheet.create({
			button: buttonStyle,
			toggleButton: {
				...buttonStyle,
			},
			toggleButtonActive: {
				...buttonStyle,
				backgroundColor: theme.backgroundColor3,
			},
			input: {
				flexBasis: 0,
				flexGrow: 1,
				height: buttonSize,
				backgroundColor: theme.backgroundColor4,
				color: theme.color4,
				margin: 2,
				width: 90, // Reduce the min width for mobile screens in portrait
			},
			buttonText: buttonTextStyle,
			activeButtonText: {
				...buttonTextStyle,
				color: theme.color4,
			},
			text: {
				color: theme.color,
			},
			labeledInput: {
				flexGrow: 1,
				flexDirection: 'row',
				alignItems: 'center',
				justifyContent: 'center',
				marginLeft: 10,
			},
			panelContainer: {
				// Workaround for the editor disappearing when dismissing search on Android.
				// See https://github.com/laurent22/joplin/issues/12781
				//
				// It may be possible to remove this line after upgrading to React Native's New Architecture.
				borderColor: 'transparent',
			},
		});
	}, [theme]);
};

export const SearchPanel = (props: SearchPanelProps) => {
	const theme = props.editorSettings.themeData;
	const placeholderColor = theme.color3;
	const styles = useStyles(theme);

	const [showingAdvanced, setShowAdvanced] = useState(false);

	const state = props.searchState;
	const control = props.searchControl;

	const updateSearchState = (changedData: Partial<SearchState>) => {
		const newState = { ...state, ...changedData };
		control.setSearchState(newState);
	};

	// Creates a TextInput with the given parameters
	const createInput = (
		placeholder: string, value: string, onChange: OnChangeCallback, autoFocus: boolean,
	) => {
		return (
			<TextInput
				style={styles.input}
				autoFocus={autoFocus}
				onChangeText={onChange}
				value={value}
				placeholder={placeholder}
				placeholderTextColor={placeholderColor}
				returnKeyType='search'
				blurOnSubmit={false}
				onSubmitEditing={control.findNext}
				selectTextOnFocus={true}
			/>
		);
	};

	// Close the search dialog on back button press
	useEffect(() => {
		// Only register the listener if the dialog is visible
		if (!state.dialogVisible) {
			return () => {};
		}

		const handler = () => {
			control.hideSearch();
			return true;
		};

		BackButtonService.addHandler(handler);
		return () => BackButtonService.removeHandler(handler);
	}, [state.dialogVisible, control]);


	const themeId = props.editorSettings.themeData.themeId;
	const closeButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="close"
			onPress={control.hideSearch}
			title={_('Close')}
		/>
	);

	const showDetailsButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="menu-down"
			onPress={() => setShowAdvanced(true)}
			title={_('Show advanced')}
		/>
	);

	const hideDetailsButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="menu-up"
			onPress={() => setShowAdvanced(false)}
			title={_('Hide advanced')}
		/>
	);

	const searchTextInput = createInput(
		_('Search for...'),
		state.searchText,
		(newText: string) => {
			updateSearchState({
				searchText: newText,
			});
		},

		// Autofocus
		true,
	);

	const replaceTextInput = createInput(
		_('Replace with...'),
		state.replaceText,
		(newText: string) => {
			updateSearchState({
				replaceText: newText,
			});
		},

		// Don't autofocus
		false,
	);

	const labeledSearchInput = (
		<View style={styles.labeledInput} accessible>
			<Text style={styles.text}>{_('Find: ')}</Text>
			{ searchTextInput }
		</View>
	);

	const labeledReplaceInput = (
		<View style={styles.labeledInput} accessible>
			<Text style={styles.text}>{_('Replace: ')}</Text>
			{ replaceTextInput }
		</View>
	);

	const toNextButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="menu-right"
			onPress={control.findNext}
			title={_('Next match')}
		/>
	);

	const toPrevButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="menu-left"
			onPress={control.findPrevious}
			title={_('Previous match')}
		/>
	);

	const replaceButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="swap-horizontal"
			onPress={control.replaceNext}
			title={_('Replace')}
		/>
	);

	const replaceAllButton = (
		<ActionButton
			themeId={themeId}
			styles={styles}
			iconName="reply-all"
			onPress={control.replaceAll}
			title={_('Replace all')}
		/>
	);

	const regexpButton = (
		<ToggleButton
			themeId={themeId}
			styles={styles}
			iconName="regex"
			onToggle={() => {
				updateSearchState({
					useRegex: !state.useRegex,
				});
			}}
			active={state.useRegex}
			title={_('Regular expression')}
		/>
	);

	const caseSensitiveButton = (
		<ToggleButton
			themeId={themeId}
			styles={styles}
			iconName="format-letter-case"
			onToggle={() => {
				updateSearchState({
					caseSensitive: !state.caseSensitive,
				});
			}}
			active={state.caseSensitive}
			title={_('Case sensitive')}
		/>
	);

	const simpleLayout = (
		<View style={{ flexDirection: 'row', flexShrink: 1 }}>
			{ closeButton }
			{ searchTextInput }
			{ showDetailsButton }
			{ toPrevButton }
			{ toNextButton }
		</View>
	);

	const advancedLayout = (
		<View style={{ flexDirection: 'column', flexShrink: 1 }}>
			<View style={{ flexDirection: 'row' }}>
				{ closeButton }
				{ labeledSearchInput }
				{ hideDetailsButton }
				{ toPrevButton }
				{ toNextButton }
			</View>
			<View style={{ flexDirection: 'row' }}>
				{ regexpButton }
				{ caseSensitiveButton }
				{ labeledReplaceInput }
				{ replaceButton }
				{ replaceAllButton }
			</View>
		</View>
	);

	if (!state.dialogVisible) {
		return null;
	}

	return <View style={styles.panelContainer}>
		{showingAdvanced ? advancedLayout : simpleLayout}
	</View>;
};

export default SearchPanel;
