//check plan calculation option
        List <PlanCheckResult> CheckCalcOption(IonPlanSetup plan)
        {
            List <PlanCheckResult> planCheckResults = new List <PlanCheckResult>();

            Dictionary <string, string> currentCalcOptions = plan.ProtonCalculationOptions;
            Dictionary <string, string> defaultCalcOptions = new DefaultCalcOptions();

            foreach (string s in defaultCalcOptions.Keys)
            {
                if (currentCalcOptions[s].ToUpper() != defaultCalcOptions[s].ToUpper())
                {
                    var planCheckResult = new PlanCheckResult();
                    planCheckResult.Item        = s;
                    planCheckResult.Expected    = defaultCalcOptions[s];
                    planCheckResult.CurrentPlan = currentCalcOptions[s];
                    planCheckResult.Pass        = PlanCheckResult.CheckResult.Warning;
                    planCheckResult.Comments    = "Calculation Option is not same as default";
                    planCheckResults.Add(planCheckResult);
                }
            }
            if (planCheckResults.Count == 0)
            {
                var planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "Calculation Options Checked";
                planCheckResult.Expected    = "Default";
                planCheckResult.CurrentPlan = "Default";
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Pass;
                planCheckResult.Comments    = "";
                planCheckResults.Add(planCheckResult);
            }

            return(planCheckResults);
        }
        //Check Table coordinates should be 0,0,0
        PlanCheckResult CheckTableCoords(IonPlanSetup plan)
        {
            bool tableCoordsZero        = true;
            IEnumerable <IonBeam> beams = plan.IonBeams;

            foreach (IonBeam b in beams)
            {
                if (Math.Abs(b.IonControlPoints.First().TableTopLongitudinalPosition - 0.0) > Double.Epsilon * 100)
                {
                    tableCoordsZero = false;
                }
                if (Math.Abs(b.IonControlPoints.First().TableTopLateralPosition - 0.0) > Double.Epsilon * 100)
                {
                    tableCoordsZero = false;
                }
                if (Math.Abs(b.IonControlPoints.First().TableTopVerticalPosition - 0.0) > Double.Epsilon * 100)
                {
                    tableCoordsZero = false;
                }
            }
            PlanCheckResult planCheckResult = new PlanCheckResult();

            planCheckResult.Item        = "Check Table Coords";
            planCheckResult.Expected    = "All table coords are zero";
            planCheckResult.CurrentPlan = tableCoordsZero ? "All table coords are zero" : "some table coords are not zero";
            planCheckResult.Pass        = tableCoordsZero ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Warning;
            planCheckResult.Comments    = tableCoordsZero ? "" : "Please check the table coordinations";
            return(planCheckResult);
        }
        //Check CT Calibration Curve
        PlanCheckResult CheckCTCalibration(IonPlanSetup plan)
        {
            PlanCheckResult planCheckResult       = new PlanCheckResult();
            string          CTCalibrationCurrent  = plan.StructureSet.Image.Series.ImagingDeviceId;
            string          CTCalibrationExpected = "GE CT";

            planCheckResult.Item        = "CT Calibration Curve";
            planCheckResult.Expected    = CTCalibrationExpected;
            planCheckResult.CurrentPlan = CTCalibrationCurrent;
            planCheckResult.Pass        = (CTCalibrationCurrent == CTCalibrationExpected) ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Fail;
            planCheckResult.Comments    = (CTCalibrationCurrent == CTCalibrationExpected) ? "" : "Please check the CT calibration curve";
            return(planCheckResult);
        }
        //check fields
        List <PlanCheckResult> CheckLabelGantryAngle(IonPlanSetup plan)
        {
            List <PlanCheckResult> planCheckResults = new List <PlanCheckResult>();
            IEnumerable <IonBeam>  ionBeams         = plan.IonBeams;

            foreach (IonBeam b in ionBeams)
            {
                double gantry = b.IonControlPoints.First().GantryAngle;
                double couch  = b.IonControlPoints.First().PatientSupportAngle;

                FieldNameChecker fieldNameChecker = new FieldNameChecker(
                    b.Id,
                    gantry,
                    couch,
                    plan.StructureSet.Image.ImagingOrientation);

                bool pass = fieldNameChecker.IsLegitName || fieldNameChecker.IsAngleMatch;

                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "Check " + b.Id + "...";
                planCheckResult.Expected    = "";
                planCheckResult.CurrentPlan = pass ? "Pass!" : "Not Pass";
                planCheckResult.Pass        = pass ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Warning;
                planCheckResult.Comments    = pass ? "" : fieldNameChecker.Warning;
                planCheckResults.Add(planCheckResult);

                //check the gantry and couch agnle is integer
                if (Math.Abs(gantry % 1) >= (Double.Epsilon * 100))
                {
                    planCheckResult.Item        = b.Id + " Gantry";
                    planCheckResult.Expected    = "Integer";
                    planCheckResult.CurrentPlan = gantry.ToString("0.00");
                    planCheckResult.Pass        = PlanCheckResult.CheckResult.Warning;
                    planCheckResult.Comments    = "Gantry angle is not integer.";
                    planCheckResults.Add(planCheckResult);
                }
                if (Math.Abs(couch % 1) >= (Double.Epsilon * 100))
                {
                    planCheckResult.Item        = b.Id + " Couch";
                    planCheckResult.Expected    = "Integer";
                    planCheckResult.CurrentPlan = couch.ToString("0.00");
                    planCheckResult.Pass        = PlanCheckResult.CheckResult.Warning;
                    planCheckResult.Comments    = "Couch angle is not integer.";
                    planCheckResults.Add(planCheckResult);
                }
            }

            return(planCheckResults);
        }
        //check if all beams are same range shift
        PlanCheckResult CheckSameRangeShift(IonPlanSetup plan)
        {
            PlanCheckResult planCheckResult = new PlanCheckResult();

            planCheckResult.Item     = "Same Range Shift";
            planCheckResult.Expected = "All beams range shift are same";
            IEnumerable <IonBeam>      ionBeams = plan.IonBeams;
            IEnumerable <RangeShifter> firstBeamRangeShifters = ionBeams.First().RangeShifters;
            bool isSame = ionBeams.All(b => b.RangeShifters.SequenceEqual(firstBeamRangeShifters)) ? true : false;

            planCheckResult.CurrentPlan = isSame ? "All beams range shift are same" : "Some beam range shift is different";
            planCheckResult.Pass        = isSame ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Warning;
            planCheckResult.Comments    = isSame ? "" : "Please check the range shift for all beams";
            return(planCheckResult);
        }
        //check if iso center are integer
        PlanCheckResult CheckISOInteger(IonPlanSetup plan)
        {
            PlanCheckResult planCheckResult = new PlanCheckResult();

            planCheckResult.Item     = "ISO Integer";
            planCheckResult.Expected = "ISO is integer";
            IEnumerable <IonBeam> ionBeams = plan.IonBeams;
            var  firstBeamISO = ionBeams.First().IsocenterPosition;
            bool isInteger    = (
                Math.Abs(firstBeamISO.x % 1) < Double.Epsilon * 100 &&
                Math.Abs(firstBeamISO.y % 1) < Double.Epsilon * 100 &&
                Math.Abs(firstBeamISO.z % 1) < Double.Epsilon * 100);

            planCheckResult.CurrentPlan = isInteger ? "ISO is integer" : "ISO is not integer";
            planCheckResult.Pass        = isInteger ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Warning;
            planCheckResult.Comments    = isInteger ? "" : "Please check the ISO Center for all beams";
            return(planCheckResult);
        }
        //check plan name
        PlanCheckResult CheckPlanName(IonPlanSetup plan)
        {
            var planCheckResult = new PlanCheckResult
            {
                Item        = "Plan Name",
                Expected    = "",
                CurrentPlan = plan.Id,
                Pass        = PlanCheckResult.CheckResult.Pass,
                Activated   = true,
                Comments    = ""
            };

            if (!PlanNameRule(plan.Id))
            {
                planCheckResult.Pass     = PlanCheckResult.CheckResult.Warning;
                planCheckResult.Comments = "Plan Name is not right.";
            }
            return(planCheckResult);
        }
        //check if all beams are same iso center
        PlanCheckResult CheckSameISO(IonPlanSetup plan)
        {
            PlanCheckResult planCheckResult = new PlanCheckResult();

            planCheckResult.Item     = "Same ISO";
            planCheckResult.Expected = "All beams ISO are same";
            IEnumerable <IonBeam> ionBeams = plan.IonBeams;
            var  firstBeamISO = ionBeams.First().IsocenterPosition;
            bool isSame       = ionBeams.All(b => b.IsocenterPosition.Equals(firstBeamISO)) ? true : false;

            planCheckResult.CurrentPlan = isSame ? "All beams ISO are same" : "Some beam ISO is different";
            planCheckResult.Pass        = isSame ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Warning;
            planCheckResult.Comments    = isSame ? "Isocenter is:("
                                          + firstBeamISO.x.ToString("0.00") + ","
                                          + firstBeamISO.y.ToString("0.00") + ","
                                          + firstBeamISO.z.ToString("0.00") + ")."
                : "Please check the ISO Center for all beams";
            return(planCheckResult);
        }
        //check plan calculation model
        PlanCheckResult CheckPlanModel(IonPlanSetup plan)
        {
            var planCheckResult = new PlanCheckResult
            {
                Item        = "Proton Dose Calculation Model",
                Expected    = "15.5",
                CurrentPlan = plan.ProtonCalculationModel,
                Pass        = PlanCheckResult.CheckResult.Pass,
                Activated   = true,
                Comments    = ""
            };

            if (!planCheckResult.CurrentPlan.Contains(planCheckResult.Expected))
            {
                planCheckResult.Pass     = PlanCheckResult.CheckResult.Fail;
                planCheckResult.Comments = "Old model is used.";
            }
            return(planCheckResult);
        }
        //check Support Structure
        PlanCheckResult CheckSupportStructure(IonPlanSetup plan)
        {
            PlanCheckResult         planCheckResult = new PlanCheckResult();
            IEnumerable <Structure> structures      = plan.StructureSet.Structures;

            bool hasSupport = false;

            foreach (Structure s in structures)
            {
                IEnumerable <StructureCodeInfo> structureCode = s.StructureCodeInfos;
                bool isSupport = structureCode.Any(sc => sc.Code == "Support");
                if (isSupport)
                {
                    hasSupport = true;
                }
            }

            planCheckResult.Item        = "Support Structure";
            planCheckResult.Expected    = "Support Structure exists";
            planCheckResult.CurrentPlan = hasSupport ? "Support Structure exists" : "Support Structure not exists";
            planCheckResult.Pass        = hasSupport ? PlanCheckResult.CheckResult.Pass : PlanCheckResult.CheckResult.Warning;
            planCheckResult.Comments    = hasSupport ? "" : "Please check the support structure";
            return(planCheckResult);
        }
        //Check Image (Orientation, CT thickness, or missing CT, total number, etc.)
        List <PlanCheckResult> CheckImage(IonPlanSetup plan)
        {
            List <PlanCheckResult> planCheckResults = new List <PlanCheckResult>();


            Image image = plan.StructureSet.Image;

            //Orientation
            PatientOrientation patientOrientation = image.ImagingOrientation;

            if (patientOrientation == PatientOrientation.HeadFirstSupine)
            {
                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "Patient Orientation";
                planCheckResult.Expected    = PatientOrientation.HeadFirstSupine.ToString();
                planCheckResult.CurrentPlan = patientOrientation.ToString();
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Pass;
                planCheckResult.Comments    = "";
                planCheckResults.Add(planCheckResult);
            }
            else
            {
                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "Patient Orientation";
                planCheckResult.Expected    = "";
                planCheckResult.CurrentPlan = patientOrientation.ToString();
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Warning;
                planCheckResult.Comments    = "Patient is NOT HeadFirstSupine, please double check patient orientation.";
                planCheckResults.Add(planCheckResult);
            }


            //CT thickness
            double CTThickness        = image.ZRes;
            double defaultCTThickness = 2.5;

            if (CTThickness - defaultCTThickness < 1e-7)
            {
                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "CT Thickness (mm)";
                planCheckResult.Expected    = "2.5";
                planCheckResult.CurrentPlan = CTThickness.ToString();
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Pass;
                planCheckResult.Comments    = "";
                planCheckResults.Add(planCheckResult);
            }
            else
            {
                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "CT Thickness (mm)";
                planCheckResult.Expected    = "2.5";
                planCheckResult.CurrentPlan = CTThickness.ToString("0.00");
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Warning;
                planCheckResult.Comments    = "CT thickness is not same as default. Please check.";
                planCheckResults.Add(planCheckResult);
            }


            //CT total slices < 300
            double CTSlices = image.ZSize;
            double maxSlice = 300;

            if (CTSlices <= maxSlice)
            {
                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "CT Slices";
                planCheckResult.Expected    = "<300";
                planCheckResult.CurrentPlan = CTSlices.ToString();
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Pass;
                planCheckResult.Comments    = "";
                planCheckResults.Add(planCheckResult);
            }
            else
            {
                PlanCheckResult planCheckResult = new PlanCheckResult();
                planCheckResult.Item        = "CT Slices";
                planCheckResult.Expected    = "<300";
                planCheckResult.CurrentPlan = CTSlices.ToString();
                planCheckResult.Pass        = PlanCheckResult.CheckResult.Warning;
                planCheckResult.Comments    = "CT slice is more than 300";
                planCheckResults.Add(planCheckResult);
            }

            return(planCheckResults);
        }