// @flow
import * as React from 'react';
import {
	Button,
	Form, FormGroup, Label, Input,
	Modal, ModalHeader, ModalBody, ModalFooter,
	FormText,
	Col,
	Badge,
	Row,
} from 'reactstrap';

import { connect } from 'react-redux';

import { generateNewKey } from '@helpers';
import { BEAT_ENTITIES, nameValidator, nameSegmentsForEntity, getTcFromExpName } from '@helpers/beat';
import type { BeatEntity, BeatObject } from '@helpers/beat';
import CacheInput from './CacheInput.jsx';

import * as Selectors from '@store/selectors.js';
import * as Actions from '@store/actions';

import { getValidDatabaseObj } from '@helpers/beat';
import { getValidDataformatObj } from '@helpers/beat';
import { getValidLibraryObj } from '@helpers/beat';
import { getValidAlgorithmObj } from '@helpers/beat';
import { getValidToolchainObj } from '@helpers/beat';
import { getValidExperimentObj } from '@helpers/beat';
import { getValidPlotterObj } from '@helpers/beat';
import { getValidPlotterparameterObj } from '@helpers/beat';

const getObjs = {
	database: getValidDatabaseObj,
	dataformat: getValidDataformatObj,
	library: getValidLibraryObj,
	algorithm: getValidAlgorithmObj,
	toolchain: getValidToolchainObj,
	experiment: getValidExperimentObj,
	plotter: getValidPlotterObj,
	plotterparameter: getValidPlotterparameterObj,
};

type Props = {
	isOpen: boolean,
	toggle: () => mixed,
	entity: BeatEntity,
	copyObj?: BeatObject,
	create: (string, BeatObject[], BeatObject[], ?BeatObject) => mixed,
	data: BeatObject[],
	toolchains: BeatObject[],
	algorithms: BeatObject[],
	// if true, the name is being changed but the version is stuck at 1 (for copying or creating new)
	// if false, the name is stuck and the version is incremented (for making a new version)
	nameOrVersion: boolean,
};

type State = {
	nameSegs: string[],
	numSegs: number,
};

const getStartNameSegs = (copyObj?: BeatObject, existingNames: string[], entity: BeatEntity, nameOrVersion: boolean) => {
	const autoVersion = entity !== 'experiment';
	if(copyObj){
		const newName = copyObj.name || '';
		const segs = newName.split('/');
		if(autoVersion){
			const version = Number.parseInt(segs[segs.length - 1]);
			segs[segs.length - 1] = `${ nameOrVersion ? 1 : version + 1 }`;
		}
		return segs;
	} else {
		const len = nameSegmentsForEntity(entity);
		return Array.apply(null, Array(len)).map((u, i) => i === len - 1 && autoVersion ? '1' : '');
	}
};

export class NewEntityModal extends React.Component<Props, State> {
	constructor(props: Props) {
		super(props);
	}

	state = {
		nameSegs: getStartNameSegs(this.props.copyObj, this.props.data.map(d => d.name), this.props.entity, this.props.nameOrVersion),
		numSegs: nameSegmentsForEntity(this.props.entity),
	}

	componentDidUpdate = (prevProps: Props) => {
		if(this.props !== prevProps){
			this.setState({
				nameSegs: getStartNameSegs(this.props.copyObj, this.props.data.map(d => d.name), this.props.entity, this.props.nameOrVersion),
				numSegs: nameSegmentsForEntity(this.props.entity),
			});
		}
	}

	titleStr = () => `New ${ this.props.entity }: "${ this.getNameString() }"`;

	submit = (name: string) => {
		if(!this.nameIsValid())
			return;

		this.props.create(name, this.props.algorithms, this.props.toolchains, this.props.copyObj);
		this.props.toggle();
	}

	getNameString = (lastSeg?: string) => {
		let segs = [...this.state.nameSegs];
		if(lastSeg)
			segs[segs.length - 1] = lastSeg;
		return segs.join('/');
	}

	validatorFunc = () => nameValidator(this.props.entity, this.props.data.map(d => d.name));

	nameIsValid = (str: string = this.getNameString()): true | React.Node => {
		const err = `Please provide a unique and valid ${ this.props.entity } name`;
		;
		const isValid = this.validatorFunc()(str);
		return isValid || err;
	}

	handleInput = (...segs: Array<?string>) => {
		const newSegs = Array.apply(null, Array(this.state.numSegs))
		.map((u, i) => segs[i] || this.state.nameSegs[i] || '');
		this.setState({
			nameSegs: newSegs,
		});
	}

