import clsx from 'clsx';

import Grid from "@material-ui/core/Grid";
import Card from "@material-ui/core/Card";
import CardHeader from "@material-ui/core/CardHeader";
import CardContent from "@material-ui/core/CardContent";
import {createStyles, Theme, withStyles, WithStyles, WithTheme} from "@material-ui/core";
import CardActions from "@material-ui/core/CardActions";
import IconButton from "@material-ui/core/IconButton";
import Collapse from "@material-ui/core/Collapse";
import * as React from "react";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import DoneIcon from "@material-ui/icons/Done";
import EditIcon from "@material-ui/icons/Edit";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableContainer from "@material-ui/core/TableContainer";
import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
import TextField from "@material-ui/core/TextField";
import TableHead from "@material-ui/core/TableHead";
import {MonitoredArea, PvArea, PvPanel} from "../MonitoredAreaPageTypes";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import DeleteIcon from "@material-ui/icons/Delete";
import AddIcon from "@material-ui/icons/Add";
import Chip from "@material-ui/core/Chip";
import Typography from "@material-ui/core/Typography";
import CloseIcon from '@material-ui/icons/Close';
import {appRoutes, AppRoutes, SiteDescription} from "../../../../common/appRoutes";


const styles = (theme: Theme) =>
    createStyles({
        paper: {
            padding: theme.spacing(2),
            textAlign: 'center',
            color: theme.palette.text.secondary,
        },
        tableCell: {},
        expand: {
            transform: "rotate(0deg)",
            marginLeft: "auto",
            transition: theme.transitions.create("transform", {
                duration: theme.transitions.duration.shortest
            })
        },
        expandOpen: {
            transform: "rotate(180deg)"
        },
        formControl: {
            margin: theme.spacing(1),
            minWidth: 120,
        },
    });

type MonitoredAreaCardProps = {
    cardData: MonitoredArea
    availablePvPanels: PvPanel[]
    handleUpdate: (updatedMa: MonitoredArea) => void
    isExpanded: boolean
    handleExpanded: (updatedMa: MonitoredArea) => void
    onSectionChange: (route: SiteDescription, params?: number[]) => void
} & WithStyles<typeof styles> & WithTheme


type MonitoredAreaCardState = {
    isEdited: boolean,
    isAddingRow: boolean,
    cardData: MonitoredArea,
    availablePvPanels: PvPanel[],
    newPvArea: PvArea
}


class MonitoredAreaCard extends React.Component<MonitoredAreaCardProps, MonitoredAreaCardState> {
    readonly state: MonitoredAreaCardState = {
        isEdited: false,
        isAddingRow: false,
        cardData: this.props.cardData,
        availablePvPanels: this.props.availablePvPanels,
        newPvArea: {
            id: 0,
            area: 0,
            altitude: 0,
            tilt: 0,
            direction: 0,
            pvPanel: undefined

        }
    };

    private createMonitoredAreaTitle = (cardId: number) => cardId > 0 ? `Monitored Area #${cardId}` : `[Draft]#${-cardId} Monitored Area`;


    private handleEditMonitoredAreaProperties = () => {

        if (this.state.isAddingRow) {

            this.setState({
                ...this.state,
                isAddingRow: !this.state.isAddingRow,
                isEdited: !this.state.isEdited,
                cardData: {
                    ...this.state.cardData,
                    pvAreas: this.appendNewPvArea()
                }
            }, () => {
                this.props.handleUpdate(this.state.cardData);
                this.cleanDataFromNewPvArea();
            })
        } else {
            this.setState({
                ...this.state,
                isEdited: !this.state.isEdited,
                isAddingRow: false
            }, () => {
                if (!this.state.isEdited) this.props.handleUpdate(this.state.cardData)
            })
        }
    };

    private discardChanges = () => {
        this.setState({
            ...this.state,
            isEdited: false,
            isAddingRow: false,
            cardData: this.props.cardData
        })
    };

    private appendNewPvArea = () => {
        this.state.cardData.pvAreas.push(this.state.newPvArea);
        return this.state.cardData.pvAreas
    };

    private handleExpandClick = () => {
        this.props.handleExpanded(this.props.cardData);
    };

    private renderTableRows = (pvAreas: PvArea[]) => {
        return [pvAreas.map((value, index) => this.renderTableRow(value, index)), this.renderAddTableRow(pvAreas, this.state.newPvArea)]

    };

    private renderAddTableRow = (pvAreas: PvArea[], newPvArea: PvArea) => {
        if (this.state.isEdited) {
            if (this.state.isAddingRow) {
                return this.renderTableRow(this.state.newPvArea, pvAreas.length)
            } else {
                return (
                    <TableRow key={"add-table-row"}>
                        <TableCell>
                            <IconButton aria-label="add to favorites" onClick={this.handleAddPvArea}>
                                <AddIcon/>
                            </IconButton>
                        </TableCell>
                    </TableRow>
                )
            }
        } else return undefined;
    };

