import { 
  Box, 
  Button, 
  Card, 
  List, 
  ListItem, 
  ListSubheader, 
  Typography 
} from "@mui/material";
import { cloneDeep } from "lodash";
import { DevicePreset } from "models/DevicePreset";
import ObjectDescriber from "objects/ObjectDescriber";
import { pond } from "protobuf-ts/pond";
import React, { useEffect, useState } from "react";
import DevicePresetCard from "./devicePresetCard";

interface Props {
  objectKey: string;
  objectType: pond.ObjectType;
  presets: DevicePreset[];
  refreshCallback: (newPresets: DevicePreset[]) => void;
}

export default function DevicePresetController(props: Props) {
  const { objectKey, objectType, presets, refreshCallback } = props;
  const [newPresets, setNewPresets] = useState<Map<string, DevicePreset>>(new Map());
  const [savedPresets, setSavedPresets] = useState<Map<pond.PresetType, DevicePreset[]>>(new Map());

  useEffect(() => {
    let presetTypeMap: Map<pond.PresetType, DevicePreset[]> = new Map();
    presets.forEach(preset => {
      let typePresets = presetTypeMap.get(preset.settings.type);
      if (typePresets) {
        typePresets.push(preset);
      } else {
        presetTypeMap.set(preset.settings.type, [preset]);
      }
    });
    setSavedPresets(presetTypeMap);
  }, [presets]);

  const createNewPreset = () => {
    //add to the list of new presets using the default preset settings
    let newPreset = DevicePreset.create();
    newPreset.name = "New Preset";
    let p = new Map(newPresets);
    let tempKey = p.size.toString(); //generate a unique string for a tempKey
    newPreset.key = tempKey;
    p.set(tempKey, newPreset);
    setNewPresets(p);
  };

  const addCallback = (preset: DevicePreset, tempKey: string) => {
    //add the preset to the list of saved presets
    let presetMap = cloneDeep(savedPresets);
    if (presetMap.get(preset.settings.type)) {
      presetMap.get(preset.settings.type)?.push(preset);
    } else {
      presetMap.set(preset.settings.type, [preset]);
    }
    //send the parent component the list of presets after the new one has been saved
    let presets: DevicePreset[] = [];
    presetMap.forEach(presetList => {
      presets = presets.concat(presetList);
    });
    refreshCallback(presets);
    setSavedPresets(presetMap);
    //remove the preset from the newpreset map using the temp key
    let pMap = cloneDeep(newPresets);
    pMap.delete(tempKey);
    setNewPresets(pMap);
  };

  const removeCallback = (preset: DevicePreset) => {
    //get the type list this preset is a part of
    let map = cloneDeep(savedPresets);
    let list = map.get(preset.settings.type);
    //remove it from the list
    if (list) {
      let foundIndex;
      list.forEach((p, i) => {
        if (p.key === preset.key) {
          foundIndex = i;
        }
      });
      if (foundIndex !== undefined) {
        list.splice(foundIndex, 1);
      }
      map.set(preset.settings.type, list);
    }
    //send the parent the new list of presets after it has been removed
    let presets: DevicePreset[] = [];
    map.forEach(presetList => {
      presets = presets.concat(presetList);
    });
    refreshCallback(presets);
    setSavedPresets(map);
  };

  const updateCallback = (oldPreset: DevicePreset, newPreset: DevicePreset) => {
    //only need to update the lists if the presets if the type changed
    if (oldPreset.settings.type !== newPreset.settings.type) {
      let map = cloneDeep(savedPresets);
      //use the new preset to tget the list to add to
      let newPresetList = map.get(newPreset.settings.type);
      if (newPresetList) {
        newPresetList.push(newPreset);
        map.set(newPreset.settings.type, newPresetList);
      } else {
        map.set(newPreset.settings.type, [newPreset]);
      }

      //use the type from the old preset to get the list to remove from
      let oldPresetList = map.get(oldPreset.settings.type);
      if (oldPresetList) {
        let foundIndex;
        oldPresetList.forEach((preset, i) => {
          if (oldPreset.key === preset.key) {
            foundIndex = i;
          }
        });
        if (foundIndex !== undefined) {
          oldPresetList.splice(foundIndex, 1);
          if (oldPresetList.length === 0) {
          }
        }
        map.set(oldPreset.settings.type, oldPresetList);
      }
      let presets: DevicePreset[] = [];
      map.forEach(presetList => {
        presets = presets.concat(presetList);
      });
      refreshCallback(presets);
      setSavedPresets(map);
    }
  };

  const savedPresetDisplay = () => {
    let display: JSX.Element[] = [];
    savedPresets.forEach((presetList, type) => {
      let list: JSX.Element = (
        <React.Fragment key={type}>
          <List>
            {/* <ListSubheader>Saved {ObjectDescriber(objectType).name} Presets</ListSubheader> */}
            <ListSubheader>{DevicePreset.presetTypeString(type)} Presets</ListSubheader>
            {presetList.map((preset, i) => (
              <ListItem key={i}>
                <DevicePresetCard
                  preset={preset}
                  existing
                  objectType={objectType}
                  objectKey={objectKey}
                  removeCallback={removeCallback}
                  updateCallback={updateCallback}
                />
              </ListItem>
            ))}
          </List>
        </React.Fragment>
      );
      display.push(list);
    });

    return display;
  };

  return (
    <Card raised>
      <Box padding={1} display="flex" justifyContent="space-between">
        <Typography style={{ fontWeight: 650, fontSize: 25 }}>Custom Bin Presets</Typography>
        <Button onClick={createNewPreset} variant="contained" color="primary">
          Add New Preset
        </Button>
      </Box>

      {newPresets.size > 0 && (
        <React.Fragment>
          <Typography style={{ paddingLeft: 10, fontWeight: 650 }}>New Presets</Typography>
          <List>
            {Array.from(newPresets.values()).map((preset, i) => (
              <ListItem key={i}>
                <DevicePresetCard
                  preset={preset}
                  addCallback={addCallback}
                  objectType={objectType}
                  objectKey={objectKey}
                  removeCallback={removeCallback}
                />
              </ListItem>
            ))}
          </List>
        </React.Fragment>
      )}
      {savedPresets.size > 0 ? (
        <React.Fragment>
          <Typography style={{ paddingLeft: 10, fontWeight: 650 }}>
            Saved {ObjectDescriber(objectType).name} Presets
          </Typography>
          {savedPresetDisplay()}
        </React.Fragment>
      ) : (
        <Box padding={2}>
          <Typography>Add custom presets to use when changing modes</Typography>
        </Box>
      )}
    </Card>
  );
}
