import { Box, Button, Card, DialogActions, DialogContent, DialogTitle, FormControlLabel, Grid2, Radio, RadioGroup, Step, StepLabel, Stepper, Typography } from "@mui/material";
import { cloneDeep } from "lodash";
import { getSubtypes, Subtype } from "pbHelpers/ComponentType";
import { pond, quack } from "protobuf-ts/pond";
import React from "react";
import { useEffect, useState } from "react";
import ScannedI2C from "./ScannedI2C";
import ComponentForm from "component/ComponentForm";
import { Component, Device } from "models";
import { DeviceAvailabilityMap, FindAvailablePositions, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useComponentAPI, useDeviceAPI, useSnackbar } from "hooks";
import { ConfigurablePin } from "pbHelpers/AddressTypes";
import ScannedOneWirePort from "./OneWire/ScannedOneWirePort";

interface Props {
  scannedComponents: pond.ComponentAddressMap
  device: Device
  availablePositions: DeviceAvailabilityMap
  availableOffsets: OffsetAvailabilityMap
  refreshCallback: () => void
}
 
interface CompStep {
  label: string;
  completed?: boolean;
}

let i2cBlacklistAddresses: number[] = [0x70]

export default function DeviceScannedComponents(props: Props){
  const {scannedComponents, device, availablePositions, availableOffsets, refreshCallback} = props
  const compAPI = useComponentAPI();
  const deviceAPI = useDeviceAPI()
  const [scannedI2C, setScannedI2C] = useState<pond.DetectI2C>() //the unmodified scan of addresses
  const [scannedOneWire, setScannedOneWire] = useState<pond.DetectOneWire[]>([])
  const [validI2CCompAddresses, setValidI2CComponentAddresses] = useState<quack.AddressData[]>([]) //the filtered array of address data
  const [components, setComponents] = useState<Component[]>([])
  const [steps, setSteps] = useState<CompStep[]>([]);
  const { error, success } = useSnackbar();
  const [currentStep, setCurrentStep] = useState(0)
  const [settingsValid, setSettingsValid] = useState(false);
  const [openDialog, setOpenDialog] = useState(false);
  const [clearOpen, setClearOpen] = useState(false);

  useEffect(()=>{
    if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
    if (scannedComponents.oneWire) setScannedOneWire(scannedComponents.oneWire)
    //makes the array empty for testing
    // if (scannedComponents.i2c) {
    //   let clone = cloneDeep(scannedComponents.i2c)
    //   if (clone.settings){
    //     clone.settings.foundAddresses = []
    //   }
    //   setScannedI2C(clone)
    // }
  },[scannedComponents])

  useEffect(() => {
    let steps: CompStep[] = []
    components.forEach(comp => {
      steps.push({
        label: comp.name(),
        completed: false
      })
    })
    setSteps(steps)
  },[components])

  useEffect(()=>{
    let valid: quack.AddressData[] = []
    //filter the address data
    let addr = scannedI2C?.settings?.foundAddresses
    if(addr){
      addr.forEach(addrData => {
        if(!i2cBlacklistAddresses.includes(addrData.address)){
          if(!(addrData.address === 0x71 && addrData.expansionLine === undefined)){
            valid.push(addrData)
          }
        }
      })
    }
    setValidI2CComponentAddresses(valid)
  },[scannedI2C])

  const stepper = () => {
    return (
      <Stepper nonLinear activeStep={currentStep} style={{ width: "100%" }}>
        {steps.map(compStep => {
          const labelProps: {
            optional?: React.ReactNode;
          } = {};
          return (
            <Step key={compStep.label} completed={compStep.completed}>
              <StepLabel {...labelProps}>{compStep.label}</StepLabel>
            </Step>
          );
        })}
      </Stepper>
    );
  };

  const addComponents = () => {
    let c: pond.MultiComponentSettings = pond.MultiComponentSettings.create();
    components.forEach(component => {
      c.components.push(component.settings);
    });
    compAPI
      .addMultiComponents(device.id(), c)
      .then(resp => {
        success("Components added to Device");
      })
      .catch(err => {
        error("One or more component failed to add");
      })
      .finally(() => {
        refreshCallback();
      });
  };

  const addComponentDialog = () => {
    return (
      <ResponsiveDialog open={openDialog} onClose={()=>{setOpenDialog(false)}}>
        <DialogTitle>Adding Components</DialogTitle>
        <DialogContent>
          {stepper()}
          <ComponentForm
            component={components[currentStep]}
            canEdit
            device={device}
            componentChanged={(component, isValid) => {
              components[currentStep].settings = component.settings;
              setSettingsValid(isValid);
            }}
            />
        </DialogContent>
          <DialogActions>
            <Grid2 container direction="row" justifyContent="space-between">
              <Grid2>
                <Button
                  variant="contained"
                  color="primary"
                  onClick={() => {
                    setOpenDialog(false)
                  }}>
                  Cancel
                </Button>
              </Grid2>
              <Grid2>
                {currentStep !== 0 && (
                  <Button
                    sx={{marginX: 1}}
                    variant="contained"
                    color="primary"
                    onClick={() => {
                      setCurrentStep(currentStep - 1);
                    }}>
                    Back
                  </Button>
                )}
                {currentStep !== steps.length - 1 && (
                  <React.Fragment>
                    <Button
                    sx={{marginX: 1}}
                      variant="contained"
                      color="primary"
                      onClick={() => {
                        setCurrentStep(currentStep + 1);
                      }}>
                      Next
                    </Button>
                  </React.Fragment>
                )}
                <Button
                  sx={{marginX: 1}}
                  variant="contained"
                  color="primary"
                  onClick={() => {
                    setOpenDialog(false)
                    addComponents();
                  }}>
                  Confirm
                </Button>
              </Grid2>
            </Grid2>
          </DialogActions>
      </ResponsiveDialog>
    );
  };

  const componentSelection = (newComponents: Component[], checked: boolean) => {
    let cloneList = cloneDeep(components)
    if(checked){
      //add the component to the list
      cloneList = cloneList.concat(newComponents)
    }else{
      newComponents.forEach(newComponent => {
        //remove any components that match the component location: (type)-(addressType)-(address)
        cloneList.forEach((comp, i) => {
          //check the components location against the one in the list
          if(comp.locationString() === newComponent.locationString()){
            cloneList.splice(i, 1)
          }
        })
      })
    }
    console.log(cloneList)
    setComponents(cloneList)
  }

  const removeScan = (key: string, type: pond.ObjectType) => {
    deviceAPI.removeFoundComponents(device.settings.deviceId, key, type)
    .then(resp => {
      console.log("Cleared this scan")
    }).catch(err => {
      console.log("There was a problem clearing this scan")
    })
  }

  const removeAllScans = () => {
    deviceAPI.removeAllFoundComponents(device.settings.deviceId)
    .then(resp => {
      console.log("Cleared all scans")
      refreshCallback()
    }).catch(err => {
      console.log("There was a problem clearing scans")
    })
  }

  const clearWarning = () => {
    return (
      <ResponsiveDialog open={clearOpen} onClose={()=>{setClearOpen(false)}}>
        <DialogTitle>Clear All Scans</DialogTitle>
        <DialogContent>This action will clear all scans for this device.</DialogContent>
        <DialogActions>
          <Button variant="contained" onClick={()=>{setClearOpen(false)}}>Close</Button>
          <Button variant="contained" color="primary" onClick={()=>{
            removeAllScans()
            setClearOpen(false)
            }}>Continue</Button>
        </DialogActions>
      </ResponsiveDialog>
    )
  }



  return (
    <React.Fragment>
      {clearWarning()}
      <Card raised sx={{padding: 1}}>
        <Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
          <Typography sx={{fontWeight: 650, fontSize: 25}}>Sensor Scan</Typography>
          <Button variant="contained" color="primary" onClick={()=>{setClearOpen(true)}}>Clear</Button>
        </Box>
        {scannedI2C !== undefined && 
          <Box marginBottom={5}>
          <Box justifyContent="space-between" display="flex">
            <Typography sx={{fontWeight: 650}}>
              I2C Sensors
            </Typography>
            <Button variant="contained" color="primary" onClick={()=>{removeScan(scannedI2C.key, pond.ObjectType.OBJECT_TYPE_DETECT_I2C)}}>Clear Scan</Button>
          </Box>
          {validI2CCompAddresses.length > 0 ? validI2CCompAddresses.map((addr, index) => {
            return (
              <ScannedI2C key={index} sensorNum={index+1} addressData={addr} deviceProduct={device.settings.product} componentSelectionCallback={componentSelection} availablePositions={availablePositions.get(quack.AddressType.ADDRESS_TYPE_I2C) ?? new Map<quack.ComponentType, number[]>()}/>
            )
          }) :
            <Box sx={{
              margin: 1,
              display: "flex",
              justifyContent: "center"
            }}>
              <Typography>
                No Sensors Found  
              </Typography>
            </Box>
          }
          </Box>
        }
        {scannedOneWire.length > 0 &&
          <Box>
            <Box justifyContent="space-between" display="flex">
              <Typography sx={{fontWeight: 650}}>
                Pin Port Sensors
              </Typography>
            </Box>
            {scannedOneWire.map((scan,i) => {
              let map = FindAvailablePositions([], device.settings.product).availability.get(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY)
              let portAddress = scan.settings?.oneWireData?.port
              let portLabel = ""

              map?.forEach(entry => {
                let pin = entry as ConfigurablePin
                if (pin.address && pin.address === portAddress) {
                  portLabel = pin.label
                }
              })
              return(
                <Box key={i} paddingX={2}>
                  <Box  display="flex" justifyContent="space-between" alignItems={"center"}>
                    <Typography>Port: {portLabel}</Typography>
                    <Button variant="contained" color="primary" onClick={()=>{
                      removeScan(scan.key, pond.ObjectType.OBJECT_TYPE_DETECT_ONEWIRE)
                    }}>Clear Port Scan</Button>
                  </Box>
                    <ScannedOneWirePort scan={scan} componentSelectionCallback={componentSelection}/>
                </Box>
              )
            })}
          </Box>
        }
        <Box display="flex" flexDirection="row-reverse" marginTop={2}>
          <Button disabled={components.length === 0} variant="contained" color="primary" onClick={()=>{setOpenDialog(true)}}>Add Components</Button>
        </Box>
      </Card>
      {addComponentDialog()}
    </React.Fragment>
  )
}