    private renderTableRow = (pvArea: PvArea, index: number) => {
        return (<TableRow key={`table-row-${index}`}>
            {this.renderActionCell(index)}
            {Object.entries(pvArea).map(([k, v]) => {
                if (this.isEntryOfPrimitiveType(v) && k !== "pvPanel") {
                    if (k !== "id") {
                        return this.renderPrimitiveTableCell(index, k, v)
                    }
                } else {
                    return this.renderPvPanelCell(index, v as PvPanel)
                }
            })}
        </TableRow>);
    };

    private renderActionCell = (index: number) => {
        if (this.state.isEdited) {
            return <TableCell key={`table-cell-action`} className={this.props.classes.tableCell}>
                {this.renderActionButton(index)}
            </TableCell>;
        }
    };

    private isEntryOfPrimitiveType = (value: any) => {
        return typeof value === "string" || typeof value === "number";
    };

    private renderPrimitiveTableCell = (pvAreaIndex: number, key: string, value: any) => {
        return <TableCell key={`table-cell-${key}`} className={this.props.classes.tableCell}
                          align="right">
            {this.state.isEdited ? this.renderCellTextField(pvAreaIndex, key, value) : this.renderStaticText(value)}
        </TableCell>;
    };

    private renderPvPanelCell = (pvAreaIndex: number, pvPanel: PvPanel) => {
        return <TableCell key={`table-cell-pvpanel`} className={this.props.classes.tableCell}
                          align="right">
            <FormControl className={this.props.classes.formControl}>
                <Select
                    onChange={(event) => this.handlePvPanelChange(event, pvAreaIndex)}
                    displayEmpty
                    inputProps={{'aria-label': 'Without label'}}
                    value={this.getPvPanelIndex(pvPanel)}
                    disabled={!this.state.isEdited}
                >
                    {this.state.availablePvPanels.map((v, i) =>
                        <MenuItem key={i} value={i}>{v.code}</MenuItem>
                    )}
                </Select>
            </FormControl>
        </TableCell>;
    };

    private getPvPanelIndex = (pvPanel: PvPanel) => {
        const result = this.state.availablePvPanels.find((value) => {
            if (pvPanel) {
                return value.code === pvPanel.code
            }
        });
        return result ? this.state.availablePvPanels.indexOf(result) : "";

    };

    private handlePvPanelChange = (event: React.ChangeEvent<{ name?: string; value: unknown }>, index: number) => {
        if (this.isEditingAddedRow(index)) {
            this.setState({
                ...this.state,
                newPvArea: {
                    ...this.state.newPvArea,
                    pvPanel: this.state.availablePvPanels[event.target.value as number]
                }
            })
        } else {
            this.setState({
                cardData: {
                    ...this.state.cardData,
                    pvAreas: this.state.cardData.pvAreas.map((value, i): PvArea => {
                        if (i === index) {
                            return {
                                ...this.state.cardData.pvAreas[i],
                                pvPanel: this.state.availablePvPanels[event.target.value as number],
                            }
                        }
                        return value
                    })
                }
            })
        }


    };

    private renderCellTextField = (index: number, key: string, value: any) => {
        return <TextField
            autoFocus
            required
            margin="dense"
            id={key}
            type={typeof value}
            fullWidth
            name={key}
            value={value}
            onChange={(event) => this.handleTableRowChange(event, index)}
        />;
    };

    private renderStaticText = (value: PvArea) => value ? value : "-";

    private renderTableHeader = () => {
        return <TableRow>
            {this.state.isEdited ? <TableCell>Action</TableCell> : undefined}
            <TableCell align={"right"}>Area</TableCell>
            <TableCell align={"right"}>Altitude</TableCell>
            <TableCell align={"right"}>Tilt</TableCell>
            <TableCell align={"right"}>Direction</TableCell>
            <TableCell>PV panel</TableCell>
        </TableRow>;
    };

    componentDidUpdate(prevProps: MonitoredAreaCardProps, prevState: MonitoredAreaCardState) {
        if (this.props.cardData !== prevState.cardData && this.props.cardData !== prevProps.cardData) {
            this.setState({...this.state, cardData: this.props.cardData})
        }
    }