	render () {
		const nameValidity = this.nameIsValid();
		const nameOrVersion = this.props.nameOrVersion;
		return (
			<Modal isOpen={this.props.isOpen} toggle={this.props.toggle} size='lg'>
				<ModalHeader toggle={this.props.toggle}>
					{ this.titleStr() }
					{' '}
					<Badge
						color={`${ nameValidity === true ? 'success' : 'danger' }`}
						title={nameValidity === true ? undefined : nameValidity}
					>
						{`${ nameValidity === true ? 'Valid Name' : 'Invalid Name' }`}
					</Badge>
				</ModalHeader>
				<ModalBody>
					<Form>
						<FormGroup row>
							{/* database */}
							{ this.props.entity === 'database' &&
									<React.Fragment>
										<Col sm={6}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New database name...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={6}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Database version...`}
												value={this.state.nameSegs[1]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
							{/* library */}
							{ this.props.entity === 'library' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Library user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New library name...`}
												value={this.state.nameSegs[1]}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												fieldTest
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Library version...`}
												value={this.state.nameSegs[2]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
							{/* dataformat */}
							{ this.props.entity === 'dataformat' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Dataformat user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New dataformat name...`}
												value={this.state.nameSegs[1]}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												fieldTest
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Dataformat version...`}
												value={this.state.nameSegs[2]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
							{/* algorithm */}
							{ this.props.entity === 'algorithm' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Algorithm user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New algorithm name...`}
												value={this.state.nameSegs[1]}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												fieldTest
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Algorithm version...`}
												value={this.state.nameSegs[2]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
							{/* toolchain */}
							{ this.props.entity === 'toolchain' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Toolchain user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New toolchain name...`}
												value={this.state.nameSegs[1]}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												fieldTest
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Toolchain version...`}
												value={this.state.nameSegs[2]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
							{/* experiment */}
							{ this.props.entity === 'experiment' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Experiment user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
											/>
										</Col>
										<Col sm={4}>
											<Label>Toolchain</Label>
											<Input
												type='select'
												name='toolchain'
												placeholder={`Experiment toolchain name...`}
												value={this.state.nameSegs.slice(1, 4).filter(s => s !== '').join('/')}
												onChange={(e) => this.handleInput(undefined, ...e.target.value.split('/'))}
											>
												<option value=''>Toolchain...</option>
												{
													this.props.toolchains.map((tc, i) =>
														<option key={i} value={tc.name}>{ tc.name}</option>
													)
												}
											</Input>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Experiment name...`}
												value={this.state.nameSegs[4]}
												onChange={(e) => this.handleInput(undefined, undefined, undefined, undefined, e.target.value)}
												fieldTest
												autoFocus
											/>
										</Col>
									</React.Fragment>
							}
							{/* plotter */}
							{ this.props.entity === 'plotter' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Plotter user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New plotter name...`}
												value={this.state.nameSegs[1]}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												fieldTest
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Plotter version...`}
												value={this.state.nameSegs[2]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
							{/* plotterparameter */}
							{ this.props.entity === 'plotterparameter' &&
									<React.Fragment>
										<Col sm={4}>
											<Label>User</Label>
											<CacheInput
												delay={0}
												type='text'
												name='user'
												placeholder={`Plotterparameter user...`}
												value={this.state.nameSegs[0]}
												onChange={(e) => this.handleInput(e.target.value)}
												fieldTest
												autoFocus
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Name</Label>
											<CacheInput
												delay={0}
												type='text'
												name='name'
												placeholder={`New plotterparameter name...`}
												value={this.state.nameSegs[1]}
												onChange={(e) => this.handleInput(undefined, e.target.value)}
												fieldTest
												disabled={!nameOrVersion}
											/>
										</Col>
										<Col sm={4}>
											<Label>Version</Label>
											<CacheInput
												delay={0}
												type='text'
												name='version'
												placeholder={`Plotterparameter version...`}
												value={this.state.nameSegs[2]}
												validateFunc={(str) => `${ Number.parseInt(str) }` === str}
												onChange={(e) => this.handleInput(undefined, undefined, e.target.value)}
												disabled
											/>
										</Col>
									</React.Fragment>
							}
						</FormGroup>
					</Form>
				</ModalBody>
				<ModalFooter>
					<Button
						color="primary"
						disabled={nameValidity !== true}
						onClick={() => this.submit(this.getNameString())}
					>Create</Button>{' '}
					<Button color="secondary" onClick={this.props.toggle}>Cancel</Button>
				</ModalFooter>
			</Modal>
		);
	}
}

const mapStateToProps = (state, ownProps) => ({
	data: Selectors[`${ ownProps.entity }Get`](state),
	toolchains: Selectors.toolchainGet(state),
	algorithms: Selectors.algorithmGet(state),
	plotters: Selectors.plotterGet(state),
});

const mapDispatchToProps = (dispatch, ownProps) => ({
	create: (name: string, algorithms: BeatObject[], toolchains: BeatObject[], copyObj?: any = {}) => {
		let newObj;
		if(ownProps.entity === 'experiment'){
			const tcName = getTcFromExpName(name);
			const tc = toolchains.find(o => o.name === tcName);
			newObj = getObjs[ownProps.entity]({ ...copyObj, name }, tc, algorithms);
		} else {
			newObj = getObjs[ownProps.entity]({ ...copyObj, name });
		}

		return dispatch(Actions.createObject(ownProps.entity, newObj, copyObj.name)());
	}
});

export default connect(mapStateToProps, mapDispatchToProps)(NewEntityModal);