    render() {
        return <Grid item sm={6} xs={12}>
            <Card>
                <CardHeader title={this.state.cardData.description}
                            subheader={this.createMonitoredAreaTitle(this.props.cardData.id)}/>
                <CardContent>
                    <Typography display={"inline"}>Monitored By: </Typography>
                    <Chip
                        color={'primary'}
                        onClick={() => this.props.onSectionChange(appRoutes.get(AppRoutes.Sensors)!,this.props.cardData.instrumentId? [this.props.cardData.instrumentId]: [])}
                        disabled={this.state.cardData.instrumentId ? this.state.isEdited : true}
                        icon={appRoutes.get(AppRoutes.Sensors)!.siteIcon}
                        label={this.state.cardData.instrumentId ? `Sensor #${this.state.cardData.instrumentId}` : "No Sensor"}/>
                    <Chip
                        color={'primary'}
                        onClick={() => this.props.onSectionChange(appRoutes.get(AppRoutes.InverterConnection)!, this.props.cardData.inverterId? [this.props.cardData.inverterId]: [])}
                        disabled={this.state.cardData.inverterId? this.state.isEdited: true}
                        icon={appRoutes.get(AppRoutes.InverterConnection)!.siteIcon}
                        label={this.state.cardData.inverterId ? `Inverter #${this.state.cardData.inverterId}` : "No Inverter"}/>
                </CardContent>
                <CardContent>
                    <Typography display={"inline"}>Total Area: </Typography>
                    <Chip
                        label={`${this.sumAllPvAreas()} m2`}/>
                </CardContent>
                <CardActions disableSpacing>
                    <IconButton
                        className={clsx(this.props.classes.expand, {
                            [this.props.classes.expandOpen]: this.props.isExpanded
                        })}
                        onClick={this.handleExpandClick}
                        aria-expanded={this.props.isExpanded}
                        aria-label="show more"
                    >
                        <ExpandMoreIcon/>
                    </IconButton>
                </CardActions>

                <Collapse in={this.props.isExpanded} timeout="auto" unmountOnExit>
                    <CardContent>
                        <CardHeader subheader="Photovoltaic Panel Areas"/>
                        <TableContainer>
                            <Table
                                size="small"
                                aria-label="simple table"
                            >
                                <TableHead>
                                    {this.renderTableHeader()}
                                </TableHead>
                                <TableBody>
                                    {this.renderTableRows(this.state.cardData.pvAreas)}
                                </TableBody>
                            </Table>
                        </TableContainer>
                    </CardContent>
                    <CardActions disableSpacing>
                        <IconButton aria-label="add to favorites"
                                    onClick={this.handleEditMonitoredAreaProperties}>
                            {this.state.isEdited ? <DoneIcon/> : <EditIcon/>}
                        </IconButton>
                        <IconButton aria-label="add to favorites"
                                    onClick={this.discardChanges}>
                            {this.state.isEdited ? <CloseIcon/> : undefined}
                        </IconButton>
                    </CardActions>
                </Collapse>
            </Card>
        </Grid>
    }

    private sumAllPvAreas = () => {
        return this.state.cardData.pvAreas.length > 0 ? this.state.cardData.pvAreas.reduce(((previousValue, currentValue) => ({
            ...previousValue,
            area: previousValue.area + currentValue.area
        }))).area.toString() : "0";
    };

    private handleTableRowChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>, index: number) => {
        if (this.isEditingAddedRow(index)) {
            this.setState({
                ...this.state,
                newPvArea: {
                    ...this.state.newPvArea,
                    [event.currentTarget.name]: event.target.value
                }
            })
        } else {
            this.setState({
                ...this.state,
                cardData: {
                    ...this.state.cardData,
                    pvAreas: this.state.cardData.pvAreas.map((value, i): PvArea => {
                        if (i === index) {
                            return {
                                ...this.state.cardData.pvAreas[i],
                                [event.currentTarget.name]: event.target.value,
                            }
                        }
                        return value
                    })
                }
            })
        }

    };

    private isEditingAddedRow = (index: number) => index === this.state.cardData.pvAreas.length;

    private renderActionButton = (index: number) => {
        return <IconButton onClick={() => this.handleDeletePvArea(index)} edge="end"
                           aria-label="delete">
            <DeleteIcon/>
        </IconButton>
    };

    private handleDeletePvArea = (index: number) => {
        if (this.isEditingAddedRow(index)) {
            this.setState({
                ...this.state,
                isAddingRow: false
            }, this.cleanDataFromNewPvArea);
        } else {
            this.setState({
                ...this.state,
                cardData: {
                    ...this.state.cardData,
                    pvAreas: this.deletePvArea(index)
                }
            })
        }
    };

    private cleanDataFromNewPvArea = () => {
        this.setState({
            ...this.state,
            newPvArea: {
                id: 0,
                area: 0,
                altitude: 0,
                tilt: 0,
                direction: 0,
                pvPanel: undefined
            }
        });
    };

    private deletePvArea = (index: number) => {
        this.state.cardData.pvAreas.splice(index, 1);
        return this.state.cardData.pvAreas
    };

    private handleAddPvArea = () => {
        this.setState({
            ...this.state,
            isAddingRow: true
        })
    }
}


export default withStyles(styles, {withTheme: true})(MonitoredAreaCard)